#455 – Using ItemContainerStyle to Bind Data Elements in a Collection to a Grid

I showed earlier that to bind Grid.Row and Grid.Column properties on child items in an ItemsControl,  we need to set up the binding on the ContentPresenter elements that contain the individual items.  I did this by creating a class that inherited from ItemsControl and then set the bindings at runtime, programmatically.

There is a much easier way to do this, pointed out by reader Bruno (thanks Bruno)!

You can set up the bindings by specifying the ItemContainerStyle of the ItemsControl and using property setters to do the binding.

        <ItemsControl ItemsSource="{Binding ChessPieces}" Height="500" Width="500">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid ShowGridLines="True">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <!-- 7 more rows -->
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <!-- 7 more columns -->
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Grid.Row" Value="{Binding Row}"/>
                    <Setter Property="Grid.Column" Value="{Binding Column}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>

Advertisement

About Sean
Software developer in the Twin Cities area, passionate about software development and sailing.

22 Responses to #455 – Using ItemContainerStyle to Bind Data Elements in a Collection to a Grid

  1. Pingback: Dew Drop – December 21, 2011 (#1,224) | Alvin Ashcraft's Morning Dew

  2. hamit says:

    Thanks. But what properties does the type you are giving as ItemsSource have? How does {Binding Row} works? are you setting it from itemssource for each item?

  3. hamit says:

    Ok, got it. If ChessPieces is defined as List this definition works:

    public class ChessPiece
    {
    public string Text { get; set; }
    public int Row { get; set; }
    public int Column { get; set; }
    }
    Thank you for your example

    • Sean says:

      Yes, exactly. You just need something with Text, Row and Column properties and then put them in an ObservableCollection (see post #449).

      public class ChessPiece
      {
      public string Text { get; set; }
      public int Row { get; set; } // 0..n-1
      public int Column { get; set; } // 0..n-1

      public ChessPiece(string text, int row, int col)
      {
      Text = text;
      Row = row – 1;
      Column = col – 1;
      }
      }

  4. Tom says:

    Hi Sean,

    where can I download the (working) sample?

    Thanks

  5. Tom says:

    Hi,

    thanks. But I can’t see how the GridBasedItemsControl-class is binded with XAML(-Elements). This Code is not working with me.

    Thanks

    • Sean says:

      Tom, I just double-checked the code listed above and it’s working fine. Can you maybe post your entire XAML and code-behind files?

      • Tom says:

        Hi Sean,

        I am using the same code like codes on this page. The XAML-page:

        The Code-Bihind of XAML:

        public partial class MainWindow : Window
        {
        public MainWindow()
        {
        InitializeComponent();

        ObservableCollection ChessPieces = new ObservableCollection();
        ChessPieces.Add(new ChessPiece(“QR-Blk”, 1, 1));
        ChessPieces.Add(new ChessPiece(“QN-Blk”, 1, 2));
        ChessPieces.Add(new ChessPiece(“QB-Blk”, 1, 3));
        ChessPieces.Add(new ChessPiece(“Q-Blk”, 1, 4));
        ChessPieces.Add(new ChessPiece(“K-Blk”, 1, 5));
        ChessPieces.Add(new ChessPiece(“KB-Blk”, 1, 6));
        ChessPieces.Add(new ChessPiece(“KN-Blk”, 1, 7));
        ChessPieces.Add(new ChessPiece(“KR-Blk”, 1, 8));

        ChessPieces.Add(new ChessPiece(“P1-Blk”, 2, 1));
        ChessPieces.Add(new ChessPiece(“P2-Blk”, 2, 2));
        ChessPieces.Add(new ChessPiece(“P3-Blk”, 2, 3));
        ChessPieces.Add(new ChessPiece(“P4-Blk”, 2, 4));
        ChessPieces.Add(new ChessPiece(“P5-Blk”, 2, 5));
        ChessPieces.Add(new ChessPiece(“P6-Blk”, 2, 6));
        ChessPieces.Add(new ChessPiece(“P7-Blk”, 2, 7));
        ChessPieces.Add(new ChessPiece(“P8-Blk”, 2, 8));
        OnPropertyChanged(“ChessPieces”);

        }

        private void OnPropertyChanged(string p)
        {

        }
        }

        and I have 2 other classes names:

        class ChessPiece
        {
        public string Text{get; set;}
        public int Row { get; set; }
        public int Column { get; set; }

        public ChessPiece(string text, int row, int column)
        {
        this.Text = text;
        this.Row = row;
        this.Column = column;
        }
        }

        and

        class GridBasedItemsControl : ItemsControl
        {
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
        base.PrepareContainerForItemOverride(element, item);

        ContentPresenter cp = element as ContentPresenter;

        if ((cp != null) && (item != null))
        {
        BindingOperations.SetBinding(cp, Grid.RowProperty, new Binding { Source = item, Path = new PropertyPath(“Row”)});
        BindingOperations.SetBinding(cp, Grid.ColumnProperty, new Binding { Source = item, Path = new PropertyPath(“Column”) });
        }
        }
        }

        What is wrong hier?

        Thanks again

      • Sean says:

        A couple of comments:

        Where are you setting up the data binding, i.e setting the data context? I don’t see it in the code behind. If you want to bind to the ObservableCollection that you created, you’ll have to make it a property of your class, set the data context of the window or the ItemsControl and then implement the INotifyPropertyChanged event.

        Also, you don’t need a derived class for the ItemsControl–using the XAML that I posted does the data binding to the Row and Column properties.

        Sean

  6. Tom says:

    Sorry, my XAML-file can not be displayed.

  7. Tom says:

    Hi,

    can you complete my class with this code please?

    Thanks

    • Sean says:

      :O) I think you’re better off reading my posts and completing it yourself–it’s pretty straightforward.

    • Sean says:

      To start with, just create a simple WPF app that uses data binding to bind some property to a property in your code-behind. This post on using an ItemContainerStyle is much more advanced and won’t make much sense if you don’t understand basic data binding. If you’re going to do any work in WPF, it will be important to have a good understanding of data binding and the INotifyPropertyChanged interface. Take a look at http://msdn.microsoft.com/en-us/library/ms229614.aspx for starters.

  8. Tom says:

    Hi Sean,

    thanks. It works. But I can’t understand what role plays the GridBasedItemsControl-class. This class is not called.

    Thanks

    • Sean says:

      You don’t need a custom class–all you need to bind elements of a collection to the Grid is to specify the ItemContainerStyle in the XAML.

      • Tom says:

        That is correct. But you should save the grid row and column for each object like this “ChessPieces.Add(new ChessPiece(“QR-Blk”, 1, 1));”. I need a way to show the contents automatically for example into two columns and 10, without that the column and rows numbers have to save.

        Thanks

  9. Alexander says:

    How can I set Height of RowDefinition to “Auto” or “*” depended on property “FillParent” in my ControlViewModel?
    How can I fill RowDefinitions dynamically depended on collection length?

    I have collection of ControlViewModel and I try to create same count of Rows. I use now StackPanel but StackPanel can’t fill all parent height like LinearLayout in Android does. DockPanel is also useless cause my ViewModel should add ControlViewModel in certain sequence (last ControlViewModel will be with FillParent == true).

    Thanks.

    • Sean says:

      Probably you don’t want to use a Grid, since you don’t typically change the Row or Column definitions dynamically. You likely want to use something like a ListBox to bind to your collection and then modify its control template to look like what you want/need.

      • Alexander says:

        ItemsControl is similar to ListBox. Does WPF have panel which stretch like DockPanel/Grid or LinearLayout in Android? Or should I create class derived from Panel for this?

  10. Mace says:

    How would you code the next step for moving the pieces?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: