更改 DataGrid 时添加对 MessageBox 的检查 [英] add a check with MessageBox when DataGrid is changed

查看:10
本文介绍了更改 DataGrid 时添加对 MessageBox 的检查的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a desktop app already released (so I'd appreciate an answer that keeps the changes and regression tests at a minimum) and I need to add a consistency check CanBeDeleted when the grid is changed.

<DataGrid AutoGenerateColumns="False" 
    ItemsSource="{Binding CurrentPosIn.PosInLocationsList}" 
    CanUserAddRows="{Binding UpdateEnabled}" CanUserDeleteRows="{Binding UpdateEnabled}" >

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

I'm using UpdateEnabled for something different (profile permissions) and I don't want to make the DataGrid read only either: I'd prefer (unless it is too complex) to see a blocking alert (a MessageBox) preventing changes.

到目前为止我所做的是

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

ViewModel 包含以下列表

The ViewModel contains the following list

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

我在这里添加一致性检查

and I'm adding the consistency check here

    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).

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

If only the ObservableCollection implemented a "previewCollectionChanged", things would be so much easier.

For your needs I would recommend creating a subclass of ObservableCollection and overloading the protected method RemoveItem.
Depending on what you are doing with your application, you might want to override other methods than just RemoveItem (like ClearItems).

When subclassing ObservableCollection, there are 5 protected methods you can override : ClearItems, RemoveItem, InsertItem, SetItem and MoveItem.
These methods are, in the end, called by all the public methods, so overriding them gives you full control.

这是一个您可以运行的小应用程序来演示这一点:

Here's a small app you can run that demonstrates this :

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);
        }
    }
}

我使用了 DeletionDenied 事件,因此该类不负责显示错误窗口,并使其更具可重用性.

I use the DeletionDenied event so that this class is not responsible for showing the error window, and it makes it more reusable.

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);
    }
}

主窗口的视图模型.
它知道它的唯一目的是显示错误消息的窗口.
(我确信有比这更好的解决方案,但我还没有找到一种好的和简短的方式来显示弹出窗口mvvm.)
当 DeletionDenied 事件触发时,会调用错误窗口.

The ViewModel for the MainWindow.
It knows it's window for the sole purpose of displaying the error message.
(I'm sure there's a better solution than this out there but I have yet to find a good and short way of displaying popup windows in mvvm.)
When the DeletionDenied event triggers, the error window is called.

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;
    }
}

我已经在启动时设置了 ViewModel 的窗口,但您应该在创建 ViewModel 的任何地方都进行设置


I've set the ViewModel's window in the startup, but you should probably do it wherever you create your ViewModel


这篇关于更改 DataGrid 时添加对 MessageBox 的检查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