How to use a Grid as an ItemsHost
You know how sometimes you run into problems that you know should be really simple but you just can’t manage to solve them? Well one of those problems for me was making a grid an ItemsHost and actually having it work. Sure you can always set IsItemsHost=”True” but you’ll just end up with every item in your ItemsSource all stacked on top of each other. A few weeks ago I ran into this same problem again and couldn’t find a way around it. So after beating my head against the wall for a few hours I finally gave up and posted on StackOverflow, within a half hour I had the answer I’d been trying to figure out for the better part of a day.
It turns out that setting IsItemsHost to True on a panel creates the required ContentPresenters and nothing you can do will let you get at them directly (believe me, I tried). The solution is to create a keyless style that targets ContentPresenter and let this style take care of the dirty work.
Without furthur ado, here’s an example.
The Data
Class DataPoint Private myRow As Integer Public Property Row() As Integer Get Return myRow End Get Set(ByVal value As Integer) myRow = value End Set End Property Private myCol As Integer Public Property Col() As Integer Get Return myCol End Get Set(ByVal value As Integer) myCol = value End Set End Property Private myText As String Public ReadOnly Property Text() As String Get Return myText End Get End Property Public Sub New(ByVal x As Integer, ByVal y As Integer) Me.Row = y Me.Col = x myText = "Column " + x.ToString + ", Row " + y.ToString End Sub End Class
Load the test data and set it as the data context for the window
Class GridAsItemsHostExample Private myTestData As TestData Public Sub New() InitializeComponent() myTestData = New TestData() Me.DataContext = myTestData End Sub End Class Class TestData Private myDataSet As List(Of DataPoint) Public Property DataSet() As List(Of DataPoint) Get Return myDataSet End Get Set(ByVal value As List(Of DataPoint)) myDataSet = value End Set End Property Public Sub New() Me.DataSet = New List(Of DataPoint) For x As Integer = 0 To 1 For y As Integer = 0 To 1 Me.DataSet.Add(New DataPoint(x, y)) Next Next End Sub End Class
And finally the window, note the ContentPresenter style.
<Window x:Class="GridAsItemsHostExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Grid As ItemsHost Example"
SizeToContent="WidthAndHeight">
<ItemsControl ItemsSource="{Binding DataSet}">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Column" Value="{Binding Col}" />
<Setter Property="Grid.Row" Value="{Binding Row}" />
<Setter Property="Margin" Value="5" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Style>
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Grid HorizontalAlignment="Stretch"
IsItemsHost="True"
ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Style>
</ItemsControl>
</Window>

