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.


@@ -3,8 +3,7 @@
-    MinWidth="40" MinHeight="40" Background="Gray"
-    Content="{Binding}">
+    MinWidth="40" MinHeight="40" Background="Gray">
         <DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
@@ -13,4 +12,5 @@
+    <ItemsControl ItemsSource="{Binding}"/>


@@ -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()
+            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.


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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *