WP7 Phone Capabilities

Note to self.  When adding a Web Browser to a Windows Phone 7 page like this:

<phone:WebBrowser x:Name="HelpBrowser"
                    Grid.Row="1"
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    />

and code up a navigation like this:

public GameInformation()
        {
            InitializeComponent();
            this.HelpBrowser.Loaded += new RoutedEventHandler(HelpBrowser_Loaded);
        }

        void HelpBrowser_Loaded(object sender, RoutedEventArgs e)
        {
            this.HelpBrowser.Navigate(new Uri(Constants.HelpDocumentsLocation, UriKind.Absolute));
        }

and you get an exception like this:

image

You are forgetting the capabilities section of the phone app for web browsers.  The inner exception about privileges gives it away (short of actually telling you what happened).  Just add this to the WMAPPManifest.xml file:

<Capabilities>
      <Capability Name="ID_CAP_NETWORKING" />
      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />

 

 

 

 

 

Faking Synch WCF calls in WP7

So you can’t do synchronous WCF calls in Windows Phone 7. I have a use case where I do a WCF call to get all of the data to set up a game board of the phone game I am writing. I don’t want the application to progress until the board is set up, so I need it as a blocking call. In addition, the WCF call is 5-6 layers deep in my class hierarchy.  My first idea was just to use a non-thread-safe variable from the Main UI thread and set the flag when the asynch call/thread returned:

        private List<ScenarioTile> GetScenarioTilesFromWebService(int scenarioId)
        {
            CallbackComplete = false;
            PanzerProxy.PanzerClient panzerClient = new PanzerProxy.PanzerClient();
            panzerClient.GetScenarioTilesCompleted += new EventHandler<PanzerProxy.GetScenarioTilesCompletedEventArgs>(panzerClient_GetScenarioTilesCompleted);
            panzerClient.GetScenarioTilesAsync(scenarioId);
            while (CallbackComplete == false)
            {
                Thread.Sleep(1000);
            }
            return ScenarioTiles;
        }

        void panzerClient_GetScenarioTilesCompleted(object sender, PanzerProxy.GetScenarioTilesCompletedEventArgs e)
        {
            ScenarioTiles = new List<ScenarioTile>();
            List<PanzerProxy.ScenarioTile> proxyTiles = e.Result as List<PanzerProxy.ScenarioTile>;
            foreach (PanzerProxy.ScenarioTile proxyScenarioTile in proxyTiles)
            {
                ScenarioTiles.Add(ConvertProxyScenarioTileToScenarioTile(proxyScenarioTile));
            }
            CallbackComplete = true;
        }

The problem is that, well, it doesn’t work. The app just hangs. I then thought of using a ManualResetEvent or AutoResetEvent class to trigger like this:

        ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        private List<ScenarioTile> GetScenarioTilesFromWebService(int scenarioId)
        {
            PanzerProxy.PanzerClient panzerClient = new PanzerProxy.PanzerClient();
            panzerClient.GetScenarioTilesCompleted += new EventHandler<PanzerProxy.GetScenarioTilesCompletedEventArgs>(panzerClient_GetScenarioTilesCompleted);
            panzerClient.GetScenarioTilesAsync(scenarioId);
            manualResetEvent.WaitOne();
            return ScenarioTiles;
        }

        void panzerClient_GetScenarioTilesCompleted(object sender, PanzerProxy.GetScenarioTilesCompletedEventArgs e)
        {
            ScenarioTiles = new List<ScenarioTile>();
            List<PanzerProxy.ScenarioTile> proxyTiles = e.Result as List<PanzerProxy.ScenarioTile>;
            foreach (PanzerProxy.ScenarioTile proxyScenarioTile in proxyTiles)
            {
                ScenarioTiles.Add(ConvertProxyScenarioTileToScenarioTile(proxyScenarioTile));
            }
            manualResetEvent.Set();
        }

