55 votes

Comment peupler une grille WPF basée sur un tableau à 2 dimensions

J'ai un tableau à deux dimensions d'objets et je veux essentiellement lier chaque objet à une cellule dans une grille WPF. Actuellement, j'ai réussi à le faire, mais je le fais principalement de manière procédurale. Je crée le bon nombre de définitions de ligne et de colonne, puis je parcours les cellules et je crée les contrôles et je configure les liaisons correctes pour chacun.

À tout le moins, je voudrais pouvoir utiliser un modèle pour spécifier les contrôles et les liaisons en xaml. Idéalement, je voudrais me débarrasser du code procédural et tout faire avec la liaison de données, mais je ne suis pas sûr que cela 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);
        }
    }

}

66voto

Jobi Joy Points 20883

Le but de la Grid n'est pas le véritable databinding, c'est juste un panneau. Voici la façon la plus simple de visualiser une liste bidimensionnelle

Et dans le code derrière, définissez l'ItemsSource de lst avec une structure de données bidimensionnelle.

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

Cela 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

43voto

Fredrik Hedblad Points 42772

Voici un Control appelé DataGrid2D qui peut être peuplé en fonction d'un tableau 2D ou 1D (ou de toute chose qui implémente l'interface IList). Il hérite de DataGrid et ajoute une propriété appelée ItemsSource2D qui est utilisée pour lier 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, tableau 2D ou 1D comme ceci

entrez la description de l'image ici


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

Supposons que nous ayons 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);
    }
}

Et ensuite nous voulons lier ce tableau 2D à la grille de données WPF et les modifications que nous apportons doivent se refléter dans le tableau. Pour ce faire, j'ai utilisé la classe Ref d'Eric Lippert de ceci fil de discussion.

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

Ensuite, j'ai créé une classe d'aide statique avec une méthode qui pouvait prendre un tableau 2D et renvoyer un DataView en utilisant la classe Ref ci-dessus.

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

Cela serait presque suffisant pour lier mais le chemin dans la liaison pointera vers l'objet Ref au lieu de Ref.Value dont nous avons besoin donc nous devons changer ceci lorsque les colonnes sont générées.

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

Et la sortie ressemblera à ceci

texte alternatif

Toutes les modifications apportées dans la DataGrid seront reflétées dans le m_intArray.

5voto

Johan Larsson Points 4405

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

Exemple, où Data2D est int[,]:

Rendu: Description de l'image

0voto

Torsten Points 481

Vous voudrez peut-être consulter ce lien : http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

Si vous utilisez une List à l'intérieur d'une autre List, vous pouvez utiliser myList[x][y] pour accéder à une cellule.

0voto

CitizenInsane Points 1139

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

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); // Enregistrer l'index de colonne d'origine
    }
    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];
        }
    }

    // Évite d'écrire un gestionnaire d'événements 'AutogeneratingColumn'
    table.ColumnChanged += (s, e) => 
    {
        var ci = (int)e.Column.ExtendedProperties["idx"]; // Récupérer l'index de colonne d'origine
        var ri = e.Row.Table.Rows.IndexOf(e.Row); // Récupérer l'index de ligne

        array[ri, ci] = (T)view[ri][ci];
    };

    return view;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X