如何将列表加载到 ComboBox DataGrid 中并显示从另一个列表中选择的值?

How to load a list into a ComboBox DataGrid and display selected value from another list?

目标

我的目标是实现以下目标:

视觉输出

代码...

型号

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

对于代码中所有不一致的完整答案:

  1. 如果类型用于绑定,那么为了正确操作,它必须是不可变的或实现 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); }
    }
}
  1. 如果您需要通过值确保实例相等,那么您需要覆盖 Equals 和 GetHashCode 方法(@mm8 已经写过)。 但对于可变类型,这是一个错误的决定,在某些情况下会导致错误。

  2. 如果您需要设置某个集合对应的引用类型的值,那么您不需要重新创建该类型的实例,而是将集合中的一个元素赋值已经包含所有有效值。

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

    }
}
  1. 完成 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();
}