WPF oddity of the day: attached/dependency properties and breakpoints

Silverlight doesn’t have any real support for ICommand, so I wrote an attached CommandBinder.Command property. Then it wasn’t working (at least when I compiled for WPF, which is a lot easier to debug), so I tried putting breakpoints in my SetCommand and CommandChanged methods. Neither ever got hit.

It turns out that WPF’s attached-property pattern is weird: you have to declare a getter and setter method, but they don’t get called.

Here’s some sample code for an attached property, just for reference:

public class CommandBinder
{
    public static DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached(
            "Command", typeof(ICommand), typeof(CommandBinder),
            new PropertyMetadata(CommandChanged));
 
    private static void CommandChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        // do something
    }
    public static ICommand GetCommand(DependencyObject d)
    {
        return (ICommand) d.GetValue(CommandProperty);
    }
    public static void SetCommand(DependencyObject d, ICommand value)
    {
        d.SetValue(CommandProperty, value);
    }
}

You have to have a setter method; otherwise you get a compiler error. (Apparently the compiler has some logic to enforce the design pattern.) But as far as the XAML parser is concerned, the setter method is more of an attribute — it says “Yeah, I’m settable.” The XAML loader doesn’t call SetCommand; it calls SetValue directly on the target object.

I did some experimenting, and it looks like this applies to dependency properties as well. You have to declare a corresponding CLR property, or you’ll get a compiler error when you try to set the property in XAML; but the XAML loader never actually uses the property.

In both cases, your setter could actually do nothing at all, and the XAML would still set the property’s value correctly. (But then anyone who tried to set the property programmatically would be in for some head-scratching. So don’t do that.)

I had wondered, in the past, why you would pass a PropertyMetadata with a change-handler delegate, rather than just putting the on-change logic inside the SetXXX or property setter. Now I know.

(Actually, in my experimenting, I was able to get it to call the property setter. All I had to do was misspell the property name that I passed to DependencyProperty.Register. So that’s why I have to tell it the name (something else I had always wondered) — so it can do a dictionary lookup. If the dictionary lookup fails, it falls back on trusty-but-slow Reflection.)

So that explains why my SetCommand wasn’t being called. Why wasn’t CommandChanged firing either? Stupid mistake on my part — I hadn’t set the DataContext, so the binding expression failed.

Databinding the videogame, part 6: logical coordinates

Last time I made my map 2-D. However, the coordinates left something to be desired.

The way I left the code last time, if I wanted to put two SteelPlates next to one another, I would need code like this:

var steelPlate = new SteelPlate();
m_map.Add(new Square(steelPlate, 0, 0));
m_map.Add(new Square(steelPlate, 40, 0));

That’s a problem. In the GUI, each square happens to be 40 pixels by 40 pixels. But that 40-pixel thing is strictly a GUI matter. The map, and the Square class, are DataModel objects that should have no GUI awareness, and that includes GUI constants.

Instead, I want to be able to write this:

var steelPlate = new SteelPlate();
m_map.Add(new Square(steelPlate, 0, 0));
m_map.Add(new Square(steelPlate, 1, 0));

That is, I want Square.X and Square.Y to be in logical coordinates, not in pixel coordinates. Then onscreen, the 1 should magically become 40 so that everything looks right.

This is certainly doable — in fact, there are several possible ways of dealing with it. Here are the most plausible ones I can think of:

  1. Scale transforms. This is a possibility, but not an easy one. I’d love to design all my terrain views to be 1×1 pixels, and then scale them up for display, but Visual Studio’s WPF designer maxes out at 20x zoom. Designing my tiles at no better than 20×20 pixels in the designer, when they’ll be at least 40×40 at runtime, feels way too cramped. I could always design them at 40×40, scale them down to 1×1 inside the MapView, and then scale the MapView back up, but that feels way too silly.
  2. ViewModel collection. I thought about making a ViewModel, and explicitly binding the GUI to the ViewModel instead of directly to the DataModel. That seems like a great idea — except that I’m dealing with a collection, which makes it more complicated. The back-end logic will need a collection of simple DataModel objects, so it can do things like collision detection; then the GUI would need to bind to a parallel collection of ViewModels; and both collections need to be kept in sync in the face of changes. It’s possible to make an observable collection that wraps and adapts another observable collection, but that’s an awful lot of work.
  3. On-demand ViewModels. I would love to be able to use the same ViewModel as in #2, but manufacture the ViewModels on the fly as needed. This would be great if it worked, but I haven’t been able to figure out how to do it. I can easily add another DataTemplate to map a Square to a SquareViewModel, but that puts the SquareViewModel nested inside another child control — i.e., it moves down the visual tree. It doesn’t do anything to make the ViewModel’s X and Y available at the top level, which is where I actually have to bind to Canvas.Left and Canvas.Top using ItemContainerStyle. Much as I would love to do this one, it looks like a non-starter based on what I know about WPF.
  4. ValueConverters. I could continue to bind the view directly to the DataModel, and use a custom ValueConverter to scale the X and Y values that get databound.

Option #4 is the simplest to implement. #2 would have the benefit of giving me a ViewModel class that I can unit-test, but first I would have to build a dynamic collection decorator and unit-test that thoroughly to make sure I’m actually getting the right ViewModel at the right time.

I’m going to go with simplicity for now, and do #4. If it later turns out not to be good enough, I’ll probably switch to #2.

CoordinateConverter

So I need a CoordinateConverter class:

public class CoordinateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        return ((int)value) * 40;
    }
    public object ConvertBack(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

I only need a one-way binding: I’ll only ever propagate the DataModel value to the GUI; I’ll never change the GUI’s Canvas.Left and expect that to propagate back down to the DataModel. So I don’t need ConvertBack.

Then I put a CoordinateConverter instance into MapView‘s Resources, and reference it from the X and Y Bindings:

@@ -3,8 +3,10 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:TerrainDataModels="clr-namespace:Game.Core.TerrainDataModels"
     xmlns:TerrainViews="clr-namespace:Game.Core.TerrainViews"
+    xmlns:GameViews="clr-namespace:Game.Core.GameViews"
     MinWidth="40" MinHeight="40" Background="Gray">
     <UserControl.Resources>
+        <GameViews:CoordinateConverter x:Key="CoordinateConverter"/>
         <DataTemplate DataType="{x:Type TerrainDataModels:Square}">
             <ContentPresenter Content="{Binding Path=Terrain}"/>
         </DataTemplate>
@@ -23,8 +25,8 @@
         </ItemsControl.ItemsPanel>
         <ItemsControl.ItemContainerStyle>
             <Style>
-                <Setter Property="Canvas.Left" Value="{Binding Path=X}"/>
-                <Setter Property="Canvas.Top" Value="{Binding Path=Y}"/>
+                <Setter Property="Canvas.Left" Value="{Binding Path=X,
+                    Converter={StaticResource CoordinateConverter}}"/>
+                <Setter Property="Canvas.Top" Value="{Binding Path=Y,
+                    Converter={StaticResource CoordinateConverter}}"/>
             </Style>
         </ItemsControl.ItemContainerStyle>
     </ItemsControl>

The only other detail is tweaking the random-number code in Window1 so that the now-logical coordinates will actually fit into the window:

@@ -21,8 +21,8 @@
         private void AddTerrain(Terrain terrain)
         {
             var random = new Random();
-            var x = random.Next(200);
-            var y = random.Next(200);
+            var x = random.Next(5);
+            var y = random.Next(5);
             m_map.Add(new Square(terrain, x, y));
         }
         private void Dirt_Click(object sender, RoutedEventArgs e)

databindingthevideogamepart6

Today’s test project

You can browse today’s code online, or check it out from my Subversion repository:

svn co http://svn.excastle.com/databinding_the_videogame/tags/part6

What’s next?

There’s obviously some work that needs to be done around encapsulation and map generation, but I think I want to get more of the GUI working before I delve into logic too deeply.

As far as GUI work, map rendering is pretty much done, but I haven’t touched scrolling yet. I already said I won’t have scrollbars, but I’ll still have scrolling; it’ll just be automatic instead of scrollbar-controlled. My map will be bigger than the screen, and the player will move through it. But before I add scrolling, I need a reason for it to scroll — namely, because the player is moving around in it.

