.net 如何基于二维数组填充 WPF 网格

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/276808/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:33:39  来源:igfitidea点击:

How to populate a WPF grid based on a 2-dimensional array

.netwpfdata-bindingtemplatesgrid

提问by Daniel Plaisted

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);
        }
    }

}

回答by Jobi Joy

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

Grid 的目的不是用于真正的数据绑定,它只是一个面板。我列出了完成二维列表可视化的最简单方法

<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.

并在后面的代码中使用 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;
    }

This gives you the following screen as output. You can edit the DataTemplate_Level2 to add more specific data of your object.

这将为您提供以下屏幕作为输出。您可以编辑 DataTemplate_Level2 以添加对象的更多特定数据。

alt text

替代文字

回答by Fredrik Hedblad

Here is a Control called DataGrid2Dthat can be populated based on a 2D or
1D array (or anything that implements the IListinterface). It subclasses DataGridand adds a property called ItemsSource2Dwhich is used for binding against 2D or 1D sources. Library can be downloaded hereand source code can be downloaded here.

这是一个DataGrid2D可以基于二维或
一维数组(或任何实现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、二维数组或一维数组,如下所示

<dg2d:DataGrid2D Name="dataGrid2D"
                 ItemsSource2D="{Binding Int2DList}"/>

enter image description here

在此处输入图片说明



OLD POST
Here is an implementation that can bind a 2D array to the WPF datagrid.

旧帖子
这是一个可以将二维数组绑定到 WPF 数据网格的实现。

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 thisthread.

然后我们要将这个二维数组绑定到 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); } }  
} 

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.

然后我用一个方法创建了一个静态帮助器类,该类可以接受一个二维数组并使用上面的 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.

这几乎足以绑定到,但 Binding 中的 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");
}

And after this we can use

在此之后我们可以使用

c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);

And the output will look like this

输出将如下所示

alt text

替代文字

Any changes made in the DataGridwill be reflected in the m_intArray.

中所做的任何更改DataGrid都将反映在 m_intArray 中。

回答by Johan Larsson

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: enter image description here

渲染: 在此处输入图片说明

回答by CitizenInsane

Here is another solution based on Meleak's answer but without requiring for an AutoGeneratingColumnevent handler in the code behind of each binded DataGrid:

这是基于Meleak的答案的另一种解决方案,但不需要AutoGeneratingColumn在每个 binded 后面的代码中使用事件处理程序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;
}

回答by Torsten

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.thinkbottomup.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] 访问单元格。