Как заполнить сетку WPF на основе двумерного массива

У меня есть 2-мерный массив объектов, и я в основном хочу привязать каждую привязку к ячейке в сетке 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); } } } 

Назначение Grid не для реальной привязки данных, это просто панель. Я перечисляю самый простой способ выполнить визуализацию двумерного списка

                  

И в коде, установленном для набора ItemsSource lst с двухмерными структурами данных.

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

Это дает вам следующий экран в качестве вывода. Вы можете редактировать DataTemplate_Level2 для добавления более конкретных данных вашего объекта.

alt text

Вот элемент управления под названием DataGrid2D который может быть заполнен на основе 2D или
1D (или все, что реализует интерфейс IList ). Он подclassифицирует DataGrid и добавляет свойство, называемое ItemsSource2D которое используется для привязки к 2D или 1D источникам. Библиотека можно скачать здесь, и здесь можно скачать исходный код.

Чтобы использовать его, просто добавьте ссылку на DataGrid2DLibrary.dll, добавьте это пространство имен

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

а затем создайте DataGrid2D и привяжите его к вашему массиву IList, 2D или 1D, как это

  

введите описание изображения здесь


СТАРЫЙ ПОЧТА
Вот реализация, которая может привязать 2D-массив к datagrid 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, и изменения, которые мы делаем, должны отражаться в массиве. Для этого я использовал class Ref Eric Lippert's из этой темы.

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

Затем я сделал статический вспомогательный class с методом, который мог бы взять 2D-массив и вернуть DataView с использованием classа Ref выше.

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

Этого было бы достаточно для привязки, но Path in Binding укажет на объект Ref вместо Ref.Value, который нам нужен, поэтому мы должны изменить это, когда генерируются столбцы.

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

И результат будет выглядеть следующим образом:

alt text

Любые изменения, внесенные в DataGrid будут отражены в m_intArray.

Я написал небольшую библиотеку прикрепленных свойств для DataGrid . Вот источник

Пример, где Data2D – int[,] :

  

Оказывает: введите описание изображения здесь

Вы можете проверить эту ссылку: http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

Если вы используете список в списке, вы можете использовать myList [x] [y] для доступа к ячейке.

Вот еще одно решение, основанное на ответе Meleak , но не требующее обработчика событий AutoGeneratingColumn в коде, AutoGeneratingColumn за каждым связанным DataGrid :

 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; } 
  • Как конкатенировать строки в шаблонах django?
  • Исходное и наследование в Android
  • Какие шаблоны проектирования могут быть применены к проблеме настроек конфигурации?
  • Неявное преобразование типа C ++ с шаблоном
  • Предложения по шаблонам электронной почты Java?
  • Полиморфизм шаблонов C ++
  • WPF - Как создать кнопку изображения с шаблоном
  • Специализация шаблона на основе classа inherit
  • Может кто-нибудь объяснить этот код шаблона, который дает мне размер массива?
  • Шаблонная специализация отдельных участников?
  • Можно ли использовать Razor View Engine вне asp.net
  • Interesting Posts
    Давайте будем гением компьютера.