INotifiPropertyChanged
1. 作用:向客户端发出某一属性值已更改的通知。该接口包含一个PropertyChanged事件成员(MSDN的解释)
INotifyPropertyChanged 接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知。
例如,考虑一个带有名为 FirstName 属性的 Person 对象。若要提供一般性属性更改通知,则 Person
类型实现NotifyPropertyChanged 接口并在 FirstName 更改时引发 PropertyChanged
事件。若要在将客户端与数据源进行绑定时发出更改通知,则绑定类型应具有下列任一功能:
A. 实现 INotifyPropertyChanged 接口(首选)。
B. 为绑定类型的每个属性提供更改事件。
2. 使用:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
定义一个抽象基类,该类实现了INotifyPropertyChanged接口,所有继承自ViewModelBase 的类,都将具有:通知绑定端,后台属性发生变化的能力。
public class MainViewModel : ViewModelBase
{
//.............
private LeagueList dataToShow;
public LeagueList DataToShow
{
get
{
if (dataToShow == null)
dataToShow = new LeagueList();
return dataToShow;
}
set
{
dataToShow= value;
OnPropertyChanged("DataToShow");
}
}
}
MainViewModel 继承BaseViewModel,当属性值发生变化的时候,即在属性的set段中调用OnPropertyChanged函数,那么就能通知UI,绑定数据源发生了变化,UI也会自动更新数据显示。那么如何实现绑定呢,看看下面代码:
<Window x:Class="Views.Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:ViewModels"
xmlns:View="clr-namespace:Views"
xmlns:c="clr-namespace:Commands">
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModel:MainViewModel}">
<View:MainView/>
</DataTemplate>
</Window.Resources>
上面代码的意思是,MainViewModel中定义各种数据源和代码逻辑,如后这些数据按照MainView中所定义的布局进行显示,这也就是DataTemplate的作用,这里不展开(后续将在数据模板中介绍)。
好了,现在把两个类:MainViewMode(l数据,普通cs文件),MainView(UI,即一个xaml文件,通常该类为一个Usrcontrol)进行了绑定,那么具体的数据怎么实现绑定呢。简单,看看MainView中的代码:
<ListBox Grid.Column="0" Name="topiclist" ItemsSource="{Binding Path=DataToShow}">
这样就实现了,具体数据的绑定。
3. 进一步分析
(1)绑定分析:
首先定义一个抽象基类BaseViewModel实现INotifyProperChanged接口;定义MainViewModel继承自
BaseVIewModel,这样就能使用PropertyChange函数,当属性值发生变化的时候,在set段调用PropertyChange函
数。
其次,定义好MainView文件,该文件定义界面布局,实现UI,通过绑定MainViewModel中数据。
最后在Window.xaml中使用DataTemplate将MainViewModel和MainView进行绑定
注意在WPF中,xaml
和xam.cs文件是自动绑定的,但是MainViewModel是普通的cs文件,不是xaml.cs文件,因此,仅仅在MainView中使用绑定,
系统不会再MainViewModel中去寻找数据源,,而是在MainView.xaml.cs中去寻找数据。因此需要最后一步。
(2)为什么不在xaml.cs中定义数据源和逻辑代码?
原因1:xaml.cs是控件的逻辑文件,而MainViewModel需要继承INotifyPropertyChange接口,这样就必须让控
件继承INotifyPropertyChanged,相当于是控件重写了,这样的编程模式,xaml.cs将越来越大,这个类的测试也将越发复杂。因
此,从降低类复杂度的角度,不应在xaml.cs中定义数据源和逻辑代码。
原因2:如果在xaml.cs中实现逻辑,,不利于逻辑和UI的分离,不利于UI和逻辑的分开编写,降低的程序编写的效率,同时增加了代码的耦合
度。采用MainViewModel和
MainView的框架,其实就是MVVM模式(Model-View-ViewModel),该模式可以说和WPF是珠联璧合,等我陆续阐述完,各种基
础后,,我将在WPF进阶之MVVM中详细说明,现在请大家,耐心掌握基础。
(3)看刚才的例子,ListBox的ItemsSource通常需要一个集合类型数据,好了,我们知道ObservableCollection
是一个数据集合类型,并且实现了INotifyPropertyChanged接口,那么为什么不绑定到一个ObservableCollection数
据类型呢?
原因:说的很对通常绑定到ObservableCollection是可行的,绑定到普通数据,并实现
INotifyPropertyChanged也是可行的,。他们的区别在于ObservableCollection继承
INotifyCollectionChanged, INotifyPropertyChanged那么当Collection添加项,删除项,刷新时,都将发送PropertyChanged通知,有时这部分功能是我们不需要的,因此,采用自己实现INotifyPropertyChanged的类将更具灵活性。
ICommand
定义:
大家知道在xaml中定义的事件,响应函数只能在xaml.cs中,如上所述,如果我们采用MVVM框架,那么我们不能通过事件响应的模式,实现代码逻辑。那么如何监听事件呢。我们使用命令。且看下面实现
//不带参数的命令类型
public class DelegateCommand : ICommand
{
public DelegateCommand(Action executeMethod) : this(executeMethod, null, false)
{
}
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
: this(executeMethod, canExecuteMethod, false) {
}
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}
public bool CanExecute()
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod();
}
return true;
}
public void Execute()
{
if (_executeMethod != null)
{
_executeMethod();
}
}
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if (_isAutomaticRequeryDisabled != value)
{
if (value)
{
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{