Bre*_*dan 14 c# wpf observablecollection mvvm
作为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; }
}
}
Run Code Online (Sandbox Code Playgroud)
然后,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()
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
"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()
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后 - 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>
Run Code Online (Sandbox Code Playgroud)
基本上,当我编辑姓氏时,应该启用"保存"按钮; 如果我撤消我的编辑 - 那么它应该再次被禁用,因为没有任何改变.
我在很多例子中看到了这一点,但还没有找到如何做到这一点.
任何帮助将非常感激!布伦丹
根据我的经验,如果IsDirty
在视图模型中实现,您可能还希望实现视图模型IEditableObject
.
假设您的视图模型是通常的排序,实施PropertyChanged
和私人或受保护OnPropertyChanged
的是提高它的方法,设置IsDirty
是相当简单:您只需设置IsDirty
中OnPropertyChanged
,如果它是不是已经真的.
你的IsDirty
二传手应该,如果属性是假的,现在是真的,请打电话BeginEdit
.
您的Save
命令应该调用EndEdit
,它会更新数据模型并设置IsDirty
为false.
您的Cancel
命令应该调用CancelEdit
,从数据模型刷新视图模型并设置IsDirty
为false.
在CanSave
和CanCancel
属性(假设你使用RelayCommand
这些命令)刚刚返回的当前值IsDirty
.
请注意,由于此功能都不依赖于视图模型的特定实现,因此可以将其放在抽象基类中.派生类不必实现任何与命令相关的属性或IsDirty
属性; 他们只需要重写BeginEdit
,EndEdit
和CancelEdit
.
由于您的 UserSave 命令位于 ViewModel 中,因此我将在那里跟踪“脏”状态。我将数据绑定到列表框中选定的项目,当它发生更改时,存储选定用户属性的当前值的快照。然后您可以与此进行比较以确定是否应启用/禁用该命令。
但是,由于您直接绑定到模型,因此您需要某种方法来查明某些内容是否发生变化。您还可以在模型中实现 INotifyPropertyChanged,或者将属性包装在 ViewModel 中。
请注意,当命令的 CanExecute 更改时,您可能需要触发 CommandManager.InvalidateRequerySuggested()。
归档时间: |
|
查看次数: |
11356 次 |
最近记录: |