假设你有一个集合,想将集合的数据显示在Grid上,数据所显示行和列根据集合的内容动态设置。你需要在集合的单个实例中有指定行和列的属性并对其进行绑定。
你可以使用Grid 面板作为ItemsControl的ItemsPanel 。这样子元素将以Grid 的方式进行布局。然后你可以设置ItemTemplate 属性确定如何显示每个子项的内容。
首先你可以试着用下面的代码:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<ItemsControl ItemsSource="{Binding ChessPieces}" Height="500" Width="500">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Text}" Grid.Row="{Binding Row}" Grid.Column="{Binding Column}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
上面的代码中ItemsPanel 为一个8行8列的Grid ,用以显示集合。并且将集合ChessPieces绑定到ItemsSource 。下面是ChessPieces的定义,并添加两行实例进行测试:
public ObservableCollection<ChessPiece> ChessPieces { get; private set; }
ChessPieces = new ObservableCollection<ChessPiece>();
ChessPieces.Add(new ChessPiece("QR-Blk", 1, 0));
ChessPieces.Add(new ChessPiece("QN-Blk", 1, 1));
ChessPieces.Add(new ChessPiece("QB-Blk", 1, 2));
ChessPieces.Add(new ChessPiece("Q-Blk", 1, 3));
ChessPieces.Add(new ChessPiece("K-Blk", 1, 4));
ChessPieces.Add(new ChessPiece("KB-Blk", 1, 5));
ChessPieces.Add(new ChessPiece("KN-Blk", 1, 6));
ChessPieces.Add(new ChessPiece("KR-Blk", 1, 7));
ChessPieces.Add(new ChessPiece("P1-Blk", 2, 0));
ChessPieces.Add(new ChessPiece("P2-Blk", 2, 1));
ChessPieces.Add(new ChessPiece("P3-Blk", 2, 2));
ChessPieces.Add(new ChessPiece("P4-Blk", 2, 3));
ChessPieces.Add(new ChessPiece("P5-Blk", 2, 4));
ChessPieces.Add(new ChessPiece("P6-Blk", 2, 5));
ChessPieces.Add(new ChessPiece("P7-Blk", 2, 6));
ChessPieces.Add(new ChessPiece("P8-Blk", 2, 7));
//OnPropertyChanged("ChessPieces");
ChessPiece的定义:
public class ChessPiece
{
public ChessPiece(string text, int row, int column)
{
this.Text = text;
this.Row = row;
this.Column = column;
}
public string Text { get; set; }
public int Row { get; set; }
public int Column { get; set; }
}
到现在为止,每个元素都已经显示在了Grid面板中,但是并没有显示在预想的位置,而是全部显示在0行0列。Grid.Row和Grid.Column 附加属性被忽略了。
造成这个现象的原因我们是在数据模板中设置的Label 的Grid.Row 和Grid.Column ,Label 控件并不是Grid的直接子元素,所以指定的行和列并没有起效果。
我们可以从ItemsControl 可视树中看到,Grid的直接子元素是ContentPresenter,而Lable 控件是ContentPresenter 的子元素。
那么我们应该做的是设置ContentPresenter的Grid.Row和Grid.Column 属性,而不是Label的。我们可以通过重写ItemsControl.PrepareContainerForItemOverride 方法来实现它。在这个方法中准备指定的元素以显示指定项,它会在收到要显示的项(比如本例中的ContentPresenter )和已经包含的内容(比如本例中的ChessPiece 实例)的时候调用。我们在这个方法中为ChessPiece 实例绑定行和列到ContentPresenter 。
public 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") });
}
}
}
上面的代码新建了一个继承自ItemsControl的GridBasedItemsControl类,并重写了PrepareContainerForItemOverride 以实现行和列的绑定。为了将绑定应用,我们需要将最开始XAML代码中Window下的根元素ItemsControl改为GridBasedItemsControl。
<local:GridBasedItemsControl ItemsSource="{Binding ChessPieces}" Height="500" Width="500">
......
</local:GridBasedItemsControl>
原文地址:https://wpf.2000things.com/2011/12/12/448-data-binding-elements-in-a-collection-to-a-grid-part-i/