如何将列表加载到 ComboBox DataGrid 中并显示从另一个列表中选择的值?
How to load a list into a ComboBox DataGrid and display selected value from another list?
目标
我的目标是实现以下目标:
- 将人员列表加载到 DataGrid ✔️
- 将位置列表加载到 DataGrid 列到 ComboBox ✔️
- 将Person的Position值设置为Position的ComboBox ❌
视觉输出
代码...
型号
我有2个模型
public class Person
{
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
}
查看模型
public class ViewModel : BaseViewModel
{
public ViewModel()
{
People = new ObservableCollection<Person>();
People.Add(new Person { Name = "Name 1", Position = new Position { PositionId = 1, PositionTitle = "Position Title 1" } });
People.Add(new Person { Name = "Name 2", Position = new Position { PositionId = 1, PositionTitle = "Position Title 1" } });
People.Add(new Person { Name = "Name 3", Position = new Position { PositionId = 2, PositionTitle = "Position Title 2" } });
Positions = new ObservableCollection<Position>();
Positions.Add(new Position { PositionId = 1, PositionTitle = "Position Title 1" });
Positions.Add(new Position { PositionId = 2, PositionTitle = "Position Title 2" });
}
private ObservableCollection<Person> people;
public ObservableCollection<Person> People
{
get { return people; }
set
{
people = value;
OnPropertyChanged();
}
}
private ObservableCollection<Position> _positions;
public ObservableCollection<Position> Positions
{
get { return _positions; }
set
{
_positions = value;
OnPropertyChanged();
}
}
}
public class Person
{
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
}
查看
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions, RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedValue="{Binding Path=Position}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
问题
如何将 Position 的 SelectedItem/Index 设置为 Person 的 Position 设置的值?
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedItem="{Binding Path=Position,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}" />
</DataTemplate>
Nope.. doesn't work
对于代码中所有不一致的完整答案:
- 如果类型用于绑定,那么为了正确操作,它必须是不可变的或实现 INotifyPropertyChanged 接口:
namespace PeoplePosition
{
public class Position
{
public int PositionId { get; }
public string PositionTitle { get; }
public Position(int positionId, string positionTitle)
{
PositionId = positionId;
PositionTitle = positionTitle;
}
}
}
using Simplified;
namespace PeoplePosition
{
public class Person : BaseInpc // Implementation of the "INPC" interface
{
private string _name;
private Position _position;
public string Name { get => _name; set => Set(ref _name, value); }
public Position Position { get => _position; set => Set(ref _position, value); }
}
}
如果您需要通过值确保实例相等,那么您需要覆盖 Equals 和 GetHashCode 方法(@mm8 已经写过)。
但对于可变类型,这是一个错误的决定,在某些情况下会导致错误。
如果您需要设置某个集合对应的引用类型的值,那么您不需要重新创建该类型的实例,而是将集合中的一个元素赋值已经包含所有有效值。
using System.Collections.ObjectModel;
namespace PeoplePosition
{
public class ViewModel
{
public ViewModel()
{
Positions.Add(new Position(1, "Position Title 1"));
Positions.Add(new Position(2, "Position Title 2"));
People.Add(new Person { Name = "Name 1", Position = Positions[0] });
People.Add(new Person { Name = "Name 2", Position = Positions[0] });
People.Add(new Person { Name = "Name 3", Position = Positions[1] });
}
public ObservableCollection<Person> People { get; }
= new ObservableCollection<Person>();
public ObservableCollection<Position> Positions { get; }
= new ObservableCollection<Position>();
}
}
- 完成 XAML 演示正确操作的代码示例。
<Window x:Class="PeoplePosition.PeoplePositionsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PeoplePosition"
mc:Ignorable="d"
Title="PeoplePositionsWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<UniformGrid Columns="1">
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" IsReadOnly="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedItem="{Binding Path=Position,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Binding="{Binding Position.PositionId, Mode=OneWay}"
Header="PositionId"/>
<DataGridTextColumn Binding="{Binding Position.PositionTitle, Mode=OneWay}"
Header="Position Title"/>
</DataGrid.Columns>
</DataGrid>
</UniformGrid>
</Window>
您可以覆盖 Position
class 的 Equals
方法来定义具有相同 ID 的两个对象应被视为相等:
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
public override bool Equals(object obj) =>
obj is Position p && PositionId == p.PositionId;
public override int GetHashCode() => PositionId.GetHashCode();
}
目标
我的目标是实现以下目标:
- 将人员列表加载到 DataGrid ✔️
- 将位置列表加载到 DataGrid 列到 ComboBox ✔️
- 将Person的Position值设置为Position的ComboBox ❌
视觉输出
代码...
型号
我有2个模型
public class Person
{
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
}
查看模型
public class ViewModel : BaseViewModel
{
public ViewModel()
{
People = new ObservableCollection<Person>();
People.Add(new Person { Name = "Name 1", Position = new Position { PositionId = 1, PositionTitle = "Position Title 1" } });
People.Add(new Person { Name = "Name 2", Position = new Position { PositionId = 1, PositionTitle = "Position Title 1" } });
People.Add(new Person { Name = "Name 3", Position = new Position { PositionId = 2, PositionTitle = "Position Title 2" } });
Positions = new ObservableCollection<Position>();
Positions.Add(new Position { PositionId = 1, PositionTitle = "Position Title 1" });
Positions.Add(new Position { PositionId = 2, PositionTitle = "Position Title 2" });
}
private ObservableCollection<Person> people;
public ObservableCollection<Person> People
{
get { return people; }
set
{
people = value;
OnPropertyChanged();
}
}
private ObservableCollection<Position> _positions;
public ObservableCollection<Position> Positions
{
get { return _positions; }
set
{
_positions = value;
OnPropertyChanged();
}
}
}
public class Person
{
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
}
查看
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions, RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedValue="{Binding Path=Position}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
问题
如何将 Position 的 SelectedItem/Index 设置为 Person 的 Position 设置的值?
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedItem="{Binding Path=Position,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}" />
</DataTemplate>
Nope.. doesn't work
对于代码中所有不一致的完整答案:
- 如果类型用于绑定,那么为了正确操作,它必须是不可变的或实现 INotifyPropertyChanged 接口:
namespace PeoplePosition
{
public class Position
{
public int PositionId { get; }
public string PositionTitle { get; }
public Position(int positionId, string positionTitle)
{
PositionId = positionId;
PositionTitle = positionTitle;
}
}
}
using Simplified;
namespace PeoplePosition
{
public class Person : BaseInpc // Implementation of the "INPC" interface
{
private string _name;
private Position _position;
public string Name { get => _name; set => Set(ref _name, value); }
public Position Position { get => _position; set => Set(ref _position, value); }
}
}
如果您需要通过值确保实例相等,那么您需要覆盖 Equals 和 GetHashCode 方法(@mm8 已经写过)。 但对于可变类型,这是一个错误的决定,在某些情况下会导致错误。
如果您需要设置某个集合对应的引用类型的值,那么您不需要重新创建该类型的实例,而是将集合中的一个元素赋值已经包含所有有效值。
using System.Collections.ObjectModel;
namespace PeoplePosition
{
public class ViewModel
{
public ViewModel()
{
Positions.Add(new Position(1, "Position Title 1"));
Positions.Add(new Position(2, "Position Title 2"));
People.Add(new Person { Name = "Name 1", Position = Positions[0] });
People.Add(new Person { Name = "Name 2", Position = Positions[0] });
People.Add(new Person { Name = "Name 3", Position = Positions[1] });
}
public ObservableCollection<Person> People { get; }
= new ObservableCollection<Person>();
public ObservableCollection<Position> Positions { get; }
= new ObservableCollection<Position>();
}
}
- 完成 XAML 演示正确操作的代码示例。
<Window x:Class="PeoplePosition.PeoplePositionsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PeoplePosition"
mc:Ignorable="d"
Title="PeoplePositionsWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<UniformGrid Columns="1">
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" IsReadOnly="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedItem="{Binding Path=Position,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Binding="{Binding Position.PositionId, Mode=OneWay}"
Header="PositionId"/>
<DataGridTextColumn Binding="{Binding Position.PositionTitle, Mode=OneWay}"
Header="Position Title"/>
</DataGrid.Columns>
</DataGrid>
</UniformGrid>
</Window>
您可以覆盖 Position
class 的 Equals
方法来定义具有相同 ID 的两个对象应被视为相等:
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
public override bool Equals(object obj) =>
obj is Position p && PositionId == p.PositionId;
public override int GetHashCode() => PositionId.GetHashCode();
}