WPF进阶之接口(3):INotifyPropertyChanged,ICommand
INotifiPropertyChanged . 作用:向客户端发出某一属性值已更改的通知。该接口包含一个PropertyChanged事件成员(MSDN的解释)
INotifyPropertyChanged 接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知。
例如,考虑一个带有名为 FirstName 属性的 Person 对象。若要提供一般性属性更改通知,则 Person 类型实现NotifyPropertyChanged 接口并在 FirstName 更改时引发 PropertyChanged 事件。若要在将客户端与数据源进行绑定时发出更改通知,则绑定类型应具有下列任一功能: A. 实现 INotifyPropertyChanged 接口(首选)。 B. 为绑定类型的每个属性提供更改事件。 . 使用: 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="" Name="topiclist" ItemsSource="{Binding Path=DataToShow}"> 这样就实现了,具体数据的绑定。 . 进一步分析 ()绑定分析: 首先定义一个抽象基类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中去寻找数据。因此需要最后一步。 ()为什么不在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中详细说明,现在请大家,耐心掌握基础。 ()看刚才的例子,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
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
} public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
} protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
} public event EventHandler CanExecuteChanged
{
add
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested += value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, );
}
remove
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested -= value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
}
} bool ICommand.CanExecute(object parameter)
{
return CanExecute();
} void ICommand.Execute(object parameter)
{
Execute();
} private readonly Action _executeMethod = null;
private readonly Func<bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List<WeakReference> _canExecuteChangedHandlers; } public class DelegateCommand<T> : ICommand
{ //篇幅限制,不做展开,这里和前一个DelegateCommand类似,不过是定义一个带参数的命令
} //采用弱引用,避免内存泄漏 internal class CommandManagerHelper
{
internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
{
if (handlers != null)
{
// Take a snapshot of the handlers before we call out to them since the handlers
// could cause the array to me modified while we are reading it. EventHandler[] callees = new EventHandler[handlers.Count];
int count = ; for (int i = handlers.Count - ; i >= ; i--)
{
WeakReference reference = handlers[i];
EventHandler handler = reference.Target as EventHandler;
if (handler == null)
{
// Clean up old handlers that have been collected
handlers.RemoveAt(i);
}
else
{
callees[count] = handler;
count++;
}
} // Call the handlers that we snapshotted
for (int i = ; i < count; i++)
{
EventHandler handler = callees[i];
handler(null, EventArgs.Empty);
}
}
} internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
EventHandler handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested += handler;
}
}
}
} internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
EventHandler handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested -= handler;
}
}
}
} internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
{
AddWeakReferenceHandler(ref handlers, handler, -);
} internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
{
if (handlers == null)
{
handlers = (defaultListSize > ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
} handlers.Add(new WeakReference(handler));
} internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
{
if (handlers != null)
{
for (int i = handlers.Count - ; i >= ; i--)
{
WeakReference reference = handlers[i];
EventHandler existingHandler = reference.Target as EventHandler;
if ((existingHandler == null) || (existingHandler == handler))
{
// Clean up old handlers that have been collected
// in addition to the handler that is to be removed.
handlers.RemoveAt(i);
}
}
}
}
}
} 上面的代码不必深究,以后使用多了自然明白,这里不做展开。 定义一个带参数命令 DelegateCommand<object> newsItemLoopCommand;
public ICommand NewsItemLoopCommand
{
get
{
if (newsItemLoopCommand == null)
{
Action<object> exe = new Action<object>(NextOrPreviousNews);
newsItemLoopCommand = new DelegateCommand<object>(exe);
}
return newsItemLoopCommand;
}
} 我们将Button的Command绑定到此命令,并且传递一个参数: <Button x:Name="BackButton" Command="{Binding Path=ItemLoopCommand}" CommandParameter="Previous"/> 这样当click Button时,就会执行NextOrPreviousNews函数。 问题: . 基本上只有少数控件(如Button)在带有Command,多数控件没有Command属性,怎么使用命令呢。 我们知道有些时间发生后,属性值也会变化,例如ListBox 的SelectionChanged事件发生时,SelectedItem也将产生变化,那么我们可以将SelectedItem绑定一个后台属性PropertyItem,当SelectionChanged发生时变化,SelectedItem也变,由于绑定,PropertyItem也变,因此我们可以在PropertyItem的set段中加入逻辑代码,达到我们的目的。 .这里我们遗留了几个问题。MVVM的讲解,ICommand实现的讲解,弱引用的讲解。 ICommand实现的讲解看看MSDN便知道,主要是实现CanExcute和Excute两个函数。 弱引用,我将在下一篇中介绍。 关于MVVM请等慢慢讲完所有基础。包括:控件重写,数据绑定等内容。
转自:http://hi.baidu.com/leo_han/item/fe0eec6f54217e0da0cf0f0a
WPF进阶之接口(3):INotifyPropertyChanged,ICommand的更多相关文章
- WPF进阶之接口(4):ICommand实现详解
上一章WPF进阶之接口():INotifyPropertyChanged,ICommand中我们遗留了几个问题,我将在本节中做出解释.在详细解释ICommand实现之前,我们现在关注一下什么是:弱引用 ...
- WPF进阶之接口(1):IValueConverter,IMultiValueConverter
看一个例子,FontFamily="Trebuchet MS, GlobalSansSerif.CompositeFont" .这样一条简单的语句,熟悉WPF的人在xaml中可能经 ...
- WPF进阶之接口(2):IDisposable,ICollectionView
废话不多说,进入正题,先来说说IDisposable,看例子(来自MSDN): using System; using System.ComponentModel; // 下面的例子将展示一个实施了I ...
- WPF使用IDataErrorInfo接口进行数据校验 - 简书
原文:WPF使用IDataErrorInfo接口进行数据校验 - 简书 class ValidationBindableBase : BindableBase, IDataErrorInfo { pu ...
- WPF进阶教程 - 使用Decorator自定义带三角形的边框
原文:WPF进阶教程 - 使用Decorator自定义带三角形的边框 写下来,备忘. Decorator,有装饰器.装饰品的意思,很容易让人联想到设计模式里面的装饰器模式.Decorator类负责包装 ...
- WPF进阶技巧和实战03-控件(3-文本控件及列表控件)
系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...
- WPF进阶技巧和实战03-控件(4-基于范围的控件及日期控件)
系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...
- WPF之MVVM(Step1)——自己实现ICommand接口
开发WPF应用程序,就不得不提MVVM.下面偶将展示MVVM中简单的实现,其中主要在于ICommand的实现上,不过这种实现方式,应该不会有多少人在开发中使用,在此仅作学习使用. 准备: 界面绘制,简 ...
- wpf 属性变更通知接口 INotifyPropertyChanged
在wpf中将控件绑定到对象的属性时, 当对象的属性发生改变时必须通知控件作出相应的改变, 所以此对象需要实现 INotifyPropertyChanged 接口 例: //实现属性变更通知接口 INo ...
随机推荐
- spring各种邮件发送
参考地址一 参考地址二 参考地址三 参考地址四 Spring邮件抽象层的主要包为org.springframework.mail.它包括了发送电子邮件的主要接口MailSender,和值对象Simpl ...
- 关于RDS备份文件使用wget下载提示403 Forbidden的情况
关于RDS备份文件使用wget下载提示403 Forbidden的情况 使用wget下载提示403错误当我们RDS物理备份文件时,例如: 原因: URL中包含有特殊字符比如&,从而造成URL被 ...
- RBAC权限管理(转)
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用户-角色- ...
- java工具类之按对象中某属性排序
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang ...
- [转]IIS6 伪静态 IIS文件类型映射配置方法 【图解】
1.右键点击 要设置网站的网站 2.属性 -->主目录 -->配置--> 3.如右侧窗口,找到 .aspx 扩展名-->编辑-->复制 可执行文件的路径-->关闭 ...
- Java数据结构和算法(二):数组
上篇博客我们简单介绍了数据结构和算法的概念,对此模糊很正常,后面会慢慢通过具体的实例来介绍.本篇博客我们介绍数据结构的鼻祖——数组,可以说数组几乎能表示一切的数据结构,在每一门编程语言中,数组都是重要 ...
- 使用阿里云Docker镜像加速
使用docker官方的docker hub速度太慢,正好看到国内阿里云也做了docker镜像,于是想试试看阿里云的docker源.先附上 阿里云docker hub地址 .新用户需要注册成为开发者.打 ...
- centos5.5 快速安装mysql
安装MySQL. [root@sample ~]# yum -y install mysql-server ← 安装MySQL[root@sample ~]# yum -y install php-m ...
- Ruby入门笔记
Ruby入门笔记 一切皆为对象 “Hello”.length 方法 定义:def开头 end结尾 命名一般采用下划线分隔单词
- JSON 之GSON 解析
一. 谷歌GSON这个Java类库可以把Java对象转换成JSON,也可以把JSON字符串转换成一个相等的Java对象.Gson支持任意复杂Java对象包括没有源代码的对象. 二.Gson解析Json ...