So I think it’s about time I make a little guy who can walk around the screen. Stay tuned.

Databinding the videogame, part 5: making the map 2-D

Last time, I added the ability to have multiple Terrain objects in my map. Today I’ll take this into 2-D, so that the map is divided into logical squares, each with its own Terrain.

Enter the Square

I thought about adding X and Y properties to Terrain, but that felt wrong for a lot of reasons. For one thing, it’s awkward to talk about having a Dirt terrain here and another Dirt terrain right next to it. It just doesn’t sound right: the English word “terrain” refers to a general set of characteristics, not a specific location. It also introduced some duplication into the code that I didn’t like, both in the Terrain classes and in the construction logic. And the “Terrain has X and Y” pattern would have made it hard for a spot on the map to change its terrain type at runtime, which is a feature I want to add later.

So instead, I came up with a scheme that feels “right”: the map is a collection of Squares. Each Square has a location and a Terrain.

public class Square
{
    public Square(Terrain terrain, int x, int y)
    {
        Terrain = terrain;
        X = x;
        Y = y;
    }
 
    public Terrain Terrain { get; private set; }
    public int X { get; private set; }
    public int Y { get; private set; }
}

I also considered using a Dictionary<Point, Terrain>, rather than an ObservableCollection<Square>. I went with the flat list of Squares for pragmatic reasons: it’s a lot easier to set up dynamic databinding to a flat ObservableCollection<T> than to a Dictionary.

It was pretty straightforward to change Window1‘s test buttons to add Squares to the map, instead of Terrains. Since this is still just a test project, I made it assign a random X and Y to each square it adds, each in the range 0..199.

+using System;
 using System.Collections.ObjectModel;
 using System.Windows;
 using Game.Core.TerrainDataModels;
 
 namespace DatabindingTheVideogame
 {
     /// <summary>
     /// Interaction logic for Window1.xaml
     /// </summary>
     public partial class Window1
     {
-        private readonly ObservableCollection<Terrain> m_map =
-            new ObservableCollection<Terrain>();
+        private readonly ObservableCollection<Square> m_map =
+            new ObservableCollection<Square>();
 
         public Window1()
         {
             InitializeComponent();
             mapView.DataContext = m_map;
         }
 
+        private void AddTerrain(Terrain terrain)
+        {
+            var random = new Random();
+            var x = random.Next(200);
+            var y = random.Next(200);
+            m_map.Add(new Square(terrain, x, y));
+        }
         private void Dirt_Click(object sender, RoutedEventArgs e)
         {
-            m_map.Add(new Dirt());
+            AddTerrain(new Dirt());
         }
         private void SteelPlate_Click(object sender, RoutedEventArgs e)
         {
-            m_map.Add(new SteelPlate());
+            AddTerrain(new SteelPlate());
         }
     }
 }

With these changes, the program is broken. The MapView doesn’t yet know how to bind to Squares, so if you run it now and click the buttons a few times, all you get are lines of text saying “Game.Core.TerrainModels.Square”. Let’s tackle that first, and get back to working code before we go for the new feature of 2-D.

Picking a DataTemplate based on a property

Square really has nothing to say about which GUI control to instantiate. That’s all up to the Terrain. But the GUI is bound to a collection of Squares, so it’s going to be asking the Square what it wants to put on the screen. The Square’s answer is really just going to be, “Geez, man, don’t ask me. Talk to the Terrain.”

Having a collection of X, but wanting to render X.Y, seems like it should be a common enough thing that WPF would have a built-in way to do it. And indeed it does, but only sort of. ItemsControl has a DisplayMemberPath property that sounds like just the ticket. Unfortunately it’s both poorly named and poorly documented. It seems that it’s really more of a DisplayStringMemberPath, because it renders a TextBlock with the member’s value converted to a string (a fact not clear from the docs). It ignores DataTemplates and always renders text.

So if I want MapView to render a sub-property, while still actually using DataTemplates, I have to do it myself. It’s not hard, though; I just add a DataTemplate for Square, give it a ContentPresenter, and bind the Content property:

