#455 – Using ItemContainerStyle to Bind Data Elements in a Collection to a Grid
December 21, 2011 22 Comments
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>
Pingback: Dew Drop – December 21, 2011 (#1,224) | Alvin Ashcraft's Morning Dew
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?
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
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;
}
}
Hi Sean,
where can I download the (working) sample?
Thanks
All of the code that I have is included in this post or other posts. See https://wpf.2000things.com/2011/12/13/449-data-binding-elements-in-a-collection-to-a-grid-part-ii/ and https://wpf.2000things.com/2011/12/21/455-using-itemcontainerstyle-to-bind-data-elements-in-a-collection-to-a-grid/ (the post you commented on).
Hi,
thanks. But I can’t see how the GridBasedItemsControl-class is binded with XAML(-Elements). This Code is not working with me.
Thanks
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?
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
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
Sorry, my XAML-file can not be displayed.
Hi,
can you complete my class with this code please?
Thanks
:O) I think you’re better off reading my posts and completing it yourself–it’s pretty straightforward.
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.
Hi Sean,
thanks. It works. But I can’t understand what role plays the GridBasedItemsControl-class. This class is not called.
Thanks
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.
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
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.
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.
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?
How would you code the next step for moving the pieces?
Would help me as well 😉 Love your Work.