简体   繁体   中英

Binding List<List<object>> to DataGrid columns in code-behind

I have a problem binding List<List> to DataGrid columns in code-behind. Here is my list of lists:

private List<List<object>> matrix = new List<List<object>>()
     {
         new List<object>(){53, 2500, 3000, 3500, 4000, 4500, 5000},
         new List<object>(){2500, 1, 0, 0, 0, 0, 0},
         new List<object>(){3000, 0, 1, 0, 0, 0, 0},
         new List<object>(){3500, 0, 0, 1, 0, 0, 0},
         new List<object>(){4000, 0, 0, 0},
         new List<object>(){4500, 0, 0, 0, 0, 1, 0},
         new List<object>(){5000, 9, 7, 5, 4, 1, 1},
         new List<object>(){1, 0, 0, 0, 0, 0, 0},
         new List<object>(){2, 0, 0, 0, 0, 0, 0},
         new List<object>(){0, 0, 0, 0, 0, 0, 0}
     };

    public List<List<object>> Matrix
    {
        get { return matrix; }
        set
        {
            matrix = value;
            OnPropertyChanged(nameof(Matrix));
        }

I've done this in XAML like this:

<DataGrid ItemsSource="{Binding Matrix}" VerticalAlignment="Top" FontSize="14" x:Name="matrixDataGrid" Height="420" Width="630" MinRowHeight="20" AutoGenerateColumns="False" >
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=[0], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[1], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[2], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[3], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[4], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[5], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[6], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[7], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[8], Mode=TwoWay}"/>
            <DataGridTextColumn Binding="{Binding Path=[9], Mode=TwoWay}"/>
        </DataGrid.Columns>
    </DataGrid>

And here is the result: the result of XAML binding

And I would call it half-working. The first problem is an extra row and 3 extra columns. The second one is that I will have a lot of work if I would like to have 1000 columns. Also, there are a lot of XAML binding errors: Binding errors . And I don't even know how this specific binding works, and it looks strange. I would be glad for the explanation.

My unsuccessful attempts to do this in code-behind look like this:

Binding binding = new Binding();
binding.Source = Matrix; 
binding.Path = new PropertyPath("[0]"); 
binding.Mode = BindingMode.TwoWay;
columnOne.Binding = binding;

XAML file at the moment:

<DataGrid ItemsSource="{Binding Matrix}" VerticalAlignment="Top" FontSize="14" x:Name="matrixDataGrid" Height="420" Width="630" MinRowHeight="20" AutoGenerateColumns="False" >
        <DataGrid.Columns>
            <DataGridTextColumn Width="50" x:Name="columnOne" />
        </DataGrid.Columns>
    </DataGrid>

I only get empty column: result of attempt . I've seen someone doing this with DataTable, but I would like to do this the way I want if it is possible.

I wouldn't mess with a DataGrid , it's just too much hassle unless you need specific features of the DataGrid like sortable headers, grouping, etc. DataGrid also will not handle a huge matrix very well, since each cell is pretty heavy UI wise.

Instead, Since you have a List of List s, you can do the same thing with an ItemsControl of ItemsControl s. As long as each element has the same height/width, then it would come out looking just like the DataGrid . You could do that by fixing the size of each element, or set the ItemsPanel to something that enforces consistent sizing (like UniformGrid ).

To make binding work so the cells are editable, you need to wrap the value in each matrix cell in something that implements INotifyPropertyChanged.

public class ItemViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string p = null) 
        => PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(p));
      
    private int _value;
    public int Value
    {
        get => _value;
        set
        {
           _value = value;
           OnPropertyChanged();
        }
    }

    public static implicit operator int(Item i) => i.Value;
    public static implicit operator Item(int i) => new () { Value = i};

    public static int[] GetArray(IEnumerable<Item> input) 
        => input.Select(i => i.Value).ToArray();

    public static int[][] GetMatrix(IEnumerable<IEnumerable<Item>> input) 
        => input.Select(ToArray).ToArray();
}

Here's the XAML for a MatrixGrid, derived from ItemsControl. The code behind doesn't have anything in it but the autogenerated constructor.

<ItemsControl x:Class="WpfApp1.MatrixGrid" x:Name="ic"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              ItemsSource="{Binding}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl ItemsSource="{Binding}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="{Binding ElementName=ic, Path=ItemsSource.Count}"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
                                 TextAlignment="Center"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Demo:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp1">
    <local:MatrixGrid x:Name="matrixGrid"/>
</Window>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        matrixGrid.ItemsSource = Matrix;
    }

    public List<List<ItemViewModel>> Matrix { get; } = new List<List<Item>>()
    {
        new (){53, 2500, 3000, 3500, 4000, 4500, 5000},
        new (){2500, 1, 0, 0, 0, 0, 0},
        new (){3000, 0, 1, 0, 0, 0, 0},
        new (){3500, 0, 0, 1, 0, 0, 0},
        new (){4000, 0, 0, 0},
        new (){4500, 0, 0, 0, 0, 1, 0},
        new (){5000, 9, 7, 5, 4, 1, 1},
        new (){1, 0, 0, 0, 0, 0, 0},
        new (){2, 0, 0, 0, 0, 0, 0},
        new (){0, 0, 0, 0, 0, 0, 0}
    };
}

在此处输入图像描述

Nice thing about this is that there's no need for code behind or generating bindings programmatically.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM