Whoop! My Windows Phone App Store Check Came Today!

WP_000462

 

I know it is hard to read  – I just wanted to show the full effect of $3.46…

Advertisements

Serialization in an WP7 project

So if you want to do some basic serialization in your Windows Phone 7 app, (cerealization to my kids) image 

you most likely will start here.  However, if you copy/paste the code an out of the box project, you get the dreaded wiggly red line:

image

What is going on?

image

Oh crud.  The default for MSDN is .NET Framework 4.  When you switch it to Silverlight, you see that you need a reference to System.Xml.Serialization.

image

Out of the box, WP7 projects don’t have a reference to System.Xml.Serialization – only System.Xml.  Once I add that assembly to the project, we get the happy blue:

image

This is a bit confusing, to say the least.

Optimize Searching Using Value Types

As mentioned in my blog post here, I need to write a searching algorithm of hexes adjacent to the active unit to display movable/visible/attackable hexes.  The original code was a bit of a beast across 3 functions:

 

1 public List<Hex> GetTotalAdjacentHexes(Hex currentHex, Int32 maxDistance) 2 { 3 List<Hex> hexes = new List<Hex>(); 4 List<Hex> tempList = GetAdjacentHexes(currentHex); 5 foreach (Hex hex in tempList) 6 { 7 if (!InHexList(hexes,hex)) 8 GetAdjacentHexes(currentHex, hexes, hex, tempList, 0, maxDistance); 9 } 10 return hexes; 11 } 12 13 public void GetAdjacentHexes(Hex currentHex, List<Hex> hexes, Hex hex, List<Hex> currentList, int distanceCounter, int maxDistance) 14 { 15 distanceCounter++; 16 { 17 foreach (Hex tempHex in currentList) 18 { 19 hexes.Add(tempHex); 20 if (distanceCounter < maxDistance) 21 { 22 List<Hex> tempList = GetAdjacentHexes(tempHex); 23 GetAdjacentHexes(currentHex, hexes, hex, tempList, distanceCounter, maxDistance); 24 } 25 } 26 } 27 } 28 29 public List<Hex> GetAdjacentHexes(Hex currentHex) 30 { 31 32 List<Hex> adjacentHexes = new List<Hex>(); 33 Hex targetHex = null; 34 //Above 35 int targetColumnNumber = currentHex.ColumnNumber; 36 int targetRowNumber = currentHex.RowNumber - 1; 37 targetHex = GetHex(targetColumnNumber, targetRowNumber); 38 if (targetHex != null) 39 adjacentHexes.Add(targetHex); 40 //Top Left 41 targetColumnNumber = currentHex.ColumnNumber - 1; 42 if (currentHex.ColumnNumber % 2 == 0) 43 targetRowNumber = currentHex.RowNumber - 1; 44 else 45 targetRowNumber = currentHex.RowNumber; 46 targetHex = GetHex(targetColumnNumber, targetRowNumber); 47 if (targetHex != null) 48 adjacentHexes.Add(targetHex); 49 //Bottom Left 50 targetColumnNumber = currentHex.ColumnNumber - 1; 51 if (currentHex.ColumnNumber % 2 == 0) 52 targetRowNumber = currentHex.RowNumber; 53 else 54 targetRowNumber = currentHex.RowNumber + 1; 55 targetHex = GetHex(targetColumnNumber, targetRowNumber); 56 if (targetHex != null) 57 adjacentHexes.Add(targetHex); 58 //Below 59 targetColumnNumber = currentHex.ColumnNumber; 60 targetRowNumber = currentHex.RowNumber + 1; 61 targetHex = GetHex(targetColumnNumber, targetRowNumber); 62 if (targetHex != null) 63 adjacentHexes.Add(targetHex); 64 //Bottom Right 65 targetColumnNumber = currentHex.ColumnNumber + 1; 66 if (currentHex.ColumnNumber % 2 == 0) 67 targetRowNumber = currentHex.RowNumber; 68 else 69 targetRowNumber = currentHex.RowNumber + 1; 70 targetHex = GetHex(targetColumnNumber, targetRowNumber); 71 if (targetHex != null) 72 adjacentHexes.Add(targetHex); 73 //Top Right 74 targetColumnNumber = currentHex.ColumnNumber + 1; 75 if (currentHex.ColumnNumber % 2 == 0) 76 targetRowNumber = currentHex.RowNumber - 1; 77 else 78 targetRowNumber = currentHex.RowNumber; 79 targetHex = GetHex(targetColumnNumber, targetRowNumber); 80 if (targetHex != null) 81 adjacentHexes.Add(targetHex); 82 83 return adjacentHexes; 84 }

I recently had a duh moment when I tried to optimize the code for speed (replacing hexes with tiles) – each hex has an unique row/column value that I could use to calculate distance.  In addition, instead of looping and collecting heavy reference types, I am using 2 integers in 1 loop, so the performance is dramatically improved.  Finally, since I drop from 84 lines of code to 54, I am making my code more maintainable.

 

1 public List<Tile> GetTotalAdjacentTiles(Tile startTile, Int32 maxDistance) 2 { 3 List<Tile> tiles = new List<Tile>(); 4 int startingColumn = startTile.ColumnNumber - maxDistance; 5 if(startingColumn < 0) 6 { 7 startingColumn = 0; 8 } 9 int endingColumn = startTile.ColumnNumber + maxDistance; 10 if (endingColumn > Game.CurrentBoard.NumberOfColumns) 11 { 12 endingColumn = Game.CurrentBoard.NumberOfColumns; 13 } 14 15 int startingRow = startTile.RowNumber - maxDistance; 16 if (startingRow < 0) 17 { 18 startingRow = 0; 19 } 20 int endingRow = startTile.RowNumber + maxDistance; 21 if (endingRow > Game.CurrentBoard.NumberOfRows) 22 { 23 endingRow = Game.CurrentBoard.NumberOfRows; 24 } 25 26 Tile tile = null; 27 int combinedDistance = 0; 28 for (int i = startingColumn; i <= endingColumn; i++) 29 { 30 for (int j = startingRow; j <= endingRow; j++) 31 { 32 tile = GetTile(i, j); 33 if (!tiles.Contains(tile)) 34 { 35 combinedDistance = GetDistanceBetweenTwoTiles(startTile,tile); 36 if (combinedDistance <= maxDistance) 37 { 38 tiles.Add(tile); 39 } 40 } 41 } 42 } 43 44 //Remove Starting Tile 45 tiles.Remove(startTile); 46 47 return tiles; 48 } 49 50 public List<Tile> GetAdjacentTiles(Tile currentTile) 51 { 52 return GetTotalAdjacentTiles(currentTile, 1); 53 }

Windows Phone 7– Duh Moment Of The Day

So I wrote this code block in a Windows Phone 7 application:

1 private void CreateNewLogFile() 2 { 3 string logFileLocation = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); 4 if (File.Exists(logFileLocation)) 5 File.Delete(logFileLocation); 6 7 } 8

And I got this exception:

image

And it hit me – can’t write to the local file system of the emulator using Windows Desktop/Server constructs. To write in a Windows Phone 7 application, I need to write to the local storage.  However, since I can’t read from local storage easily (no WindowsExplorer access) – I would need to write a program to get the data out.  This seems more trouble than it is worth.  I’ll just write to the Debug window….

Windows Phone 7–Threading Issues With the Stopwatch Class

I am writing a profiling application to help me with some performance problems that I am having with the Windows Phone 7 game I am writing.  Here is a snippet where I track the time it takes to load in the game data for a given scenario:

1 private void LoadTile() 2 { 3 Stopwatch stopWatch = new Stopwatch(); 4 ScenarioTileFactory scenarioTileFactory = new ScenarioTileFactory(); 5 List<ScenarioTile> scenarioTiles = null; 6 stopWatch.Start(); 7 scenarioTiles = scenarioTileFactory.GetScenarioTiles(20); 8 stopWatch.Stop(); 9 this.TilesListBox.Items.Add(String.Format("Scenario {0} has {1} tiles and it took {2}.", 10 "20", scenarioTiles.Count.ToString(), stopWatch.ElapsedMilliseconds.ToString())); 11 12 } 13

When I run it, I get this (which is correct):

image

I then wanted time how long each scenario takes to load and update the UI after each load.  I immediately thought of using a second thread – 1 to load the data and the main UI thread to update the screen.  I update my load function for multi-threading like so:

1 Stopwatch stopWatch = new Stopwatch(); 2 ScenarioTileFactory scenarioTileFactory = new ScenarioTileFactory(); 3 List<ScenarioTile> scenarioTiles = null; 4 stopWatch.Start(); 5 scenarioTiles = scenarioTileFactory.GetScenarioTiles(20); 6 stopWatch.Stop(); 7 this.TilesListBox.Dispatcher.BeginInvoke(() => 8 { 9 this.TilesListBox.Items.Add(String.Format("Scenario {0} has {1} tiles and it took {2}.", 10 "20", scenarioTiles.Count.ToString(), stopWatch.ElapsedMilliseconds.ToString())); 11 }); 12 stopWatch.Reset(); 13 stopWatch.Start(); 14 scenarioTiles = scenarioTileFactory.GetScenarioTiles(21); 15 stopWatch.Stop(); 16 this.TilesListBox.Dispatcher.BeginInvoke(() => 17 { 18 this.TilesListBox.Items.Add(String.Format("Scenario {0} has {1} tiles and it took {2}.", 19 "21", scenarioTiles.Count.ToString(), stopWatch.ElapsedMilliseconds.ToString())); 20 }); 21

The results started getting screwy:

image

 

It sure looks like the Stopwatch.Reset is not thread-aware? Nope, hoping the thread back calls an auto refresh. For example, I put a break:

image

 

But when I hop the thread and go back to the UI thread:

image

So the stopwatch class is not thread-safe. Good to know! To get around this problem, I created a local holding variable and passed that value over to the UI thread:

1 private void LoadTiles() 2 { 3 Stopwatch stopWatch = new Stopwatch(); 4 ScenarioTileFactory scenarioTileFactory = new ScenarioTileFactory(); 5 List<ScenarioTile> scenarioTiles = null; 6 long elapsedTime = 0; 7 for (int i = 0; i < 30; i++) 8 { 9 stopWatch.Reset(); 10 stopWatch.Start(); 11 scenarioTiles = scenarioTileFactory.GetScenarioTiles(i); 12 stopWatch.Stop(); 13 elapsedTime = stopWatch.ElapsedMilliseconds; 14 15 this.TilesListBox.Dispatcher.BeginInvoke(() => 16 { 17 this.TilesListBox.Items.Add(String.Format("Scenario {0} has {1} tiles and it took {2}.", 18 i.ToString(), scenarioTiles.Count.ToString(), elapsedTime.ToString())); 19 }); 20 } 21 } 22

 

Now I have accurate diagnostic information for my analysis:

 

image

Windows Phone Application Bar Icons: Gotcha

I wanted to toggle my Panzer General game between air and ground mode. To do so, I created an Icon Button like so:

1 <shell:ApplicationBarIconButton x:Name="airSurface" Text="air/surface" IconUri="Images/appbar.upload.rest.png" Click="airSurface_Click" />

I then wired up the code-behind like so:

1 if (Game.InGroundMode) 2 { 3 this.airSurface.IconUri = new Uri(@"/Images/appbar.upload.rest.png", UriKind.Relative); 4 Game.InGroundMode = false; 5 } 6 else 7 { 8 this.airSurface.IconUri = new Uri(@"/Images/appbar.download.rest.png", UriKind.Relative); 9 Game.InGroundMode = true; 10 } 11

When I ran it, I got this:

image

I first thought – I must have the url wrong. I double checked the properties of the icon images – they are content. After screwing around with url and paths for a bit, it dawned on me that perhaps the uri was fine. Sure enough, when I separated the uri creation into its own line, the error was on the IconUri assignment – the button is null! For someone used to WinForms/WebForm programming, this was unexpected.

image

Jumping back to Nathan’s excellent book, I see that he has a different way of referring to IconButtons in code. I updated my code to this:

1 IApplicationBarIconButton button = sender as IApplicationBarIconButton; 2 3 if (Game.InGroundMode) 4 { 5 Uri uri = new Uri(@"/Images/appbar.upload.rest.png", UriKind.Relative); 6 button.IconUri = uri; 7 Game.InGroundMode = false; 8 } 9 else 10 { 11 button.IconUri = new Uri(@"/Images/appbar.download.rest.png", UriKind.Relative); 12 Game.InGroundMode = true; 13 } 14

And it now toggles as expected

So the two lessons are:

1) When 1 line of code does the work on two, you are setting yourself up for debugging headaches. Separate 1st, then consolidate

2) Just b/c it looks like a duck, walks like a duck, and quacks like a duck; it doesn’t mean that it follows the other APIs that you are used to….

A curious property about dependency properties

For my Panzer General game, I have a collection of hexes that make up the main board. Before every turn, I update the main canvas with this collection. I wrote some code like this:

this.MainCanvas.Children.Clear(); foreach (Hex hex in Game.CurrentBoard) { hex.HexInfoTextBlock.Text = string.Empty; this.MainCanvas.Children.Add(hex); }

However, when I run it, I get the following exception:

 

System.InvalidOperationException was unhandled

Message=Element is already the child of another element.

 

My first thought was to create a Clone for each Hex – remove the old Hex and then add the new one to the collection. However, I still got the same error.

After mucking around Bing for a bit (and not getting any closer to the answer), I decided to back into the parent and try and clear the collection that way:

Canvas currentCanvas = Game.CurrentBoard[0].Parent as Canvas; if (currentCanvas != null) { currentCanvas.Children.Clear(); }

Surprise! Surprise! It worked. I am guessing that the dependency property of the children still hold onto their parent even after the clear until the page goes out of scope.