简体   繁体   English

如何将列表加载到 ComboBox DataGrid 并显示来自另一个列表的选定值?

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

Goal目标

I am aiming to achieve the following:我的目标是实现以下目标:

  • Load Person List to DataGrid ✔️将人员列表加载到 DataGrid ✔️
  • Load Positions List to DataGrid column into a ComboBox ✔️将位置列表加载到 DataGrid 列到 ComboBox ✔️
  • Set the Person's Position value to Position's ComboBox ❌将 Person 的 Position 值设置为 Position 的 ComboBox ❌

Visual Output视觉Output

在此处输入图像描述

Code...代码...

Models楷模

I have 2 models我有 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; }
}

View Model查看 Model

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

View看法

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

Question问题

How do I set the SelectedItem/Index of Position to what the Person's Position is set to?如何将 Position 的 SelectedItem/Index 设置为 Person 的 Position 设置的值?

You could override the Equals method of your Position class to define that two objects with the same id should be considered equal:您可以覆盖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();
}
            <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不行。。不行

Full answer, for all inconsistencies in the code:完整答案,对于代码中的所有不一致:

  1. If the type is used for binding, then for correct operation it must either be immutable or implement the INotifyPropertyChanged interface:如果该类型用于绑定,那么为了正确操作,它必须是不可变的或实现 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. If you need to ensure equality of instances by value, then you need to override the Equals and GetHashCode methods (as @mm8 already wrote).如果您需要按值确保实例的相等性,那么您需要覆盖 Equals 和 GetHashCode 方法(正如@mm8 已经写的那样)。 But for mutable types, this is a bad decision, which in some cases can lead to bugs.但是对于可变类型,这是一个糟糕的决定,在某些情况下可能会导致错误。

  2. If you need to set the values of a reference type corresponding to some collection, then you do not need to re-create instances of this type, but assign one of the elements of the collection that already contains all the valid values.如果需要设置某个集合对应的引用类型的值,则不需要重新创建该类型的实例,而是分配集合中已经包含所有有效值的元素之一。

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. Complete XAML code example demonstrating correct operation.完整的 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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