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

时间:2008-11-10 01:05:52

标签: .net wpf data-binding templates grid

我有一个二维对象数组,我基本上想要将每个对象数据绑定到WPF网格中的一个单元格。目前我有这个工作,但我在程序上做了大部分工作。我创建了正确数量的行和列定义,然后循环遍历单元格并创建控件并为每个控件设置正确的绑定。

至少我希望能够使用模板来指定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);
        }
    }

}

4 个答案:

答案 0 :(得分:66)

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

<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以添加对象的更多特定数据。

alt text

答案 1 :(得分:42)

这是一个名为DataGrid2D的控件,可以根据2D或
来填充 1D数组(或实现IList接口的任何内容)。它子类DataGrid并添加一个名为ItemsSource2D的属性,用于绑定2D或1D源。可以下载资源库here,可以下载源代码here

要使用它,只需添加对DataGrid2DLibrary.dll的引用,添加此命名空间

xmlns:dg2d="clr-namespace:DataGrid2DLibrary;assembly=DataGrid2DLibrary"

然后创建一个DataGrid2D并将其绑定到IList,2D数组或1D数组,如下所示

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

enter image description here


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

假设我们有这个2D数组

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

然后我们想要将这个2D数组绑定到WPF DataGrid,我们所做的更改将反映在数组中。为此,我使用了来自this线程的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;
}

这几乎足以绑定,但Binding中的Path将指向Ref对象而不是我们需要的Ref.Value,因此我们必须在生成Columns时更改它。

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

输出看起来像这样

alt text

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

答案 2 :(得分:3)

我为DataGrid写了一个附属属性的小型库。 Here is the source

示例,其中Data2D为int[,]

<DataGrid HeadersVisibility="None"
          dataGrid2D:Source2D.ItemsSource2D="{Binding Data2D}" />

渲染: enter image description here

答案 3 :(得分:0)

您可以查看以下链接:http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

如果您在列表中使用列表,则可以使用myList [x] [y]访问单元格。