如何基于2维数组填充WPF网格
我有一个2维的对象数组,我基本上想要将每个数据绑定到WPF网格中的单元格。 目前我有这个工作,但我正在做程序化的大部分。 我创build了正确数量的行和列定义,然后循环遍历单元格并创build控件,并为每个单元格设置正确的绑定。
至less我希望能够使用模板来指定xaml中的控件和绑定。 理想情况下,我想摆脱程序代码,只是用数据绑定来完成,但我不确定这是可能的。
这是我目前使用的代码:
public void BindGrid() { m_Grid.Children.Clear(); m_Grid.ColumnDefinitions.Clear(); m_Grid.RowDefinitions.Clear(); for (int x = 0; x < MefGrid.Width; x++) { m_Grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star), }); } for (int y = 0; y < MefGrid.Height; y++) { m_Grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star), }); } for (int x = 0; x < MefGrid.Width; x++) { for (int y = 0; y < MefGrid.Height; y++) { Cell cell = (Cell)MefGrid[x, y]; SolidColorBrush brush = new SolidColorBrush(); var binding = new Binding("On"); binding.Converter = new BoolColorConverter(); binding.Mode = BindingMode.OneWay; BindingOperations.SetBinding(brush, SolidColorBrush.ColorProperty, binding); var rect = new Rectangle(); rect.DataContext = cell; rect.Fill = brush; rect.SetValue(Grid.RowProperty, y); rect.SetValue(Grid.ColumnProperty, x); m_Grid.Children.Add(rect); } } }
网格的目的不是真正的数据绑定,而只是一个面板。 我列出了完成二维列表可视化的最简单方法
<Window.Resources> <DataTemplate x:Key="DataTemplate_Level2"> <Button Content="{Binding}" Height="40" Width="50" Margin="4,4,4,4"/> </DataTemplate> <DataTemplate x:Key="DataTemplate_Level1"> <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </DataTemplate> </Window.Resources> <Grid> <ItemsControl x:Name="lst" ItemTemplate="{DynamicResource DataTemplate_Level1}"/> </Grid>
并且在后面的代码中用TwoDimentional数据结构设置lst的ItemsSource。
public Window1() { List<List<int>> lsts = new List<List<int>>(); for (int i = 0; i < 5; i++) { lsts.Add(new List<int>()); for (int j = 0; j < 5; j++) { lsts[i].Add(i * 10 + j); } } InitializeComponent(); lst.ItemsSource = lsts; }
这给你以下屏幕作为输出。 您可以编辑DataTemplate_Level2以添加对象的更多特定数据。
这里是一个名为DataGrid2D
,可以根据2D或者
1D数组(或实现IList
接口的任何东西)。 它将子类DataGrid
并添加一个名为ItemsSource2D
的属性,用于绑定2D或1D源。 库可以在这里下载,源代码可以在这里下载。
要使用它只需添加对DataGrid2DLibrary.dll的引用,请添加此名称空间
xmlns:dg2d="clr-namespace:DataGrid2DLibrary;assembly=DataGrid2DLibrary"
然后创build一个DataGrid2D并将其绑定到像这样的IList,2D数组或一维数组
<dg2d:DataGrid2D Name="dataGrid2D" ItemsSource2D="{Binding Int2DList}"/>
旧post
这是一个可以将二维数组绑定到WPF数据网格的实现。
假设我们有这个二维数组
private int[,] m_intArray = new int[5, 5]; ... for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { m_intArray[i,j] = (i * 10 + j); } }
然后我们想把这个二维数组绑定到WPF DataGrid,我们所做的改变应该反映在数组中。 为了做到这一点,我从这个线程使用了Eric Lippert的Ref类。
public class Ref<T> { private readonly Func<T> getter; private readonly Action<T> setter; public Ref(Func<T> getter, Action<T> setter) { this.getter = getter; this.setter = setter; } public T Value { get { return getter(); } set { setter(value); } } }
然后我做了一个静态辅助类,可以使用一个2D数组,并使用上面的Ref类返回一个DataView。
public static DataView GetBindable2DArray<T>(T[,] array) { DataTable dataTable = new DataTable(); for (int i = 0; i < array.GetLength(1); i++) { dataTable.Columns.Add(i.ToString(), typeof(Ref<T>)); } for (int i = 0; i < array.GetLength(0); i++) { DataRow dataRow = dataTable.NewRow(); dataTable.Rows.Add(dataRow); } DataView dataView = new DataView(dataTable); for (int i = 0; i < array.GetLength(0); i++) { for (int j = 0; j < array.GetLength(1); j++) { int a = i; int b = j; Ref<T> refT = new Ref<T>(() => array[a, b], z => { array[a, b] = z; }); dataView[i][j] = refT; } } return dataView; }
这几乎足以绑定到,但绑定中的path将指向Ref对象,而不是我们需要的Ref.Value,所以我们必须在生成列时对其进行更改。
<DataGrid Name="c_dataGrid" RowHeaderWidth="0" ColumnHeaderHeight="0" AutoGenerateColumns="True" AutoGeneratingColumn="c_dataGrid_AutoGeneratingColumn"/> private void c_dataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { DataGridTextColumn column = e.Column as DataGridTextColumn; Binding binding = column.Binding as Binding; binding.Path = new PropertyPath(binding.Path.Path + ".Value"); }
之后我们可以使用
c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);
输出结果如下所示
DataGrid
所做的任何更改都将反映在m_intArray中。
我为DataGrid
写了一个附加属性的小型库。 这是来源
示例,其中Data2D是int[,]
:
<DataGrid HeadersVisibility="None" dataGrid2D:Source2D.ItemsSource2D="{Binding Data2D}" />
呈现:
你可能想看看这个链接: http : //www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python
如果您使用List中的List,则可以使用myList [x] [y]来访问单元格。
这里是另一个基于Meleak的答案的解决scheme,但不需要在每个绑定的DataGrid
后面的代码中使用AutoGeneratingColumn
事件处理程序:
public static DataView GetBindable2DArray<T>(T[,] array) { var table = new DataTable(); for (var i = 0; i < array.GetLength(1); i++) { table.Columns.Add(i+1, typeof(bool)) .ExtendedProperties.Add("idx", i); // Save original column index } for (var i = 0; i < array.GetLength(0); i++) { table.Rows.Add(table.NewRow()); } var view = new DataView(table); for (var ri = 0; ri < array.GetLength(0); ri++) { for (var ci = 0; ci < array.GetLength(1); ci++) { view[ri][ci] = array[ri, ci]; } } // Avoids writing an 'AutogeneratingColumn' handler table.ColumnChanged += (s, e) => { var ci = (int)e.Column.ExtendedProperties["idx"]; // Retrieve original column index var ri = e.Row.Table.Rows.IndexOf(e.Row); // Retrieve row index array[ri, ci] = (T)view[ri][ci]; }; return view; }