将 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 明智。
相反,由于您有一个 List
个 List
个,您可以用一个 ItemsControl
个 ItemsControl
个来做同样的事情。只要每个元素具有相同的 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}
};
}
这样做的好处是不需要隐藏代码或以编程方式生成绑定。
我在代码隐藏中将 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 明智。
相反,由于您有一个 List
个 List
个,您可以用一个 ItemsControl
个 ItemsControl
个来做同样的事情。只要每个元素具有相同的 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}
};
}
这样做的好处是不需要隐藏代码或以编程方式生成绑定。