c# - MVVM - 为 ModelView 实现 'IsDirty' 功能以保存数据

标签 c# wpf mvvm observablecollection

作为 WPF 和 MVVM 的新手,我在一些基本功能上遇到了困难。

让我先解释一下我在追求什么,然后附上一些示例代码...

我有一个显示用户列表的屏幕,我在右侧显示了带有可编辑文本框的所选用户的详细信息。然后我有一个 Save 按钮,它是 DataBound,但我只希望这个按钮在数据实际更改时显示。即 - 我需要检查“脏数据”。

我有一个完整的 MVVM 示例,其中我有一个名为 User 的模型:

namespace Test.Model
{
    class User
    {
        public string UserName { get; set; }
        public string Surname { get; set; }
        public string Firstname { get; set; }
    }
}

然后,ViewModel 看起来像这样:

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Input;
using Test.Model;

namespace Test.ViewModel
{
    class UserViewModel : ViewModelBase
    {
        //Private variables
        private ObservableCollection<User> _users;
        RelayCommand _userSave;

        //Properties
        public ObservableCollection<User> User
        {
            get
            {
                if (_users == null)
                {
                    _users = new ObservableCollection<User>();
                    //I assume I need this Handler, but I am stuggling to implement it successfully
                    //_users.CollectionChanged += HandleChange;

                    //Populate with users
                    _users.Add(new User {UserName = "Bob", Firstname="Bob", Surname="Smith"});
                    _users.Add(new User {UserName = "Smob", Firstname="John", Surname="Davy"});
                }
                return _users;
            }
        }

        //Not sure what to do with this?!?!

        //private void HandleChange(object sender, NotifyCollectionChangedEventArgs e)
        //{
        //    if (e.Action == NotifyCollectionChangedAction.Remove)
        //    {
        //        foreach (TestViewModel item in e.NewItems)
        //        {
        //            //Removed items
        //        }
        //    }
        //    else if (e.Action == NotifyCollectionChangedAction.Add)
        //    {
        //        foreach (TestViewModel item in e.NewItems)
        //        {
        //            //Added items
        //        }
        //    } 
        //}

        //Commands
        public ICommand UserSave
        {
            get
            {
                if (_userSave == null)
                {
                    _userSave = new RelayCommand(param => this.UserSaveExecute(), param => this.UserSaveCanExecute);
                }
                return _userSave;
            }
        }

        void UserSaveExecute()
        {
            //Here I will call my DataAccess to actually save the data
        }

        bool UserSaveCanExecute
        {
            get
            {
                //This is where I would like to know whether the currently selected item has been edited and is thus "dirty"
                return false;
            }
        }

        //constructor
        public UserViewModel()
        {

        }

    }
}

“RelayCommand”只是一个简单的包装类,“ViewModelBase”也是如此。 (为了清楚起见,我会附上后者)

using System;
using System.ComponentModel;

namespace Test.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
    {
        protected ViewModelBase()
        { 
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        public void Dispose()
        {
            this.OnDispose();
        }

        protected virtual void OnDispose()
        {
        }
    }
}

最后 - XAML

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:Test.ViewModel"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:UserViewModel/>
    </Window.DataContext>
    <Grid>
        <ListBox Height="238" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox1" VerticalAlignment="Top" 
                 Width="197" ItemsSource="{Binding Path=User}" IsSynchronizedWithCurrentItem="True">
            <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                        <TextBlock Text="{Binding Path=Firstname}"/>
                        <TextBlock Text="{Binding Path=Surname}"/>
                </StackPanel>
            </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Label Content="Username" Height="28" HorizontalAlignment="Left" Margin="232,16,0,0" Name="label1" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="323,21,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=User/UserName}" />
        <Label Content="Surname" Height="28" HorizontalAlignment="Left" Margin="232,50,0,0" Name="label2" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="323,52,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" Text="{Binding Path=User/Surname}" />
        <Label Content="Firstname" Height="28" HorizontalAlignment="Left" Margin="232,84,0,0" Name="label3" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="323,86,0,0" Name="textBox3" VerticalAlignment="Top" Width="120" Text="{Binding Path=User/Firstname}" />
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="368,159,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding Path=UserSave}" />
    </Grid>
</Window>

所以基本上,当我编辑姓氏时,应该启用“保存”按钮;如果我撤消我的编辑 - 那么它应该再次被禁用,因为没有任何改变。

我在很多例子中都看到了这一点,但还没有找到如何去做。

任何帮助将不胜感激! 布伦丹

最佳答案

根据我的经验,如果您在 View 模型中实现了 IsDirty,您可能还希望 View 模型实现 IEditableObject

假设您的 View 模型是通常的类型,实现 PropertyChanged 和引发它的私有(private)或 protected OnPropertyChanged 方法,设置 IsDirty 是非常简单:您只需在 OnPropertyChanged 中设置 IsDirty(如果它尚未为真)。

您的 IsDirty setter 应该,如果该属性为 false 而现在为 true,则调用 BeginEdit

您的Save 命令应该调用EndEdit,它会更新数据模型并将IsDirty 设置为false。

您的Cancel 命令应该调用CancelEdit,它从数据模型刷新 View 模型并将IsDirty 设置为false。

CanSaveCanCancel 属性(假设您正在为这些命令使用 RelayCommand)只返回 的当前值脏了

请注意,由于这些功能都不依赖于 View 模型的具体实现,因此您可以将其放在抽象基类中。派生类不必实现任何与命令相关的属性或 IsDirty 属性;他们只需覆盖 BeginEditEndEditCancelEdit

关于c# - MVVM - 为 ModelView 实现 'IsDirty' 功能以保存数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4556387/

相关文章:

c# - 使用MVVM,如何使用App.config动态生成 View

c# - 在 AWS Lambda 上运行 IronPDF - 无法将二进制文件解压到/tmp (.NET/C#)

c# - 带有 ASP.NET MVC 的 Paypal API

wpf - 使用自定义控件而不是用户控件来创建复杂的 View

c# - 保存和自动保存 PowerPoint OpenXML PresentationDocuments

wpf - WPF 中的闪烁按钮内容模板

c# - 如果c# wpf中的可见性为 'Auto',如何知道 ListView 的VerticalScrollbar是否可见?

WPF TextBox lostfocus 作为附加属性

ios - 在 MVVM 和 MVC 模式中在哪里创建 View ?

java - 如何从 Worker 获取输出数据? (MVVM + WorkManager + Room)