@@ -5,6 +5,9 @@
     xmlns:TerrainViews="clr-namespace:Game.Core.TerrainViews"
     MinWidth="40" MinHeight="40" Background="Gray">
     <UserControl.Resources>
+        <DataTemplate DataType="{x:Type TerrainDataModels:Square}">
+            <ContentPresenter Content="{Binding Path=Terrain}"/>
+        </DataTemplate>
         <DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
             <TerrainViews:DirtView/>
         </DataTemplate>

This brings things back to where they were last time: clicking one of the buttons adds a new Terrain to the bottom of the column.

Now to go 2-D.

Taking it to the second dimension

There are two parts to showing the terrains in two dimensions: specifying which Panel type to use, and binding the attached properties to set the position.

By default, ItemsControl renders its children using a StackPanel, which gives us the top-to-bottom column we’ve seen so far. To go two-dimensional, we need a different type of panel. Grid would be a possibility, but to make that work you really need to predefine all your rows and columns, which sounds like a pain. So I went with Canvas: the ultimate in free-form.

To tell the ItemsControl to use a different Panel type, you set its ItemsPanelTemplate:

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <Canvas/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

Then you need to bind each Square’s X and Y to something. You can’t do this in the Square’s DataTemplate, because, as far as I can tell, the Left and Top properties can only be set on immediate children of the Canvas. DataTemplate is not added as an immediate child of the Canvas; instead each item gets an ItemContainer (which is added as the immediate child of the Canvas), and then the DataTemplate goes inside the ItemContainer. So the Left and Top need to be set on the ItemContainer, which is done using an ItemContainerStyle:

<ItemsControl.ItemContainerStyle>
    <Style>
        <Setter Property="Canvas.Left" Value="{Binding Path=X}"/>
        <Setter Property="Canvas.Top" Value="{Binding Path=Y}"/>
    </Style>
</ItemsControl.ItemContainerStyle>

Honestly, I don’t fully understand why this needs a Style with Setters, rather than the same straightforward DataTemplate that’s so useful everywhere else. This is just one of those dark corners of WPF that I figure I’ll learn more about eventually, so I won’t bust my brain figuring it out now. It works, and that’s the important thing.

databindingthevideogamepart5

And there you go: a two-dimensional map. If you make these changes and run, then every time you click “Dirt” or “Steel Plate”, a new square gets added at a random position within the MapView. All it takes to make a real map is to use something more interesting than Random.Next() for the X and Y.

Today’s test project

You can browse today’s code online, or check it out from my Subversion repository:

svn co http://svn.excastle.com/databinding_the_videogame/tags/part5

What’s next?

Right now, squares’ X and Y properties are in pixel coordinates, not logical coordinates. Among other things, that means the squares aren’t square with each other. I could just hack the random number generation, but I’ll do better: next time I’ll introduce coordinate systems.

Databinding the videogame, part 4: terrain collection

Last time, I implemented real databinding, but it was only to one Terrain object at a time. Today I’ll extend it so you can add multiple terrain objects.

Before, I was binding to my UserControl’s Content property. UserControl descends from ContentControl, and ContentControls know how to do rendering magic if you put a single object in their Content. So far so good.

I had to teach WPF how to render my (non-GUI) terrain objects, and I could probably teach it how to render a collection too. Then I could either databind to a single terrain object, or to a collection of them, and either way, it would just work. But that feels like unnecessary complexity (especially since WPF syntax doesn’t lend itself to specifying a generic type, like ObservableCollection<Terrain>, for a DataTemplate’s DataType — it’s doable but not easy). And after all, I’m trying to make a map viewer, and a map will always be a collection; so why should I bother to make it do single objects too?

So I cut over to always binding a collection. This meant adding an ItemsControl to my MapView, and databinding ItemsControl.ItemsSource instead of MapView.Content.

Then I just had to change the GUI to set DataContext to a collection, and make the buttons add to the collection, and I was done.

I made sure the collection was an ObservableCollection<T>, which automatically sends change notifications that WPF databinding knows how to listen for — so all my buttons need to do is add non-GUI objects to my non-GUI-aware collection, and the GUI magically shows more GUI.

