Ich habe ein 2-dimensionales Array von Objekten und möchte im Grunde jedes zu einer Zelle in einem WPF-Gitter databind. Zur Zeit habe ich diese Arbeit, aber ich mache das meiste prozessual. Ich erstelle die richtige Anzahl von Zeilen- und Spaltendefinitionen, dann durchlaufe ich die Zellen, erstelle die Steuerelemente und richte die richtigen Bindungen für jede ein.
Zumindest möchte ich in der Lage sein, eine Vorlage zu verwenden, um die Steuerelemente und Bindungen in xaml anzugeben. Im Idealfall möchte ich den Verfahrenscode loswerden und einfach alles mit Datenbindung erledigen, aber ich bin mir nicht sicher, ob das möglich ist.
Hier ist der Code, den ich momentan verwende:
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);
}
}
}
Der Zweck des Grids ist nicht für echtes Datenbinden, es ist nur ein Panel. Ich liste den einfachsten Weg auf, um eine zweidimensionale Liste zu visualisieren
<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>
Und im Code dahinter setzen Sie die ItemsSource von lst mit einer TwoDimentional-Datenstruktur.
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;
}
Dadurch erhalten Sie den folgenden Bildschirm als Ausgabe. Sie können DataTemplate_Level2 bearbeiten, um spezifischere Daten Ihres Objekts hinzuzufügen.
Hier ist ein Steuerelement mit dem Namen DataGrid2D
, das basierend auf einem 2D-Element oder gefüllt werden kann
1D-Array (oder etwas, das die IList
-Schnittstelle implementiert). Die Unterklassen sind DataGrid
und fügen eine Eigenschaft namens ItemsSource2D
hinzu, die für die Bindung von 2D- oder 1D-Quellen verwendet wird. Bibliothek kann heruntergeladen werden hier und Quellcode kann heruntergeladen werden hier .
Um es zu verwenden, fügen Sie einfach einen Verweis auf DataGrid2DLibrary.dll hinzu, und fügen Sie diesen Namespace hinzu
xmlns:dg2d="clr-namespace:DataGrid2DLibrary;Assembly=DataGrid2DLibrary"
erstellen Sie dann ein DataGrid2D und binden Sie es wie folgt an Ihr IList-, 2D-Array oder 1D-Array
<dg2d:DataGrid2D Name="dataGrid2D"
ItemsSource2D="{Binding Int2DList}"/>
OLD POST
Hier ist eine Implementierung, die ein 2D-Array an das WPF-Datenraster binden kann.
Angenommen, wir haben dieses 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);
}
}
Und dann möchten wir dieses 2D-Array an das WPF-DataGrid binden, und die von uns vorgenommenen Änderungen werden im Array wiedergegeben. Dazu habe ich die Ref-Klasse von Eric Lippert aus this thread verwendet.
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); } }
}
Dann erstellte ich eine statische Hilfsklasse mit einer Methode, die ein 2D-Array verwenden und ein DataView mit der obigen Ref-Klasse zurückgeben konnte.
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;
}
Dies wäre fast zum Binden ausreichend, aber der Pfad in der Bindung verweist auf das Ref-Objekt statt auf den Ref.Value-Wert, den wir benötigen. Daher müssen wir dies ändern, wenn die Spalten generiert werden.
<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");
}
Und danach können wir verwenden
c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);
Und die Ausgabe wird so aussehen
Alle Änderungen, die in der Variablen DataGrid
vorgenommen wurden, werden im m_intArray wiedergegeben.
Für DataGrid
habe ich eine kleine Bibliothek mit angefügten Eigenschaften geschrieben. Hier ist die Quelle
Beispiel, wo Data2D int[,]
ist:
<DataGrid HeadersVisibility="None"
dataGrid2D:Source2D.ItemsSource2D="{Binding Data2D}" />
Rendert:
Sie können diesen Link überprüfen: http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python
Wenn Sie eine Liste in einer Liste verwenden, können Sie mit myList [x] [y] auf eine Zelle zugreifen.
Hier ist eine andere Lösung, die auf der Antwort von Meleak basiert, jedoch ohne einen AutoGeneratingColumn
-Ereignishandler im Code hinter jeder gebundenen 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;
}