views:

327

answers:

2

I would like to use a Grid as an ItemsHost but none of the items appear in their bound (column, row) positions. How can I make this work? As a simple example:

XAML

<ItemsControl ItemsSource="{Binding DataSet}">
 <ItemsControl.ItemTemplate>
  <DataTemplate>
   <TextBlock Grid.Column="{Binding Col}" Grid.Row="{Binding Row}" 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">
       <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
       </Grid.ColumnDefinitions>
       <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
       </Grid.RowDefinitions>
      </Grid>
     </ControlTemplate>
    </Setter.Value>
   </Setter>
  </Style>
 </ItemsControl.Style>
</ItemsControl>

Codebehind

Class Window1 

    Private myTestData As TestData

    Public Sub New()
     ' This call is required by the Windows Form Designer.
     InitializeComponent()

     ' Add any initialization after the InitializeComponent() call.
     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, "Column " + x.ToString + ", Row " + y.ToString))
      Next
     Next
    End Sub

End Class

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 Property Text() As String
     Get
      Return myText
     End Get
     Set(ByVal value As String)
      myText = value
     End Set
    End Property

    Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal name As String)
     Me.Row = y
     Me.Col = x
     Me.Text = name
    End Sub

End Class
+2  A: 

Because you're using an ItemsControl, a container is generated for each item. That container (an instance of ContentPresenter for a plain old ItemsControl) wraps the TextBlock, and is a direct child of the Grid. Therefore, the Grid never even sees the Column and Row properties on the TextBlock because it's looking instead at the container.

You can solve this by setting an ItemContainerStyle that binds the appropriate properties for the container:

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="Grid.Column" Value="{Binding Column}"/>
        <Setter Property="Grid.Row" Value="{Binding Row}"/>
    </Style>
</ItemsControl.ItemContainerStyle>

HTH, Kent

Kent Boogaart
You just became my hero.
Bryan Anderson
A: 

Possible workaround: if you use UniformGrid, you may not need to specify the rows and columns.

Daniel Earwicker