I have a 2-dimensional array of objects and I basically want to databind each one to a cell in a WPF grid. Currently I have this working but I am doing most of it procedurally. I create the correct number of row and column definitions, then I loop through the cells and create the controls and set up the correct bindings for each one.
我有一个二维的对象数组,我基本上想把每个对象都绑定到WPF网格中的一个单元。目前我有这个工作,但大部分是按程序进行的。我创建正确的行和列定义数量,然后循环遍历单元格,创建控件,并为每个单元设置正确的绑定。
At a minimum I would like to be able to use a template to specify the controls and bindings in xaml. Ideally I would like to get rid of the procedural code and just do it all with databinding, but I'm not sure that's possible.
至少,我希望能够使用模板来指定xaml中的控件和绑定。理想情况下,我希望去掉过程代码,只使用数据绑定完成所有工作,但我不确定这是否可能。
Here is the code I am currently using:
以下是我目前使用的代码:
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);
}
}
}
5 个解决方案
#1
61
The purpose of the Grid is not for real databinding, it is just a panel. I am listing down the easiest way to accomplish the visualization of a two dimensional list
网格的目的不是为了真正的数据库,它只是一个面板。我列出了实现二维列表可视化的最简单方法
<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>
And in the code behind set the ItemsSource of lst with a TwoDimentional data structure.
在后面的代码中,用二维数据结构设置ItemsSource的lst。
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;
}
This gives you the following screen as output. You can edit the DataTemplate_Level2 to add more specific data of your object.
这将提供以下屏幕作为输出。您可以编辑DataTemplate_Level2以添加对象的更具体的数据。
#2
41
Here is a Control called DataGrid2D
that can be populated based on a 2D or
1D array (or anything that implements the IList
interface). It subclasses DataGrid
and adds a property called ItemsSource2D
which is used for binding against 2D or 1D sources. Library can be downloaded here and source code can be downloaded here.
这里有一个名为DataGrid2D的控件,可以基于2D或1D数组(或实现IList接口的任何东西)填充该控件。它子类化DataGrid,并添加一个名为ItemsSource2D的属性,用于绑定2D或1D源。图书馆可在此下载,源代码可在此下载。
To use it just add a reference to DataGrid2DLibrary.dll, add this namespace
要使用它,只需向DataGrid2DLibrary添加一个引用。dll,添加这个名称空间
xmlns:dg2d="clr-namespace:DataGrid2DLibrary;assembly=DataGrid2DLibrary"
and then create a DataGrid2D and bind it to your IList, 2D array or 1D array like this
然后创建一个DataGrid2D并将其绑定到IList、2D数组或一维数组中
<dg2d:DataGrid2D Name="dataGrid2D"
ItemsSource2D="{Binding Int2DList}"/>
OLD POST
Here is an implementation that can bind a 2D array to the WPF datagrid.
这里的旧POST是一个可以将2D数组绑定到WPF datagrid的实现。
Say we have this 2D array
假设我们有这个二维数组
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);
}
}
And then we want to bind this 2D array to the WPF DataGrid and the changes we make shall be reflected in the array. To do this I used Eric Lippert's Ref class from this thread.
然后我们要将这个2D数组绑定到WPF数据网格中,我们所做的更改将在数组中反映出来。为此,我从这个线程中使用了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); } }
}
Then I made a static helper class with a method that could take a 2D array and return a DataView using the Ref class above.
然后我用一个方法创建了一个静态助手类,该方法可以获取一个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;
}
This would almost be enough to bind to but the Path in the Binding will point to the Ref object instead of the Ref.Value which we need so we have to change this when the Columns get generated.
这几乎可以绑定到绑定,但是绑定中的路径将指向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");
}
And after this we can use
之后我们就可以用了
c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);
And the output will look like this
输出是这样的
Any changes made in the DataGrid
will be reflected in the m_intArray.
在DataGrid中所做的任何更改都将反映在m_intintarray中。
#3
3
I wrote a small library of attached properties for the DataGrid
. Here is the source
我为DataGrid编写了一个小的附加属性库。这是源
Sample, where Data2D is int[,]
:
样本,其中Data2D为int[,]:
<DataGrid HeadersVisibility="None"
dataGrid2D:Source2D.ItemsSource2D="{Binding Data2D}" />
Renders:
呈现:
#4
0
You may want to check out this link: http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python
您可能想要查看这个链接:http://www.thinksurup.com.au/site/blog/game_of_life_in_xaml_wpf_using_embedded_python。
If you use a List within a List you can use myList[x][y] to access a cell.
如果使用列表中的列表,可以使用myList[x][y]访问单元格。
#5
0
Here is another solution based on Meleak's answer but without requiring for an AutoGeneratingColumn
event handler in the code behind of each binded DataGrid
:
下面是另一个基于Meleak的解决方案,但是不需要在每个绑定的DataGrid后面的代码中使用自动生成列事件处理程序:
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;
}
#1
61
The purpose of the Grid is not for real databinding, it is just a panel. I am listing down the easiest way to accomplish the visualization of a two dimensional list
网格的目的不是为了真正的数据库,它只是一个面板。我列出了实现二维列表可视化的最简单方法
<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>
And in the code behind set the ItemsSource of lst with a TwoDimentional data structure.
在后面的代码中,用二维数据结构设置ItemsSource的lst。
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;
}
This gives you the following screen as output. You can edit the DataTemplate_Level2 to add more specific data of your object.
这将提供以下屏幕作为输出。您可以编辑DataTemplate_Level2以添加对象的更具体的数据。
#2
41
Here is a Control called DataGrid2D
that can be populated based on a 2D or
1D array (or anything that implements the IList
interface). It subclasses DataGrid
and adds a property called ItemsSource2D
which is used for binding against 2D or 1D sources. Library can be downloaded here and source code can be downloaded here.
这里有一个名为DataGrid2D的控件,可以基于2D或1D数组(或实现IList接口的任何东西)填充该控件。它子类化DataGrid,并添加一个名为ItemsSource2D的属性,用于绑定2D或1D源。图书馆可在此下载,源代码可在此下载。
To use it just add a reference to DataGrid2DLibrary.dll, add this namespace
要使用它,只需向DataGrid2DLibrary添加一个引用。dll,添加这个名称空间
xmlns:dg2d="clr-namespace:DataGrid2DLibrary;assembly=DataGrid2DLibrary"
and then create a DataGrid2D and bind it to your IList, 2D array or 1D array like this
然后创建一个DataGrid2D并将其绑定到IList、2D数组或一维数组中
<dg2d:DataGrid2D Name="dataGrid2D"
ItemsSource2D="{Binding Int2DList}"/>
OLD POST
Here is an implementation that can bind a 2D array to the WPF datagrid.
这里的旧POST是一个可以将2D数组绑定到WPF datagrid的实现。
Say we have this 2D array
假设我们有这个二维数组
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);
}
}
And then we want to bind this 2D array to the WPF DataGrid and the changes we make shall be reflected in the array. To do this I used Eric Lippert's Ref class from this thread.
然后我们要将这个2D数组绑定到WPF数据网格中,我们所做的更改将在数组中反映出来。为此,我从这个线程中使用了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); } }
}
Then I made a static helper class with a method that could take a 2D array and return a DataView using the Ref class above.
然后我用一个方法创建了一个静态助手类,该方法可以获取一个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;
}
This would almost be enough to bind to but the Path in the Binding will point to the Ref object instead of the Ref.Value which we need so we have to change this when the Columns get generated.
这几乎可以绑定到绑定,但是绑定中的路径将指向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");
}
And after this we can use
之后我们就可以用了
c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);
And the output will look like this
输出是这样的
Any changes made in the DataGrid
will be reflected in the m_intArray.
在DataGrid中所做的任何更改都将反映在m_intintarray中。
#3
3
I wrote a small library of attached properties for the DataGrid
. Here is the source
我为DataGrid编写了一个小的附加属性库。这是源
Sample, where Data2D is int[,]
:
样本,其中Data2D为int[,]:
<DataGrid HeadersVisibility="None"
dataGrid2D:Source2D.ItemsSource2D="{Binding Data2D}" />
Renders:
呈现:
#4
0
You may want to check out this link: http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python
您可能想要查看这个链接:http://www.thinksurup.com.au/site/blog/game_of_life_in_xaml_wpf_using_embedded_python。
If you use a List within a List you can use myList[x][y] to access a cell.
如果使用列表中的列表,可以使用myList[x][y]访问单元格。
#5
0
Here is another solution based on Meleak's answer but without requiring for an AutoGeneratingColumn
event handler in the code behind of each binded DataGrid
:
下面是另一个基于Meleak的解决方案,但是不需要在每个绑定的DataGrid后面的代码中使用自动生成列事件处理程序:
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;
}