Cómo llenar una grilla WPF basada en una matriz bidimensional

Tengo una matriz bidimensional de objetos y, básicamente, quiero conectarlos en una celda en una grilla WPF. Actualmente tengo esto funcionando pero estoy haciendo la mayor parte del proceso. Creo el número correcto de definiciones de filas y columnas, luego recorro las celdas y creo los controles y configuro los enlaces correctos para cada uno.

Como mínimo, me gustaría poder utilizar una plantilla para especificar los controles y enlaces en xaml. Idealmente, me gustaría deshacerme del código de procedimiento y simplemente hacerlo todo con enlaces de datos, pero no estoy seguro de que eso sea posible.

Aquí está el código que estoy usando actualmente:

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

El propósito de la Grilla no es el enlace de datos real, es solo un panel. Estoy enumerando la forma más fácil de lograr la visualización de una lista bidimensional

                  

Y en el código detrás de set el ItemsSource de lst con una estructura de datos TwoDimentional.

  public Window1() { List> lsts = new List>(); for (int i = 0; i < 5; i++) { lsts.Add(new List()); for (int j = 0; j < 5; j++) { lsts[i].Add(i * 10 + j); } } InitializeComponent(); lst.ItemsSource = lsts; } 

Esto le da la siguiente pantalla como salida. Puede editar DataTemplate_Level2 para agregar datos más específicos de su objeto.

texto alternativo

Aquí hay un Control llamado DataGrid2D que puede ser poblado basado en un 2D o
Matriz 1D (o cualquier cosa que implemente la interfaz IList ). ItemsSource2D DataGrid y agrega una propiedad llamada ItemsSource2D que se utiliza para enlazar contra fonts 2D o 1D. La biblioteca se puede descargar aquí y el código fuente se puede descargar aquí .

Para usarlo solo agregue una referencia a DataGrid2DLibrary.dll, agregue este espacio de nombres

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

y luego crea un DataGrid2D y vincúlalo a tu IList, matriz 2D o matriz 1D como este

  

enter image description here


ANTIGUO POST
Aquí hay una implementación que puede vincular una matriz 2D a la cuadrícula de datos WPF.

Digamos que tenemos esta matriz 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); } } 

Y luego queremos vincular esta matriz 2D a WGD DataGrid y los cambios que hagamos se reflejarán en la matriz. Para hacer esto utilicé la clase Ref de Eric Lippert de este hilo.

 public class Ref { private readonly Func getter; private readonly Action setter; public Ref(Func getter, Action setter) { this.getter = getter; this.setter = setter; } public T Value { get { return getter(); } set { setter(value); } } } 

Luego hice una clase de ayudante estático con un método que podría tomar una matriz 2D y devolver una vista de datos usando la clase Ref de arriba.

 public static DataView GetBindable2DArray(T[,] array) { DataTable dataTable = new DataTable(); for (int i = 0; i < array.GetLength(1); i++) { dataTable.Columns.Add(i.ToString(), typeof(Ref)); } 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 refT = new Ref(() => array[a, b], z => { array[a, b] = z; }); dataView[i][j] = refT; } } return dataView; } 

Esto sería casi suficiente para enlazar, pero la ruta en el enlace señalará el objeto Ref en lugar del Ref.Value que necesitamos, así que tenemos que cambiar esto cuando se generen las columnas.

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

Y después de esto podemos usar

 c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray(m_intArray); 

Y la salida se verá así

texto alternativo

Cualquier cambio realizado en DataGrid se reflejará en m_intArray.

Escribí una pequeña biblioteca de propiedades adjuntas para DataGrid . Aquí está la fuente

Muestra, donde Data2D es int[,] :

  

Renders: enter image description here

Es posible que desee consultar este enlace: http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

Si usa una Lista dentro de una Lista, puede usar myList [x] [y] para acceder a una celda.

Aquí hay otra solución basada en la respuesta de Meleak pero sin requerir un manejador de eventos AutoGeneratingColumn en el código detrás de cada DataGrid enlazado:

 public static DataView GetBindable2DArray(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; }