wake-up-neo.net

So füllen Sie ein WPF-Gitter basierend auf einem 2-dimensionalen Array

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

}
55
Daniel Plaisted

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.

alt text

65
Jobi Joy

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}"/>

enter image description here


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

alt text

Alle Änderungen, die in der Variablen DataGrid vorgenommen wurden, werden im m_intArray wiedergegeben.

42
Fredrik Hedblad

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

3
Johan Larsson

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.

0
Torsten

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