Still no luck.  I then ran across this thread – basically you can’t do anything to block the Main UI thread.  Chong’s solution was to launch a 3rd thread from the secondary thread.  That seemed to me as overly complex and smelled of brittle code.  I then took a step back and realized I just had to architect my solution to get the same effect.  Basically, once the user hits the LOAD button, the MAINUI thread is done.  However, the page also subscribes to an event that the business logic raises.  Once the event fires, then the app progresses to the next page.

So instead of this:

private void SetupScenario(int scenarioId)
{
    Game.CurrentScenarioId = scenarioId;
    Game.BoardFactory.PopulateBoard(scenarioId);
    Game.CurrentBoard = Game.BoardFactory.Board;
    Game.Turns = Game.TurnFactory.GetTurnsForAScenario(scenarioId);
    this.NavigationService.Navigate(new Uri(@"/Briefing.xaml", UriKind.Relative));
}

I have this:

private void SetupScenario(int scenarioId)
{
    Game.CurrentScenarioId = scenarioId;
    Game.BoardFactory.BoardLoaded += new EventHandler<EventArgs>(BoardFactory_BoardLoaded);
    Game.BoardFactory.PopulateBoard(scenarioId);
    Game.CurrentBoard = Game.BoardFactory.Board;
    Game.Turns = Game.TurnFactory.GetTurnsForAScenario(scenarioId);

}

void BoardFactory_BoardLoaded(object sender, EventArgs e)
{
    this.NavigationService.Navigate(new Uri(@"/Briefing.xaml", UriKind.Relative));
}

The event is defined as simple as it comes:

public event EventHandler<EventArgs> BoardLoaded;

and

void TileFactory_TilesLoaded(object sender, EventArgs e)
{
    Tiles = Game.TileFactory.Tiles;
    Hexes = Game.HexFactory.GetHexes(Tiles);
    foreach (Hex hex in Hexes)
    {
        Board.MainCanvas.Children.Add(hex);
    }
    SetBoardDimensions(Board, Tiles);
    BoardLoaded(null, null);
}

Basically I just bubbled up the WCF asych event up from the lowest level to the UI page.

Using Lambdas To Increase Code Readability

I was having a spirited debate with Rob Seder yesterday about the use of lambdas – the question was weather using them increases or decreases the codes readability.  For example, here is a basic for…each:

1 foreach (Employee employee in employeeFactory.GetAllEmployees()) 2 { 3 if (employee.Name == "Moe") 4 { 5 Console.WriteLine(employee.ToString()); 6 } 7 }

Replacing with a lambda:

1 var moe = employeeFactory.GetAllEmployees().Where(e => e.Name == "Moe").FirstOrDefault(); 2 Console.WriteLine(moe.ToString());

Readability suffers for three reasons: the function chaining. the use of single letters for the variable, and the use of the var keyword.  A more readable use of lambda is like so:

1 List<Employee> employeeList = employeeFactory.GetAllEmployees().ToList(); 2 Employee selectedEmployee = employeeList.Where(employee => employee.Name == "Moe").FirstOrDefault(); 3 Console.WriteLine(selectedEmployee.ToString());

Taking the problems in reverse, Rob mentioned that he uses the var keyword when he is knocking something out, then he goes back and refactors the var keyword to the correct type.  I would agree with this.  As for the single varaible, I am less convinced that readability suffers because

  • Everyone does it – making this convention readability by default
  • You actually don’t use the variable – it is not returned or acted on beyond the line of code.

As for function chaining, I can see why readability suffers.  Breaking out the functions into logic units of work with each getting their own line seems reasonable.

Windows Phone 7 Game: Getting Closer

I have spent the last month or so writing a port of Panzer General to a Windows Phone 7 application. 

Here is a screen shot of the original game:

image

Here is a screen shot from my phone app:

image

And here it is after pinching:

image

There is 1 thing I don’t like about the graphics – each hex has a copy of the underlying image.  I really only need 1 in memory so I will make that change – which should improve the game load time.

C++ to C# Port