I also finally got around to making a common base class, Terrain, and making both Dirt and SteelPlate descend from it. That way I could have an ObservableCollection<Terrain>.

The diffs

I won’t bother showing the diffs for adding the Terrain base class, but here are the interesting changes for databinding a collection.

MapView.xaml:

@@ -3,8 +3,7 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:TerrainDataModels="clr-namespace:Game.Core.TerrainDataModels"
     xmlns:TerrainViews="clr-namespace:Game.Core.TerrainViews"
-    MinWidth="40" MinHeight="40" Background="Gray"
-    Content="{Binding}">
+    MinWidth="40" MinHeight="40" Background="Gray">
     <UserControl.Resources>
         <DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
             <TerrainViews:DirtView/>
@@ -13,4 +12,5 @@
             <TerrainViews:SteelPlateView/>
         </DataTemplate>
     </UserControl.Resources>
+    <ItemsControl ItemsSource="{Binding}"/>
 </UserControl>

Window1.xaml.cs:

@@ -1,4 +1,5 @@
+using System.Collections.ObjectModel;
 using System.Windows;
 using Game.Core.TerrainDataModels;
 
 namespace DatabindingTheVideogame
@@ -8,18 +9,21 @@
     /// </summary>
     public partial class Window1
     {
+        private readonly ObservableCollection<Terrain> m_map =
+            new ObservableCollection<Terrain>();
+
         public Window1()
         {
             InitializeComponent();
+            mapView.DataContext = m_map;
         }
 
         private void Dirt_Click(object sender, RoutedEventArgs e)
         {
-            mapView.DataContext = new Dirt();
+            m_map.Add(new Dirt());
         }
         private void SteelPlate_Click(object sender, RoutedEventArgs e)
         {
-            mapView.DataContext = new SteelPlate();
+            m_map.Add(new SteelPlate());
         }
     }
 }

Yes, the collection should really live on a model object instead of in the GUI. I’ll clean this up later, but I wanted today’s diffs to be focused on just databinding a collection.

databindingthevideogamepart4

Today’s test project

Now, instead of the “Dirt” and “Steel Plate” buttons changing the one existing terrain, they add a new terrain to the collection.

As you can see from the screenshot, this isn’t a proper map yet; the terrains aren’t in a grid, they’re in a vertical column — adding a terrain puts it at the bottom of the column. And if you get to the bottom, too bad; there’s no scrollbar. (I won’t bother adding a scrollbar, either. Once I get a proper 2D map going, my game won’t need or want a scrollbar.)

You can browse today’s code online, or check it out from my Subversion repository:

svn co http://svn.excastle.com/databinding_the_videogame/tags/part4

What’s next?

So far, my “map” is one-dimensional — just a vertical column. I think the next step will be to go 2D. Stay tuned.

Databinding the videogame, part 3: actual databinding

Last time, I mapped terrain objects to pretty pictures — but only one terrain square at a time. And despite the title, it wasn’t really databinding as such.

This time, I fix one of those two problems… the databinding one. ’Cause it turns out it’s not hard — I just needed to figure out the right way to do it. And of course, I figured it out after I had already posted part 2.

Databinding happens through the magic of the {Binding ...} extension syntax in XAML. (Yeah, you can also do it in code, but I don’t need that today.) I already knew about some of the settings you could put between the braces; for example, {Binding Path=PropertyName} will databind to the property named PropertyName on whatever object is in the DataContext. But I couldn’t figure out how to bind my control’s Content to the DataContext itself.

Well, somehow it hadn’t occurred to me that I could just use {Binding} by itself, without any parameters inside the curly braces. But of course you can, and poof — whatever property you used {Binding} on is thenceforth bound to the DataContext, and changes whenever the DataContext changes. Once I finally figured that out, the switch to databinding was just a three-line diff (well, okay, four if you’re being picky — there was a line break):

MapView.xaml:

@@ -3,7 +3,8 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:TerrainDataModels="clr-namespace:Game.Core.TerrainDataModels"
     xmlns:TerrainViews="clr-namespace:Game.Core.TerrainViews"
-    MinWidth="40" MinHeight="40" Background="Gray">
+    MinWidth="40" MinHeight="40" Background="Gray"
+    Content="{Binding}">
     <UserControl.Resources>
         <DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
             <TerrainViews:DirtView/>

