Question

J'ai un tableau d'objets à 2 dimensions et je souhaite fondamentalement associer des données à chacun d'eux dans une cellule d'une grille WPF. Actuellement, cela fonctionne, mais je le fais principalement pour des raisons de procédure. Je crée le nombre correct de définitions de lignes et de colonnes, puis je parcoure les cellules, crée les contrôles et configure les liaisons correctes pour chacune d'entre elles.

Au minimum, j'aimerais pouvoir utiliser un modèle pour spécifier les contrôles et les liaisons dans xaml. Idéalement, j'aimerais supprimer le code de procédure et tout faire avec la liaison de données, mais je ne suis pas sûr que ce soit possible.

Voici le code que j'utilise actuellement:

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

}
Était-ce utile?

La solution

L’objet de la grille n’est pas de véritables liaisons de données, c’est juste un panneau. Je liste la manière la plus simple de visualiser une liste en deux dimensions

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

Et dans le code suivant, définissez ItemsSource of lst avec une structure de données TwoDimentional.

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

Ceci vous donne l’écran suivant en sortie. Vous pouvez modifier le DataTemplate_Level2 pour ajouter des données plus spécifiques de votre objet.

alt text

Autres conseils

Voici un contrôle appelé DataGrid2D qui peut être rempli sur la base d'un 2D ou d'un
Tableau 1D (ou tout élément implémentant l'interface IList ). Il sous-classe DataGrid et ajoute une propriété appelée ItemsSource2D , utilisée pour la liaison avec des sources 2D ou 1D. La bibliothèque peut être téléchargée ici et le code source peut être téléchargé ici .

Pour l'utiliser, ajoutez simplement une référence à DataGrid2DLibrary.dll, ajoutez cet espace de noms

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

puis créez un DataGrid2D et liez-le à votre IList, votre tableau 2D ou votre tableau 1D comme ceci

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

entrer la description de l'image ici

OLD POST
Voici une implémentation qui peut lier un tableau 2D à la grille de données WPF.

Disons que nous avons ce tableau 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);
    }
}

Ensuite, nous voulons lier ce tableau 2D au WPF DataGrid et les modifications apportées doivent être reflétées dans le tableau. Pour ce faire, j'ai utilisé la classe de référence d'Eric Lippert dans ce sujet.

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

J'ai ensuite créé une classe d'assistance statique avec une méthode pouvant prendre un tableau 2D et renvoyer un DataView à l'aide de la classe Ref ci-dessus.

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

Cela serait presque suffisant pour se lier à, mais le chemin dans la liaison pointera vers l'objet Ref au lieu de la valeur de référence dont nous avons besoin. Nous devons donc changer cela lorsque les colonnes sont générées.

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

Et après cela, nous pouvons utiliser

c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);

Et la sortie ressemblera à ceci

alt text

Toute modification apportée dans DataGrid sera reflétée dans le m_intArray.

J'ai écrit une petite bibliothèque de propriétés attachées pour DataGrid . Voici la source

Exemple, où Data2D est int [,] :

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

Renders: entrer la description de l'image ici

Vous pouvez consulter ce lien: http: //www.thinkbomup. com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

Si vous utilisez une liste dans une liste, vous pouvez utiliser myList [x] [y] pour accéder à une cellule.

Voici une autre solution basée sur la réponse de Meleak mais sans nécessiter de AutoGeneratingColumn gestionnaire d'événements dans le code derrière chaque DataGrid lié:

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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top