将 List<List<object>> 绑定到代码隐藏中的 DataGrid 列

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

我在代码隐藏中将 List 绑定到 DataGrid 列时遇到问题。 这是我的清单:

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

我在 XAML 中这样做过:

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

结果如下: the result of XAML binding

我称之为半工作。第一个问题是一个额外的行和 3 个额外的列。第二个是如果我想要 1000 列,我将有很多工作要做。此外,还有很多 XAML 绑定错误:Binding errors。而且我什至不知道这个特定的绑定是如何工作的,看起来很奇怪。我很乐意得到解释。

我在代码隐藏中执行此操作的失败尝试如下所示:

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

XAML 当前文件:

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

我只得到空列:result of attempt。 我见过有人用 DataTable 这样做,但如果可能的话,我想按照我想要的方式来做。

我不会弄乱 DataGrid,这太麻烦了,除非您需要 DataGrid 的特定功能,例如可排序 headers、分组等。DataGrid 也不能很好地处理巨大的矩阵,因为每个单元格都非常重 UI 明智。

相反,由于您有一个 ListList 个,您可以用一个 ItemsControlItemsControl 个来做同样的事情。只要每个元素具有相同的 height/width,那么它看起来就像 DataGrid。您可以通过固定每个元素的大小来做到这一点,或者将 ItemsPanel 设置为强制一致的大小(如 UniformGrid)。

要使绑定起作用以便单元格可编辑,您需要将每个矩阵单元格中的值包装在实现 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();
}

这是从 ItemsControl 派生的 MatrixGrid 的 XAML。除了自动生成的构造函数之外,后面的代码没有任何内容。

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

演示:

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

这样做的好处是不需要隐藏代码或以编程方式生成绑定。