As part of my project to put Panzer General on the Windows Phone, I need a way to parse the map files from the original game.  Unlike the scenario files or the unit data file, the files are not in ASCII:

image

I ran across a map editor by Frederick Chlanda  that included source code – the only hitch is that it was written in C++.  Dusting off those brain cells, I spent some time isolating the area where the map file is parsed.  Here is what the code looks like:

void __fastcall TForm4::GetTrialBtnClick(TObject *Sender) { FILE *inf; //we get the set file //read in the Mapxx.set OpenDialog1->Title="Open .set"; OpenDialog1->Filter="*.set (*.set)|*.set|All files (*.*)|*.*"; if (!OpenDialog1->Execute()) return; inf=fopen(OpenDialog1->FileName.c_str(),"rb"); //0x65 is width and 0x67 is the height fseek(inf,0x65,SEEK_SET); fread(&SmMapX,2,1,inf); fread(&SmMapY,2,1,inf); ++SmMapX; ++SmMapY; SmMapT=SmMapY*SmMapX; //load entire 123 btyes of file start fseek(inf,0,SEEK_SET); for (int x=0; x<123; ++x) fread(&(SmStartup[x]),1,1,inf); //set the pointer to start of field fseek(inf,123+5*SmMapT,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmTil[x][y]),2,1,inf); //get the country info fseek(inf,123+3*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmOwn[x][y]),1,1,inf); //get the gln numbers (kind or name) fseek(inf,123+0*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmGln[x][y]),2,1,inf); //get the side info fseek(inf,123+4*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmSide[x][y]),1,1,inf); //get the road connectivity fseek(inf,123+2*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmRdc[x][y]),1,1,inf); fclose(inf); OpenDialog1->Title="Open .stm"; OpenDialog1->Filter="Mxxxx.stm (*.stm)|*.stm|All files (*.*)|*.*"; if (!OpenDialog1->Execute()) return; inf=fopen(OpenDialog1->FileName.c_str(),"rb"); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) { fread(&(SmUtr[x][y]),2,1,inf); } fclose(inf); SmShowMap(); }

I spent some digging up the conversion – the hex values sprinkled in as magic numbers, the cryptic naming conventions, the unsigned 2-byte integers that needed to be converted to C# , and my own clumbsiness with pointers and references all posed a challenge.  However, in about 2 days of semi-intermittent work on this, I got myself an adequate port:

private static void ReadMapFile() { String filePath = @"C:\Users\Jamie\Documents\Visual Studio 2010\Projects\Tff.Panzer.ParseEquipmentFile\Tff.Panzer.ParseEquipmentFile\MAP01.SET"; FileStream fileStream = File.OpenRead(filePath); BinaryReader binaryReader = new BinaryReader(fileStream); Byte[] inputData = binaryReader.ReadBytes(10000); int sizeOfMapX = Convert.ToInt32(inputData[101]); int sizeOfMapY = Convert.ToInt32(inputData[103]); int totalNumberOfHexes = (sizeOfMapX + 1) * (sizeOfMapY + 1); string rowOutput = String.Empty; string rowSql = String.Empty; short[,] TileNumbers = new short[sizeOfMapX+1, sizeOfMapY+1]; short currentTileValue = 0; int currentPosition = 123 + (5*totalNumberOfHexes); for (int y = 0; y <= sizeOfMapY; ++y) { for (int x = 0; x <= sizeOfMapX; ++x) { currentTileValue = (short)inputData[currentPosition]; currentTileValue++; TileNumbers[x, y] = currentTileValue; rowOutput += currentTileValue + "\t"; currentPosition += 2; } Console.WriteLine(rowOutput); Debug.WriteLine(rowOutput); rowOutput = String.Empty; } }

This exercise makes me appreciate C# even more.  In any event, here is the parsed output:

image

I then threw in some Linq to Entities statement and persisted all of the unstructured data into a local instance of SQL Server.  I am up in the air about if I want to keep all of the game data in SQL Server on the phone or push the data to XML files.  Turfing that decision, I put the 1st scenario on the phone and this is what I got:

image

 

Which is pretty close to the original game:

image

Phidget Force Sensor Programming

For the next TriNUG main meeting, I thought of have a Family Feud type game. To that end, I wanted to simulate the buzzer/button that the contestants use:

image

To that end, I purchased 2 force sensors from Phidgets and hooked them up to my 8/8/8 Interface kit. I guess the contestants will use a finger versus their entire hand:

image

The sensor is very straight forward – you press the button and the Interface kit reads how much pressure is applied:

image

Thinking on how it would work as FamilyFeud buzzer system, I just need to keep track of who applied force 1st – the amount of pressure is irrelevant. Also, I need a trigger mechanism that starts listening at certain points – so that contestants don’t jump the gun.

I first started with a WPF project to see if I could hook into the Phidget InterfaceKit. I created a basic WPF application and wrote the following code (pretty much straight from the programmers guide):

public partial class MainWindow : Window { InterfaceKit _interfaceKit; public MainWindow() { InitializeComponent(); _interfaceKit = new InterfaceKit(); _interfaceKit.open(); _interfaceKit.waitForAttachment(3000); _interfaceKit.SensorChange += new SensorChangeEventHandler(_interfaceKit_SensorChange); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(String.Format("Name={0}",_interfaceKit.Name)); stringBuilder.Append(Environment.NewLine); stringBuilder.Append(String.Format("Label={0}", _interfaceKit.Label)); this.InterfaceKitInfoLabel.Content = stringBuilder.ToString(); } void _interfaceKit_SensorChange(object sender, SensorChangeEventArgs e) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(String.Format("Index={0}", e.Index)); stringBuilder.Append(Environment.NewLine); stringBuilder.Append(String.Format("Value={0}", e.Value)); this.PressureSensor01Label.Content = stringBuilder.ToString(); } }

When I hit F5 and pressed a sensor, I got this error:

 

image

Ugh! I looks like Phidgets programming need to support thread affinity?! To confirm, I wrote a Console application that does pretty much the exact same thing:

static void Main(string[] args) { Console.WriteLine("Start"); InterfaceKit interfaceKit = new InterfaceKit(); interfaceKit.Attach += new Phidgets.Events.AttachEventHandler(interfaceKit_Attach); interfaceKit.SensorChange += new Phidgets.Events.SensorChangeEventHandler(interfaceKit_SensorChange); interfaceKit.open(); interfaceKit.waitForAttachment(); Console.WriteLine("-End-"); Console.ReadLine(); } static void interfaceKit_Attach(object sender, Phidgets.Events.AttachEventArgs e) { Console.Write(String.Format("Interface Kit {0} attached",e.Device.Name)); } static void interfaceKit_SensorChange(object sender, Phidgets.Events.SensorChangeEventArgs e) { Console.WriteLine(String.Format("Sensor {0} changed with the value {1}.",e.Index, e.Value)); } }

When I F5 and press the sensor, I get the following:

image

So it looks like I need to be thread-aware on WPF applications. After some digging into the code samples from the most recent documentation, I found this nugget of a project:

image

Sure enough, the code has thread-affinity:

