Pregunta

Tengo una matriz bidimensional de objetos y básicamente quiero vincular cada uno de ellos a una celda en una cuadrícula WPF. Actualmente tengo esto funcionando pero lo estoy haciendo la mayor parte del procedimiento. Creo el número correcto de definiciones de fila y columna, luego recorro las celdas y creo los controles y configuro los enlaces correctos para cada uno.

Como mínimo, me gustaría poder usar una plantilla para especificar los controles y enlaces en xaml. Idealmente, me gustaría deshacerme del código de procedimiento y hacerlo todo con el enlace de datos, pero no estoy seguro de que sea posible.

Aquí está el código que estoy usando actualmente:

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

}
¿Fue útil?

Solución

El propósito de Grid no es el enlace de datos real, es solo un panel. Estoy enumerando la forma más fácil de lograr la visualización de una lista bidimensional

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

Y en el código subyacente establezca ItemsSource de lst con una estructura de datos 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;
    }

Esto le da la siguiente pantalla como salida. Puede editar DataTemplate_Level2 para agregar datos más específicos de su objeto.

texto alternativo ??

Otros consejos

Aquí hay un control llamado DataGrid2D que puede rellenarse en base a 2D o
Matriz 1D (o cualquier cosa que implemente la interfaz IList ). Subclasifica DataGrid y agrega una propiedad llamada ItemsSource2D que se usa para vincular contra fuentes 2D o 1D. La biblioteca se puede descargar aquí y el código fuente se puede descargar aquí .

Para usarlo simplemente agregue una referencia a DataGrid2DLibrary.dll, agregue este espacio de nombres

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

y luego cree un DataGrid2D y vincúlelo a su IList, matriz 2D o matriz 1D como esta

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

ingrese la descripción de la imagen aquí


ANTIGUA PUBLICACIÓN
Aquí hay una implementación que puede unir una matriz 2D a la cuadrícula de datos WPF.

Digamos que tenemos esta matriz 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);
    }
}

Y luego queremos vincular esta matriz 2D al WPF DataGrid y los cambios que realicemos se reflejarán en la matriz. Para hacer esto, utilicé la clase Ref de Eric Lippert de este hilo.

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

Luego hice una clase auxiliar estática con un método que podría tomar una matriz 2D y devolver un DataView usando la clase Ref anterior.

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

Esto sería casi suficiente para enlazar, pero la ruta en el enlace apuntará al objeto Ref en lugar del valor Ref. que necesitamos, por lo que tenemos que cambiar esto cuando se generen las columnas.

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

Y después de esto podemos usar

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

Y la salida se verá así

texto alternativo ??

Cualquier cambio realizado en DataGrid se reflejará en m_intArray.

Escribí una pequeña biblioteca de propiedades adjuntas para DataGrid . Aquí está la fuente

Muestra, donde Data2D es int [,] :

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

Renders: ingrese la descripción de la imagen aquí

Puede consultar este enlace: http: //www.thinkbottomup. com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

Si usa una Lista dentro de una Lista, puede usar myList [x] [y] para acceder a una celda.

Aquí hay otra solución basada en la respuesta de Meleak pero sin requerir una AutoGeneratingColumn controlador de eventos en el código detrás de cada DataGrid :

enlazado
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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top