Window1.xaml.cs:

@@ -15,11 +15,11 @@
 
         private void Dirt_Click(object sender, RoutedEventArgs e)
         {
-            mapView.Content = new Dirt();
+            mapView.DataContext = new Dirt();
         }
         private void SteelPlate_Click(object sender, RoutedEventArgs e)
         {
-            mapView.Content = new SteelPlate();
+            mapView.DataContext = new SteelPlate();
         }
     }
 }

Voilà — databinding. Of course, it still does the exact same things as before, but at least I made the title honest.

Today’s test project

Today’s project is the same as yesterday’s, but with real databinding. You can browse today’s code online, or check it out from my Subversion repository:

svn co http://svn.excastle.com/databinding_the_videogame/tags/part3

What’s next?

I’m thinking the next thing is to bind to a whole collection of terrain objects.

Yes, I know that’s what I said last time. But I’m learning this WPF stuff as I go, and on the off-chance somebody might someday stumble on this while they’re learning WPF, I think it’s worth blogging in small increments. This post is about binding a single object, which is useful info in its own right. The next one will be about binding a collection. I’m blogging in small commits.

Databinding the videogame, part 2: mapping terrain types to GUI

When I started trying to write a databound video game, my first thought was to use databinding for movement and scrolling. Which is all well and good, and I’ll cover them later. But I quickly hit on a more interesting problem (and solution).

My game needed to know which areas the hero could move through. This is a simple tile-based game, so each 40×40 map square is either passable or not. So my back-end logic needed a Map object to hold that “passability” data. It could be something as simple as a two-dimensional array of Booleans. Except that in my game, there are varying degrees of passability — for example, some terrains are passable, you just have to be moving at terminal velocity before you can crash through them. So it’s not just a Boolean; more like an enum, or, better yet, a Terrain object. So far so good.

And whenever the hero enters a new randomly-generated part of the map, I need to do two things: I need to add some new objects to this Terrain array, and I also need to add the corresponding GUI tiles on the screen.

“Aha!” thinks I. “Why should I waste ten minutes writing a simple method that adds something to two different data stores, when I could instead spend all weekend figuring out how to use an immensely complicated, nigh-impossible-to-troubleshoot databinding system that can automatically do that boring work for me?”

And so I databound the map to the GUI. And it worked. Eventually.

(If you’ve done much with WPF, this may not seem very interesting. You might even know better ways of doing it — and if so, please feel free to let me know in the comments. But I’m still learning this stuff, so I thought it was pretty cool.)

Starting small: mapping a class to a control

I won’t databind the whole map today; I’ll start with a small part — displaying one terrain square.

I’m being a good boy and writing my code in layers. The lower layer has the data model, which (for now) is just POCOs. The upper layer has the GUI. The data objects know nothing about anything GUI. So far so good — textbook stuff.

In the data model, I have classes to represent different terrains. At the moment there are only two — Dirt and SteelPlate. At the moment, they know nothing. Here are their declarations:

public class Dirt
{
}
public class SteelPlate
{
}

Not a typo. They’re both empty classes. Hey, come on, it’s only part 2 of the series.

The cool thing is, if I create an instance of this empty Dirt class, and try to show that object instance in the GUI, I don’t get a compile error, or an exception, or an empty screen, or the text “Game.Core.TerrainDataModels.Dirt”. I get a pretty picture of a square piece of dirt. (Or as close as my limited artistic ability will allow.) If I push a SteelPlate instance into the GUI, I get a pretty picture of a steel plate, with rivets in the corners and everything.

Okay, WPF doesn’t come with built-in knowledge of know to map a class called Dirt to a picture of dirt. How did I set up the mappings from those empty objects to GUI representations? Well, let’s start with this: it’s not in code. I’ll show you the only two lines of actual code I wrote in this test app (in the handlers for the “Dirt” and “Steel Plate” buttons, respectively):

mapView.Content = new Dirt();
// ...
mapView.Content = new SteelPlate();

