作为 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。
CanSave
和 CanCancel
属性(假设您正在为这些命令使用 RelayCommand
)只返回 的当前值脏了
。
请注意,由于这些功能都不依赖于 View 模型的具体实现,因此您可以将其放在抽象基类中。派生类不必实现任何与命令相关的属性或 IsDirty
属性;他们只需覆盖 BeginEdit
、EndEdit
和 CancelEdit
。
关于c# - MVVM - 为 ModelView 实现 'IsDirty' 功能以保存数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4556387/