void ifk_Attach(object sender, AttachEventArgs e) { this.Dispatcher.Invoke(new Action(delegate() { makeDigiInArrays(); makeDigiOutArrays(); makeSensorInArrays(); InterfaceKit attached = (InterfaceKit)sender; attachedLbl.Content = attached.Attached.ToString(); nameLbl.Content = attached.Name; serialNoLbl.Content = attached.SerialNumber.ToString(); versionLbl.Content = attached.Version.ToString(); digitalInputCountLbl.Content = attached.inputs.Count.ToString(); digitalOutputCountLbl.Content = attached.outputs.Count.ToString(); sensorCountLbl.Content = attached.sensors.Count.ToString(); for (int i = 0; i < attached.inputs.Count; i++) { digiInChkArray[i].Visibility = System.Windows.Visibility.Visible; digiInLblArray[i].Visibility = System.Windows.Visibility.Visible;

So this simple project just jumped up a notch if I want to use WPF.

MVVM && Game Design: Part 2

If you read this post that I wrote last week about building a game on Windows Phone 7 using MVVM, you know that I am trying to make a port of Panzer General to the Windows Phone.  Where I left it, I had made a repeating sets of hexagons to be the game board and I dynamically filled them based on the user’s touch.  My next step was to make the hexagons contain images of the terrain.  To that end, I snipped out a single hex of terrain and placed it in the images folder.  I then coded up this in the view hex:

Added this: <Image Canvas.Left="0" Canvas.Top="0" Height="100" x:Name="image1" Width="100" Stretch="Fill" Source="/Tff.Panzer;component/Images/Coast01.png" />

And I got this:

image

So then I realized that I need to add the image to the path, not layer it on top of the canvas:

<Path x:Name="PathRoot" IsHitTestVisible="True" Width="100" Height="100" d:LayoutOverrides="None" d:LastTangent="0,0" Stroke="Black" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" Stretch="Fill" Data="M0,8.660254 L5,17.320508 15,17.320508 20,8.660254 15,0 5,0 0,8.660254 z" Canvas.Left="{Binding Path=CanvasLeft}" Canvas.Top="{Binding Path=CanvasTop}"> <Path.Fill > <ImageBrush ImageSource="/Tff.Panzer;component/Images/Coast01.png" /> </Path.Fill > </Path>

Doing that, I got this:

image

Well How about that!

There are 3 different ways to get this in markup:

Fill="{Binding Path=TerraineImage}"> <!--Fill="{Binding Path=Fill}"--> <!--<Path.Fill > <ImageBrush ImageSource="{Binding Path=TerraineImageLocation}" /> </Path.Fill >-->

I prefer the 1st option from a readability and brevity point of view.

In my explorations, I found that a guy in Russia named Rudankort created a faithful PG port called PG Forever. Looking at his directory structure, he has all of the background hexes in a single image like this:

image

There are approximately 500+ images that I would need to clip and save to the file system if I wanted to separate them all out. I thought of another way of loading the entire image and then pulling pieces out of the image at runtime. Another challenge is to get the icons – removing the back fill.  I’ll work on that coming up…

Overloading Controller Methods in a MVC Project

I cranked up a new MVC3 project (Razor and Unit Testing). I went to the Home controller and added an overloaded method to the Index:

public ActionResult Index(string userName) { ViewBag.Message = String.Format("Welcome to ASP.NET MVC, {0}!", userName); return View(); }

I then hit F5 and got the following error:

image

 

I tried adding a route:

routes.MapRoute( "HomeIndexWithString", "Home/Index/{userName}", new { controller = "Home", action = "Index", userName = "" } );

and being explicit with the URL but I got the same error:

image

I then looked on Stack Overflow and added the ActionName attribute to my overloaded method:

[ActionName("HomeIndexWithString")] public ActionResult Index(string userName) { ViewBag.Message = String.Format("Welcome to ASP.NET MVC, {0}!", userName); return View(); }

The Index page returned, but the problem is that even when I passed in the explicit url, it still returned the default page. I then went back to the overloaded method and deleted it. I then added an optional parameter to the original index method and handled it if the value was populated:

public ActionResult Index(string? userName) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Welcome to ASP.NET MVC"); if (userName != null) { stringBuilder.Append(String.Format(", {0}!", userName)); } ViewBag.Message = stringBuilder.ToString(); return View(); }

The problem is that the compiler complains:

Error 1 The type ‘string’ must be a non-nullable value type in order to use it as parameter ‘T’ in the generic type or method ‘System.Nullable<T>’

So I am down to creating a custom ActionMethodSelectAttribute that can handle the requests and parse appropriately. Since this smells like a kludge, I went back to the way MSFT wants me to do it – create a different method for each action and return to appropriate view. Naming confusion notwithstanding, I guess that is what I will do…