The magic is in the way WPF can render any arbitrary object to the screen. There are a bunch of ways to tell it how to turn that POCO into a GUI control, but in this case, DataTemplate seems like the best fit. I just give it one DataTemplate that’s keyed to the Dirt class, that says “when you want to display the Dirt class on the screen, do it by creating and displaying a DirtView control”, and another one for SteelPlate that says “create and display a SteelPlateView control”. I defined a UserControl named MapView, which has the magic XAML to set up these mappings:

<UserControl x:Class="Game.Core.GameViews.MapView" ...boring XML stuff...>
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
            <TerrainViews:DirtView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type TerrainDataModels:SteelPlate}">
            <TerrainViews:SteelPlateView/>
        </DataTemplate>
    </UserControl.Resources>
</UserControl>

Then all the code has to do is add a Dirt or a SteelPlate somewhere inside a MapView, and the appropriate DirtView or SteelPlateView is created and displayed automatically.

You can see the full MapView code in MapView.xaml, but the interesting part is above — the rest of the file is XML namespace definitions, the control’s size, and its background color.

Interesting note: when I was thinking about what I wanted my classes to look like, I debated whether I should use a different class for each different type of terrain, or just have one Terrain class with an enum property. WPF decided that one for me, at least for now. It’s much easier to select a DataTemplate based on class. It’s possible to select a DataTemplate based on the value of a property, but it’s nowhere near as simple. So I’m keeping it simple.

Databinding the Videogame part 2: Sample project

Today’s test project

For today’s post, I wrote a test project with two buttons: Dirt and Steel Plate. Each button has one line of code (excerpted above) that creates a POCO and pushes it into the GUI, there to be displayed as a pretty picture. Nice and simple so far.

You can browse today’s code online, or check it out from my Subversion repository:

svn co http://svn.excastle.com/databinding_the_videogame/tags/part2

What’s next?

So far, I can show a single terrain square at a time. I’m thinking the next thing is to bind to a whole collection of them, and actually show a whole map (or a subset of it) on the screen. Stay tuned.

Databinding the videogame, part 1

This weekend, I decided to take a while off from my JavaScript video game, and play around with WPF a bit.

To keep things simple, I played around with re-creating what I could remember of a game I wrote long ago for text-mode BASIC, called “The Pit”. Didn’t get too far on that goal, but it kept me from being totally aimless. And in fairly short order, not only did I have a little stick-figure guy sliding smoothly across the screen in response to left and right cursor keys, I also had him plummeting to his doom whenever he walked off a cliff. Good times.

But since I was just playing, I was hacking all the game logic into the GUI. Decent way to get a feel for things, but not sustainable. And I got tired of always having to convert my in-game coordinates (1.0 = one tile) to screen coordinates (40 “pixels” = one tile) and back, and of having to track the horizontal and vertical coordinates in totally different ways (due to the way my game’s scrolling works), and of trying to keep my data model of “which tiles are obstacles” in sync with the GUI.

So that’s when I decided I would databind my video game.

Only makes sense, doesn’t it? Keep the player’s coordinates in logical scale, maintain them in one place, and have the databinding automatically handle the multiplying by 40 and the different rules for horizontal vs vertical scrolling. Add tiles to the Map data-model object and have them automatically appear on the screen. Databind the visual effects, like automatically showing the air-friction fireball when the player starts falling too fast. And I could even write unit tests for all the game logic. (Theoretically, anyway. How do you unit-test DoubleAnimation?)

Prepare to be entertained; I know precious little about WPF. I don’t have a copy of Expression Blend, and have to make do with the mind-bogglingly awful WPF designer in Visual Studio. I can write XAML for databinding, but only barely. I have no idea how much overhead there is in WPF databinding (though honestly, it can’t possibly be slower than trying to run JavaScript in IE). I have no idea when to use StaticResource vs an inline DataTemplate inside <ItemsControl.ItemTemplate>. I get the general gist of the Model-View-ViewModel pattern, but no real clue of how to design an effective view model in practice.

But I can use WPF animation to move a stick figure across the screen. I should be good to go.

I’ve got some stuff working. Learned some things by doing them the wrong way around, getting stuck, and eventually simplifying. And now I’m going to start blogging about some of it. Stay tuned.