如何实现WPF DataGrid可编辑ComboBox列并同步更新同值行?
问题需求
在绑定到集合的WPF DataGrid中,某列设置为可编辑的ComboBox。要求修改ComboBox文本时,集合中所有之前使用相同值的项同步更新——例如将Craig的团队名称改为“BlueGreen”,Bob的团队名称需同时自动更新。
原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>
原C#代码:
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(); TeamNames.AddRange(Students.Select(x => x.TeamName).Distinct()); } }
解决方案
核心思路是让学生共享同一个Team对象,而非独立存储字符串。修改Team对象的名称时,所有引用它的学生都会自动同步更新。
1. 创建共享的Team类
实现INotifyPropertyChanged,确保名称修改时通知UI更新:
public class Team : INotifyPropertyChanged { private string _name; public string Name { get => _name; set { if (_name != value) { _name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } } } public event PropertyChangedEventHandler PropertyChanged; }
2. 修改Student类,引用Team对象
将原字符串类型的TeamName替换为Team对象引用:
public class Student : INotifyPropertyChanged { private Team _team; public string Name { get; set; } public Team Team { get => _team; set { if (_team != value) { _team = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Team))); } } } public Student(string name, Team team) { Name = name; Team = team; } public event PropertyChangedEventHandler PropertyChanged; }
3. 调整MainWindow逻辑,管理共享团队集合
使用ObservableCollection<Team>存储团队,处理ComboBox编辑后的团队复用/创建逻辑:
public partial class MainWindow : Window { public ObservableCollection<Student> Students { get; set; } public ObservableCollection<Team> Teams { get; set; } = new ObservableCollection<Team>(); public MainWindow() { InitializeComponent(); // 初始化共享团队 var redTeam = new Team { Name = "Red" }; var blueTeam = new Team { Name = "Blue" }; Teams.Add(redTeam); Teams.Add(blueTeam); // 学生绑定同一个Team对象 Students = new ObservableCollection<Student> { new Student("Alice", redTeam), new Student("Bob", blueTeam), new Student("Craig", blueTeam) }; } // 处理ComboBox编辑完成后的逻辑 private void ComboBox_LostFocus(object sender, RoutedEventArgs e) { var comboBox = sender as ComboBox; var student = comboBox.DataContext as Student; var inputText = comboBox.Text; if (string.IsNullOrEmpty(inputText)) return; // 查找是否已有同名团队 var existingTeam = Teams.FirstOrDefault(t => t.Name == inputText); if (existingTeam != null) { student.Team = existingTeam; } else { // 创建新团队并加入集合 var newTeam = new Team { Name = inputText }; Teams.Add(newTeam); student.Team = newTeam; } } }
4. 修改XAML绑定逻辑
调整ComboBox的绑定,关联到Team对象的Name属性,并绑定编辑完成事件:
<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=Teams}" DisplayMemberPath="Name" SelectedItem="{Binding Team}" Text="{Binding Team.Name, UpdateSourceTrigger=LostFocus}" LostFocus="ComboBox_LostFocus"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </Window>
效果说明
- 修改Craig的团队名为“BlueGreen”时,由于Bob和Craig共享同一个
Blue团队对象,修改团队名称后所有引用该对象的学生UI会自动同步更新。 - 输入全新团队名时,会自动创建新的
Team对象并加入团队列表,后续其他学生可直接选择该新团队。
内容的提问来源于stack exchange,提问作者Craig W
相关产品推荐
相关产品推荐

