C# 更改DataGrid时,添加带有MessageBox的复选框
我已经发布了一个桌面应用程序(因此我希望有一个能将更改和回归测试保持在最低限度的答案),当网格更改时,我需要添加一致性检查C# 更改DataGrid时,添加带有MessageBox的复选框,c#,wpf,mvvm,C#,Wpf,Mvvm,我已经发布了一个桌面应用程序(因此我希望有一个能将更改和回归测试保持在最低限度的答案),当网格更改时,我需要添加一致性检查CanBeDeleted <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding CurrentPosIn.PosInLocationsList}" CanUserAddRows="{Binding UpdateEnabled}" CanUserDeleteRows="{Binding
CanBeDeleted
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding CurrentPosIn.PosInLocationsList}"
CanUserAddRows="{Binding UpdateEnabled}" CanUserDeleteRows="{Binding UpdateEnabled}" >
我在这里添加了一致性检查
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);
}
}
}
这里(第二部分)
也许它很难看(真的,它没那么难看:嗯,它很好,也适用于现代的对话框
),但现在我正在设置
if (!CanBeDeleted(out validate_msg)) {
PurchaseItem.MessageBoxText = validate_msg;
不可见的文本框
<TextBox Visibility="Hidden" Name="tb_message"
Text="{Binding MessageBoxText}"
TextChanged="TextBox_TextChanged"
回滚添加/删除的项目
我看到了与另一个问题的联系,在我的例子中,我认为防止删除更为重要。关于这个话题,我不知道是否有比这个(出现的)更多的更新答案
虽然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;
}
如果ObservableCollection实现了“previewCollectionChanged”,事情就会简单得多。
出于您的需要,我建议创建ObservableCollection的子类并重载受保护的方法RemoveItem。
根据您对应用程序的操作,您可能希望覆盖除RemoveItem之外的其他方法(如ClearItems)。
当对ObservableCollection进行子类化时,有5种受保护的方法可以覆盖:ClearItems、RemoveItem、InsertItem、SetItem和MoveItem
这些方法最终会被所有公共方法调用,因此重写它们可以让您完全控制。
下面是一个可以运行的小应用程序,演示了这一点:
可观测集合子类
XAML 应用程序主 我已经在启动时设置了ViewModel的窗口,但您可能应该在创建ViewModel的任何位置执行此操作 我现在所做的是 针对MVVM,因为我已经在模型中添加了警报 @Tesseract、子类化
observeCollection
和订阅removietem
的解决方案已经面向MVVM
仍然缺少的是从ViewModel发送警报的正确、现代的方法。这是有帮助的地方
- 在窗口中使用附加属性注册视图模型 使用对话框子系统
Dialog:DialogParticipation.Register="{Binding}"
其中附加属性将通过字典跟踪视图
public static class DialogParticipation
{
private static readonly IDictionary<object, DependencyObject> ContextRegistrationIndex = new Dictionary<object, DependencyObject
通过上述附加属性(视图模型为上下文
)
并显示对话框,调用检索到的视图上的相应方法:如果打开多个窗口,对话框将显示在正确的窗口上。@user1892538我用一个示例更新了我的帖子。这适用于您的应用程序吗?我对您的答案投了赞成票,但我想强调两点:1)为了使它更可重用,除了DeletionDenied事件之外,我还应该定义一个CanDelete(类似地,其他2个用于insert),这样这个类就不负责条件逻辑。2) “我还没有找到一个在mvvm中显示弹出窗口的好方法和短方法。”虽然我接受你关于子类化和事件的回答,但这部分内容太粗糙了,我同意理想的解决方案(依赖属性)不是那么简单和短。。。但是为什么你不喜欢我答案第一部分的方法呢?坦率地说,我会坚持下去。@user1892538 1)我在我的课堂上定义了一个烛台,或者你还有别的想法吗?2) 啊,我不知道MessageBox类是一个标准类。是的,使用这个比我做的要简单得多(我稍后会尝试更新我的答案),但基本上是一样的。我不喜欢这个解决方案的原因是因为它打破了MVVM模式,ViewModel必须知道Window(或者在您的案例中是MessageBox)类才能运行,它应该只属于视图。一个更干净的方法可能是在VM和用户信息逻辑之间使用一个接口!关于第2点)我不是建议像我在问题中那样使用
MessageBox
,而是一个标准的mvvm字符串属性,绑定到TextBox
(或者更优雅的dp),触发xaml中的MessageBox
:我认为从mvvm的角度来看,这是完全正确的,不是吗。。。顺便说一句,您的INotifyPropertyChanged
的实现是错误的(我知道这是不相关的,但最好不要显示它)最后一点1)也许您是对的(我想到了另一个事件处理程序),我会再次检查这一点point@user1892538好的,我理解你对留言框的建议,是的,我认为这是一个理想的解决办法。我还没有玩太多的独立歌剧,我会在未来试一试。“您对INotifyPropertyChanged的实现是错误的”,您是指我忘记检查它是否为空的事实吗?谢谢你指出,我会更新的。
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;
}
public class ObservableCollectionWithDeletionControl<T> : ObservableCollection<T>
{
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);
}
}
}
public class MainWindowViewModel
{
public MainWindow window { get; set; }
public ObservableCollectionWithDeletionControl<Person> People { get; set; } = new ObservableCollectionWithDeletionControl<Person>();
public MainWindowViewModel()
{
People.DeletionDenied += People_DeletionDenied;
}
private void People_DeletionDenied(object sender, int indexOfDeniedDeletion)
{
Person personSavedFromDeletion = People[indexOfDeniedDeletion];
window.displayDeniedDeletion(personSavedFromDeletion.Name);
}
}
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 = "";
}
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<CheckBox DockPanel.Dock="Top" Content="Can delete" IsChecked="{Binding People.CanDelete}" Margin="5" HorizontalAlignment="Left"/>
<DataGrid ItemsSource="{Binding People}" Margin="5,0"/>
</DockPanel>
</Window>
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();
}
}
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;
}
}
Dialog:DialogParticipation.Register="{Binding}"
public static class DialogParticipation
{
private static readonly IDictionary<object, DependencyObject> ContextRegistrationIndex = new Dictionary<object, DependencyObject
public class DialogCoordinator : IDialogCoordinator
{
public static readonly IDialogCoordinator Instance = new DialogCoordinator();
var association = DialogParticipation.GetAssociation(context);