WPF DataGrid中外键关联数据无法实时刷新问题咨询
问题描述
在基于WPF、MVVM和Entity Framework的应用中,DataGrid展示配送单列表。通过EditWindow对话框修改配送单关联的快递员、配送名称等信息后,调用SaveChanges能正常保存数据到数据库,但DataGrid中的CourierUid字段及快递员全名NameCourierFull无法实时刷新,必须重启应用才会显示更新。
相关代码
Delivery类
public class Delivery : INotifyPropertyChanged { string nameDelivery = string.Empty; string? courierUid = null; Courier? courier; public string DeliveryUid { get; set; } = Guid.NewGuid().ToString(); public string NameDelivery { get { return nameDelivery; } set { nameDelivery = value; OnPropertyChanged(nameof(NameDelivery)); } } public string? CourierUid { get { return courierUid; } set { courierUid = value; OnPropertyChanged(nameof(CourierUid)); OnPropertyChanged(nameof(NameCourierFull)); } } public Courier? Courier { get { return courier; } set { courier = value; OnPropertyChanged(nameof(Courier)); OnPropertyChanged(nameof(NameCourierFull)); // 同步更新CourierUid,保证属性一致性 if (courierUid != value?.CourierUid) { courierUid = value?.CourierUid; OnPropertyChanged(nameof(CourierUid)); } } } public string NameCourierFull => Courier?.NameCourierFull ?? string.Empty; public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } }
Courier类
public class Courier : INotifyPropertyChanged { string nameFirst = string.Empty; string nameSecond = string.Empty; string nameThird = string.Empty; public string CourierUid { get; set; } = Guid.NewGuid().ToString(); public string NameFirst { get { return nameFirst; } set { nameFirst = value; OnPropertyChanged(nameof(NameFirst)); OnPropertyChanged(nameof(NameCourierFull)); } } public string NameSecond { get { return nameSecond; } set { nameSecond = value; OnPropertyChanged(nameof(NameSecond)); OnPropertyChanged(nameof(NameCourierFull)); } } public string NameThird { get { return nameThird; } set { nameThird = value; OnPropertyChanged(nameof(NameThird)); OnPropertyChanged(nameof(NameCourierFull)); } } public string NameCourierFull => $"{NameFirst} {NameSecond} {NameThird}"; public ICollection<Delivery> Deliveries { get; set; } = []; public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } }
DeliveryViewModel的EditDeliveryCommand
public RelayCommand EditDeliveryCommand { get { return editDeliveryCommand ??= new RelayCommand((selectedItem) => { if (selectedItem is not Delivery dbDelivery) return; Delivery delivery = new() { DeliveryUid = dbDelivery.DeliveryUid, NameDelivery = dbDelivery.NameDelivery, CourierUid = dbDelivery.CourierUid }; WindowEditDelivery window = new(delivery, Couriers); if (window.ShowDialog() == true) { dbDelivery.NameDelivery = delivery.NameDelivery; Courier? courierNew = db.Couriers.FirstOrDefault(c => c.CourierUid == delivery.CourierUid); dbDelivery.Courier = courierNew; // EF自动跟踪实体变化,无需手动设置状态 db.SaveChanges(); } }); } }
WindowEditDelivery.xaml
<Window x:Class="DeliveryApplication.WindowsEdit.WindowEditDelivery" 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:DeliveryApplication.WindowsEdit" mc:Ignorable="d" Title="Карточка заявки" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Text="Name Delivery"/> <TextBlock Text="Assigned Courier" Grid.Row="1"/> <TextBox Text="{Binding NameDelivery}" Grid.Column="1"/> <ComboBox x:Name="ComboBoxCourier" Grid.Row="1" Grid.Column="1" DisplayMemberPath="NameCourierFull" SelectedValuePath="CourierUid" SelectedValue="{Binding CourierUid}"/> <StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Grid.Row="2" Grid.Column="1"> <Button IsDefault="True" Click="Accept_Click">OK</Button> <Button IsCancel="True">Отмена</Button> </StackPanel> </Grid> </Window>
WindowEditDelivery类
public partial class WindowEditDelivery : Window { public Delivery Delivery { get; private set; } public WindowEditDelivery(Delivery delivery, ObservableCollection<Courier> Couriers) { InitializeComponent(); Delivery = delivery; DataContext = Delivery; ComboBoxCourier.ItemsSource = Couriers; } private void Accept_Click(object sender, RoutedEventArgs e) { DialogResult = true; } }
MainWindow.xaml
<Window x:Class="DeliveryApplication.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:DeliveryApplication" xmlns:models="clr-namespace:DeliveryApplication.Models" d:DataContext="{d:DesignInstance Type=models:Courier}" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <TabControl> <TabItem Header="Deliveries"> <TabItem.Content> <Grid> <Grid.RowDefinitions> <RowDefinition Height="25"/> <RowDefinition Height="*"/> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Menu Grid.ColumnSpan="2" VerticalAlignment="Top"> <MenuItem Header="EditDelivery" Command="{Binding EditDeliveryCommand}" CommandParameter="{Binding ElementName=DataGridDelivery, Path=SelectedItem}"/> </Menu> <DataGrid x:Name="DataGridDelivery" ItemsSource="{Binding Deliveries}" AutoGenerateColumns="False" CanUserAddRows="False" Grid.Row="1" Grid.ColumnSpan="2"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding DeliveryUid}" Header="Uid" Visibility="Hidden" IsReadOnly="True"> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding NameDelivery}" Header="Название груза" Width="*" IsReadOnly="True"> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding CourierUid}" Header="Courier Uid" Width="*" IsReadOnly="True"> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding NameCourierFull}" Header="Курьер" Width="*" IsReadOnly="True"> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> </Grid> </TabItem.Content> </TabItem> </TabControl> </Window>
MainWindow.cs
public partial class MainWindow : Window { private DeliveryViewModel context; public MainWindow() { InitializeComponent(); context = new DeliveryViewModel(); DataContext = context; } }
解决方案说明
- 修复Delivery类的属性通知逻辑:
- 给
Courier属性添加完整的setter,在属性变更时触发Courier和NameCourierFull的PropertyChanged通知,同时同步更新CourierUid并触发其通知,确保DataGrid绑定的字段能感知到变化。
- 给
- 简化EF状态管理:
- 移除手动设置
EntityState.Modified的代码,EF会自动跟踪已加载实体的属性变化,无需手动标记状态。
- 移除手动设置
- 保证属性一致性:
- 修改
Courier属性时自动同步CourierUid,避免两个关联属性数据不一致的问题,同时触发对应的通知事件实现实时刷新。
- 修改
内容的提问来源于stack exchange,提问作者remaL
相关产品推荐
相关产品推荐

