如何将 WPF DataGridColumn 设为绑定的可编辑组合框,其中对项目的更改将传播到其他行?

问题描述 投票:0回答:1

我正在使用绑定到集合的 WPF DataGrid。其中一列将是一个组合框。我希望 ComboBox 是可编辑的,并且对文本的任何更改都将传播到集合中之前使用相同值的所有其他项目。

这是我的 XAML:

<Window x:Class="WpfApp1.MainWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Students}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" />
            <DataGridTemplateColumn Header="Team Name">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox IsEditable="True" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=TeamNames}" Text="{Binding TeamName}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

这是我的代码:

public class Student : INotifyPropertyChanged
{
    private string teamName;

    public string Name { get; set; }
    public string TeamName
    {
        get { return teamName; }
        set
        {
            teamName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TeamName"));
        }
    }

    public Student(string name, string teamName)
    {
        Name = name;
        this.teamName = teamName;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public partial class MainWindow : Window
{
    public ObservableCollection<Student> Students { get; set; } = new ObservableCollection<Student>(new Student[] { new Student("Alice", "Red"), new Student("Bob", "Blue"), new Student("Craig", "Blue") });
    public List<string> TeamNames { get; set; } = new List<string>();

    public MainWindow()
    {
        InitializeComponent();

        Colors.AddRange(Students.Select(x => x.TeamName).Distinct());
    }
}

例如,如果 Craig 的团队名称更改为“BlueGreen”,Bob 的团队名称应该同时更新: enter image description here

我通过处理 ComboBox.TextBoxBase.TextChanged 事件已经非常接近了,但它变得足够复杂,让我认为必须有一种更简单的方法。

c# wpf data-binding datagrid
1个回答
0
投票

这不是最优雅的解决方案,但它会起作用并通常向您展示如何解决该问题。

首先,修改您的

Student
类以使用 id 作为
Team
而不是名称,并添加
Team
类来处理团队的
Id
Name

    public class Student
    {
        public string Name { get; set; }
        public int TeamId { get; set; }

        public Student(string name, int teamId)
        {
            Name = name;
            TeamId = teamId;
        }

    }

    public class Team
    {
        public Team(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; set; }
        public string Name { get; set; }
    }

然后您可以在

ComboBox
中关闭这些值。当
ComboBox
失去焦点时,您可以通过
Teams
事件更新您的
LostFocus
列表。

XAML

<DataGrid x:Name="DataTable" ItemsSource="{Binding Students}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" Header="Name"/>
        <DataGridTemplateColumn Header="Team Name">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding DataContext.Teams, 
                                                    RelativeSource={RelativeSource AncestorType=Window}}" 
                                SelectedValue="{Binding TeamId}"
                                SelectedValuePath="Id" 
                                DisplayMemberPath="Name"
                                IsEditable="True"
                                BorderThickness="0"
                                LostFocus="ComboBox_LostFocus"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

背后代码

    public partial class EditableComboBox : Window
    {
        public ViewModel DataModel { get; set; }
        public EditableComboBox()
        {
            InitializeComponent();
            DataModel = new ViewModel();
            DataContext = DataModel;
        }

        private void ComboBox_LostFocus(object sender, RoutedEventArgs e)
        {
            var record = (e.Source as TextBox)?.DataContext as Student;
            if (record == null) return;
            DataModel.UpdateTeam(record.TeamId, (e.Source as TextBox)?.Text);

            DataTable.Items.Refresh();
        }
    }

视图模型

    public class ViewModel
    {

        public IReadOnlyList<Team> Teams { get; } = new List<Team>
        {
            new(1, "Blue"),
            new(2, "Red" ),
            new(3, "Green" ),
        };

        public List<Student> Students { get; set; } = new List<Student>
        {
            new("Name 1", 1),
            new("Name 2", 2),
            new("Name 3", 3),
            new("Name 4", 1),
            new("Name 5", 2),
            new("Name 6", 3),
            new("Name 7", 1),
            new("Name 8", 2),
            new("Name 9", 3),
        };

        public void UpdateTeam(int id, string newName)
        {
            var team = Teams.FirstOrDefault(x => x.Id == id);
            if (team == null) return;
            team.Name = newName;
        }

    }

result

© www.soinside.com 2019 - 2024. All rights reserved.