删除项目时绑定到 ObservableCollection 的 DataGrid 不会更新
Posted
技术标签:
【中文标题】删除项目时绑定到 ObservableCollection 的 DataGrid 不会更新【英文标题】:DataGrid bound to ObservableCollection doesn't update when an Item is Removed 【发布时间】:2021-11-15 03:58:41 【问题描述】:我有这个有四个主要视图的 WPF 应用程序。其中一个视图,我们称之为“OrdersView”,有一个包含订单的 DataGrid。为此,DataContext 是一个 ViewModel,即“OrdersViewModel”,当然,它实现了 INotifyPropertyChanged。网格的每一行都有一个按钮来查看订单的详细信息。此按钮通过窗口服务打开一个新窗口,允许用户通过处理订单、添加更多项目或删除订单来更新订单。该窗口包含一个名为“OrderDetailsView”的用户控件,它当然有一个“OrderDetailsViewModel”作为 DataContext。我对“编辑订单”和“处理订单”功能没有任何问题。问题是“删除订单”。此按钮绑定到 ViewModel 中的命令,该命令引发事件并将订单 Id 发送到应用程序主窗口中的侦听器,然后主窗口将此 Id 传递给“OrdersViewModel”中具有查找功能的方法在 ObservableCollection 中排序并将其删除。 该项目已删除,我在调试器中看到了 Collection 的变化,但是当“OrderDetailsView”关闭时,Orders DataGrid 仍然有血腥的订单,只有当我导航到不同的视图并返回时才会刷新它。 我试图将 RaisePropertyChanged 方法放在集合的 setter 中,在 delete 方法的末尾,属性名称为 Orders 并且不起作用。
数据网格开始如下:
<DataGrid ItemsSource="Binding Path=Orders"
Style="DynamicResource DataGridStyle">
ViewDetails 按钮是:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="BtnOrderDetails"
Content="View"
Margin="1"
Command="Binding DataContext.SeeOrderDetailsCommand, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=Window"
CommandParameter="Binding" />
</DataTemplate>
OrdersViewModel 类似于:
public class OrdersViewModel : ViewModelBase
private ObservableCollection<Order> _orders;
public OrdersViewModel(List<Order> orders)
Orders = new ObservableCollection<Order>(orders);
public ObservableCollection<Order> Orders
get => _orders;
set
_orders = value;
RaisePropertyChanged();
public void DeleteOrder(int id)
Orders.Remove(Orders
.FirstOrDefault(order => order.Id.Equals(id)));
RaisePropertyChanged(nameof(Orders));
“查看详细信息”按钮会将您带到一个新窗口,该窗口包含“OrderDetailsView”并具有如下按钮:
<Button x:Name="DeleteOrder"
Content="Delete order"
Grid.Row="2"
Margin="10,0,0,10"
Style="StaticResource DeleteOrderButton"
Command="Binding DeleteOrderCommand"/>
在此视图的视图模型中,我有以下内容:
public DelegateCommand DeleteOrderCommand get; private set;
在构造函数中:
DeleteOrderCommand = new DelegateCommand(DeleteOrder, () => CanDelete);
该方法引发了一个事件(你是这么说的吗?):
private void DeleteOrder()
DeleteOrderRequested(Order.Id);
CloseWindow();
public event Action<int> DeleteOrderRequested = delegate ;
我的主窗口是传达不同视图模型的东西,它捕获了这个事件:
private void OnDeleteOrderRequested(int id)
OrdersViewModel.DeleteOrder(id);
我知道,它的功能是一个愚蠢的复杂应用程序,对吧?
为什么,为什么,为什么视图没有收到有关项目已被删除的通知???
有人可以帮忙吗?或者至少告诉我在哪里继续寻找?
谢谢大家!
【问题讨论】:
很难理解你的逻辑是如何工作的,因为你选择只发布松散的代码 sn-ps。您应该检查是否调用了命令并监听事件并在正确的实例上调用 delete - 确保您仅使用单个 OrdersViewModel 实例。您定义事件委托的方式非常不同寻常。您应该改用Eventhandler<TEventArgs>
。但更重要的是考虑让您的 OrdersViewModel 为当前选定的订单公开 OrdersDetailsViewModel。这可以显着简化逻辑并很有可能解决问题。
非常感谢您的回复。完全同意,就是代码太多了,就是觉得不合适贴200行代码来解释viewModel的作用。我不知道如何使用你提到的 eventHandler,不过我会调查的,保证。无论如何,我发现了问题。与启动时 currentViewModel 的分配有关。在这里解释太长了。再次感谢。
【参考方案1】:
我发现了问题。我使用一个不太优雅的 switch 语句处理导航,该语句在 MainWindow 中切换 CurrentViewModel 并使用数据模板进行更改,因此当 VM 更改时,视图会更改。可能不是很传统,它有点工作。问题是,在启动时,我将 CurrentViewModel 分配给了 OrdersViewModel,然后将该 viewModel 放在一个属性中,以便稍后我想返回时使用它。所以我应该反过来做,我已经做过了,所以当应用程序启动时,我首先创建 OrderViewModel,放入一个属性,然后我将 currentVM 分配给那个 VM。我注意到这是问题所在,因为当我离开第一个视图然后返回时,删除功能实际上起作用了。 MainWindowViewModel 是这样的:
public MainWindowViewModel(IOrdersDataProvider ordersData, ViewModelBase currentViewModel, AddItemViewModel addItemViewModel, ViewModelFactory vMFactory)
_Data = ordersData;
_isModalOpen = false;
_currentViewModel = currentViewModel;
_addItemViewModel = addItemViewModel;
NavigateCommand = new RelayCommandT<string>(Navigate, () => OrderDetailsWindowsOpen);
SeeOrderDetailsCommand = new DelegateCommand<Order>(ViewOrderDetails);
CreateNewOrderCommand = new DelegateCommand(CreateNewOrder);
_vMFactory = vMFactory;
app.xaml.cs 中的 ComposeObjects 方法正在做一些非常愚蠢的事情:
private static void ComposeObjects(ViewModelFactory vMFactory)
var currentViewModel = vMFactory.CreateViewModel("Orders");
var addOrderViewModel = vMFactory.CreateViewModel("AddOrder");
var mainWindowViewModel = (MainWindowViewModel)vMFactory.CreateViewModel("MainWindow");
// Set up main ViewModel
mainWindowViewModel.AddOrderViewModel = (AddOrderViewModel)addOrderViewModel;
mainWindowViewModel.OrdersViewModel = (OrdersViewModel)currentViewModel;
mainWindowViewModel.SubscribeHandlersToEvents();
Application.Current.MainWindow = new MainWindow
DataContext = mainWindowViewModel
;
我是这样改的:
private static void ComposeObjects(ViewModelFactory vMFactory)
// Get ViewModels
var addOrderViewModel = vMFactory.CreateViewModel("AddOrder");
var mainWindowViewModel = (MainWindowViewModel)vMFactory.CreateViewModel("MainWindow");
// Set up main ViewModel
mainWindowViewModel.AddOrderViewModel = (AddOrderViewModel)addOrderViewModel;
mainWindowViewModel.SubscribeHandlersToEvents();
Application.Current.MainWindow = new MainWindow
DataContext = mainWindowViewModel
;
我将 MainWindowVM 构造函数调整为:
public MainWindowViewModel(IOrdersDataProvider ordersData, ViewModelBase ordersViewModel, AddItemViewModel addItemViewModel, ViewModelFactory vMFactory)
_Data = ordersData;
_isModalOpen = false;
OrdersViewModel = (OrdersViewModel)ordersViewModel;
_currentViewModel = OrdersViewModel;
_addItemViewModel = addItemViewModel;
NavigateCommand = new RelayCommandT<string>(Navigate, () => OrderDetailsWindowsOpen);
SeeOrderDetailsCommand = new DelegateCommand<Order>(ViewOrderDetails);
CreateNewOrderCommand = new DelegateCommand(CreateNewOrder);
_vMFactory = vMFactory;
首先分配 OrdersViewModel 属性就成功了,当然,这很麻烦,这是一项正在进行的工作,我将在接下来的几天内实现一个像 Ninject 这样的容器。 我现在可以去睡觉了。
【讨论】:
以上是关于删除项目时绑定到 ObservableCollection 的 DataGrid 不会更新的主要内容,如果未能解决你的问题,请参考以下文章
如何在绑定到ObservableCollection时删除DataGrid的空行 ?
绑定到 ObservableCollection<T> 时如何删除 DataGrid 的空白行?
Entity Framework 6 在从绑定的 DataGridView 删除时应该使用 DELETE 时使用 UPDATE