You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
    }
}
解决方案说明
  1. 修复Delivery类的属性通知逻辑:
    • 给Courier属性添加完整的setter,在属性变更时触发Courier和NameCourierFull的PropertyChanged通知,同时同步更新CourierUid并触发其通知,确保DataGrid绑定的字段能感知到变化。
  2. 简化EF状态管理:
    • 移除手动设置EntityState.Modified的代码,EF会自动跟踪已加载实体的属性变化,无需手动标记状态。
  3. 保证属性一致性:
    • 修改Courier属性时自动同步CourierUid,避免两个关联属性数据不一致的问题,同时触发对应的通知事件实现实时刷新。

内容的提问来源于stack exchange,提问作者remaL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:39:56