If you are even a little bit familiar with the Model-View-View Model pattern and its implementation in WPF, you have probably run into the ItemsControl control. Actually, even if you haven’t used MVVM, you may have used it. Somewhat like the Repeater control in ASP.NET, this control allows you to bind to a data source and do something in XAML for each of the items in that data source. In any case, let’s play around with the ItemsControl element in WPF a little bit.
[more]
Imagine we have the need to display a Tic-Tac-Toe board in an application. Our model might look like this:
public class MoveBase { public int Row { get; set; } public int Column { get; set; } } public class XMove : MoveBase { } public class OMove : MoveBase { }
You’ll notice there is a base class and two descendent classes. Each of the inheriting classes represents a move by either the X-player or the O-player.
Our View Model looks something like this:
public class MainViewModel : ViewModelBase { public MainViewModel() { Moves = new ObservableCollection<MoveBase>() { new XMove() { Row = 0, Column = 0 }, new OMove() { Row = 1, Column = 0 }, new XMove() { Row = 1, Column = 1 }, new OMove() { Row = 0, Column = 2 }, new XMove() { Row = 2, Column = 2} }; } public ObservableCollection<MoveBase> Moves { get; set; } }
For the sake of our example, the constructor creates some sample moves, but those could be loaded from a data store in a “real world” application. The key is the property called “Moves” that will be bound to in the View (XAML) portion of the app.
The View part of the app might look like this:
<Window.Resources> <DataTemplate DataType="{x:Type m:XMove}"> <Image Source="XMove.png" Stretch="None" /> </DataTemplate> <DataTemplate DataType="{x:Type m:OMove}"> <Image Source="OMove.png" Stretch="None" /> </DataTemplate> </Window.Resources> <Grid> <ItemsControl ItemsSource="{Binding Path=Moves}"> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Grid.Row" Value="{Binding Path=Row}" /> <Setter Property="Grid.Column" Value="{Binding Path=Column}" /> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> </Grid> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </Grid>
You will notice there is a data template for each of the MoveBase types that will display the appropriate image for the type of move.
Now, to the ItemsControl: Notice that it is bound directly to the Moves property in the ViewModel. Also, the <ItemsPanelTemplate> defines a 3x3 grid for the items in the Moves collection. Every item (an XMove or an OMove) will be placed inside of the grid defined here.
The last thing to take note of is the <ItemsControl.ItemContainerStyle> element. When the ItemsControl control is rendered, it actually wraps each of the items it is bound to in a container. That means each of our images won’t be directly contained in the 3x3 grid; thus we need to assign the row and column to each item’s container. We do that with some styling. Two property setters take care of this detail.
The resulting form should look like this:
Because of the ItemsControl control, the placement of the images can be controlled from the View Model. Of course, this solution just displays a Tic-Tac-Toe board, but it should be a relatively small change to add a few commands and create a playable Tic-Tac-Toe board.