更改DataGrid时,使用MessageBox添加一个检查

我已经发布了一个桌面应用程序(所以我很感激能够将更改和回归测试保持在最低限度的答案)并且我需要在网格更改时添加一致性检查CanBeDeleted

  

我正在使用UpdateEnabled用于不同的东西(配置文件权限),我也不想让DataGrid只读取:我更喜欢( 除非它太复杂 )看到阻止更改的阻止警报( MessageBox )。

我到目前为止所做的是

  1. 对抗MVVM,因为我已将警报放入模型中(但我可以接受这一点,如果它使更改快速而简单)
  2. 不工作,因为我的更改的第二部分(见下文)产生了无效的操作exception

ViewModel包含以下列表

  [Association(ThisKey="Partita", OtherKey="Partita", CanBeNull=true, IsBackReference=true)] public ObservableCollection posin_locations_list = new ObservableCollection(); public ObservableCollection PosInLocationsList { get { return posin_locations_list; } set { posin_locations_list = value; OnPropertyChanged( () => PosInLocationsList ); } } 

我在这里添加一致性检查

  string _storage; [Column(Name = "storage"), PrimaryKey] public string Storage { get { return _storage; } set { if (this.loadedEF) { string validate_msg; if (!PurchasePosIn.CanBeDeleted(out validate_msg)) { // against MVVM MessageBox.Show(validate_msg, "Alert", MessageBoxButton.OK); OnPropertyChanged( () => Storage ); return; } Persistence.MyContext.deletePosInLocation(this); } _storage = value; OnPropertyChanged( () => Storage ); if (this.loadedEF) { Persistence.MyContext.insertPosInLocation(this); } } } 

在这里(第二部分)

  internal void posin_locations_list_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args) { string validate_msg; if (!CanBeDeleted(out validate_msg)) { // indirectly produces an invalid operation exception MessageBox.Show(validate_msg, "Alert", MessageBoxButton.OK); return; } if (args.OldItems != null) foreach(var oldItem in args.OldItems) { if ( ((PosInLocation)oldItem).Partita != null) Persistence.MyContext.deletePosInLocation((PosInLocation)oldItem); } if (args.NewItems != null) foreach(var newItem in args.NewItems) { PosInLocation newPosInLocation = (PosInLocation)newItem; if ( newPosInLocation.Partita == null) { newPosInLocation.Partita = this.Partita; newPosInLocation.PurchasePosIn = this; newPosInLocation.loadedEF = true; } } } 

如果只有ObservableCollection实现了“previewCollectionChanged”,事情会变得如此简单。

根据您的需要,我建议创建一个ObservableCollection的子类并重载受保护的方法RemoveItem。
根据您对应用程序的操作,您可能希望覆盖除RemoveItem之外的其他方法(如ClearItems)。

当inheritanceObservableCollection时,您可以覆盖5个受保护的方法:ClearItems,RemoveItem,InsertItem,SetItem和MoveItem。
最后,这些方法被所有公共方法调用,因此覆盖它们可以让您完全控制。

这是一个可以运行的小应用程序,用于演示:

ObservableCollection SubClass

 public class ObservableCollectionWithDeletionControl : ObservableCollection { public delegate void DeletionDeniedEventHandler(object sender, int indexOfDeniedDeletion); public event DeletionDeniedEventHandler DeletionDenied; public bool CanDelete { get; set; } protected virtual void OnDeletionDenied(int indexOfDeniedDeletion) { if (DeletionDenied != null) { DeletionDenied(this, indexOfDeniedDeletion); } } protected override void RemoveItem(int index) { if (CanDelete) { base.RemoveItem(index); } else { OnDeletionDenied(index); } } } 

我使用DeletionDenied事件,以便此类不负责显示错误窗口,并使其更可重用。

视图模型

 public class MainWindowViewModel { public MainWindow window { get; set; } public ObservableCollectionWithDeletionControl People { get; set; } = new ObservableCollectionWithDeletionControl(); public MainWindowViewModel() { People.DeletionDenied += People_DeletionDenied; } private void People_DeletionDenied(object sender, int indexOfDeniedDeletion) { Person personSavedFromDeletion = People[indexOfDeniedDeletion]; window.displayDeniedDeletion(personSavedFromDeletion.Name); } } 

MainWindow的ViewModel。
它知道它的窗口仅用于显示错误消息。
(我确信有比这更好的解决方案,但我还没有找到一种在mvvm中显示弹出窗口的好方法。)
当DeletionDenied事件触发时,将调用错误窗口。

模型

 public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public string Name { get { return _name; } set { if(_name == value) { return; } _name = value; if( PropertyChanged != null ) { PropertyChanged(this, new PropertyChangedEventArgs("Name")); } } } private string _name = ""; } 

XAML

       

XAML.CS

 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public void displayDeniedDeletion(string name) { TextBox errorMessage = new TextBox(); errorMessage.Text = string.Format("Cannot delete {0} : access denied !", name); Window popupErrorMessage = new Window(); popupErrorMessage.Content = errorMessage; popupErrorMessage.ShowDialog(); } } 

APP MAIN

 public partial class App : Application { private void Application_Startup(object sender, StartupEventArgs e) { MainWindow window = new MainWindow(); MainWindowViewModel viewModel = new MainWindowViewModel(); viewModel.window = window; window.DataContext = viewModel; window.Show(); App.Current.MainWindow = window; } } 

我在启动时设置了ViewModel的窗口,但你应该在创建ViewModel的任何地方都这样做

也许它很难看(真的,它不是那么难看:imho它是一个很好的MVVM方法 ,也适用于现代mahapps.metro Dialogs ),但现在我正在设置

 if (!CanBeDeleted(out validate_msg)) { PurchaseItem.MessageBoxText = validate_msg; 

一个看不见的TextBox

   

我在哪里发出警报

  void TextBox_TextChanged(object sender, TextChangedEventArgs e) { string alert = tb_message.Text; if (alert != null && tb_message.Text.Length>0) { Dispatcher.BeginInvoke( (Action)(() => { MessageBox.Show(alert, "Alert", MessageBoxButton.OK); tb_message.Text = ""; })); } } 

回滚添加/删除的项目

我看到与另一个问题的连接阻止在ObservableCollection.CollectionChanged事件上添加新项目 ,在我的情况下我会说防止删除更为重要。 我不知道是否有比此更新的答案( 我可以回滚集合更改事件的集合更改吗?这看起来是错误的 )关于此主题。

虽然可以轻松地引发PropertyChanged以回滚项更新,但是对于集合更改,我被迫传递并引用CollectionChanged事件中的视图调度程序

 PurchaseItem.dispatcher.BeginInvoke((Action)(() => RollBack(args))); 

回滚添加/删除的项目

  bool rollingBack = false; private void RollBack(NotifyCollectionChangedEventArgs args) { rollingBack = true; if (args.Action == NotifyCollectionChangedAction.Remove) { foreach (var element in args.OldItems) { PosInLocationsList.Add((PosInLocation)element); } } if (args.Action == NotifyCollectionChangedAction.Add) { foreach (var element in args.NewItems) { PosInLocationsList.Remove((PosInLocation)element); } } rollingBack = false; } 

我到目前为止所做的是

对抗MVVM,因为我已将警报放入模型中

来自@Tesseract,inheritanceObservableCollection和订阅RemoveItem的解决方案已经面向MVVM。

仍然缺少的是从ViewModel发送警报的正确,现代的方法。 这是Mahapps方法有用的地方。

  • 在窗口中使用附加属性可以使用对话框子系统注册视图模型。

在你的XAML中

 Dialog:DialogParticipation.Register="{Binding}" 

附加属性DialogPartecipation将通过字典跟踪视图

 public static class DialogParticipation { private static readonly IDictionary ContextRegistrationIndex = new Dictionary 
  • 您可以直接实例化DialogCoordinator或将接口IDialogCoordinator注入其视图模型

DialogCoordinator将ViewModel与View匹配

 public class DialogCoordinator : IDialogCoordinator { public static readonly IDialogCoordinator Instance = new DialogCoordinator(); 

通过上面说的附加属性( context是视图模型)

 var association = DialogParticipation.GetAssociation(context); 

并显示对话框,在检索到的视图上调用适当的方法:如果打开多个窗口,对话框将显示在正确的窗口上。