我想大部分的WPF和SL开发者都应该对INotifyPropertyChanged这个接口再熟悉不过了。当我们向UI传递属性变化的通知并更新客户端UI时就必须应用到它。(这里插一句,当一个集合中的项改变时我们则需要使用ObservableCollection<T>泛型集合)
假设我们有一个叫做MenuButtonModel的类,其中一个属性为HasFocus,那么通常情况下我们会这样实现INotifyPropertyChanged接口
【补充】 INotifyPropertyChanged.PropertyChanged事件,它什么时候为空不为空??
控件绑定数据源时,控件会自动订阅实现了INotifyPropertyChanged接口的对象的PropertyChanged事件。有人订阅了它就不再是null了。
如果它谁都没绑定,那肯定是null了。一般Binding之后就不空了.也就是说,event 用 += 操作之后, 他就不为null了,因为控件绑定数据源时会自动订阅实现了INotifyPropertyChanged接口的对象的PropertyChanged事件。
假如你不是绑定到控件上,而是另一个(些)对象手动绑定这个对象的PropertyChanged事件,那么哪个时刻开始觉得没必要再监视这个对象的属性的变化,用-=操作取消了订阅,那很显然,PropertyChanged又会变成null.
【补充2】用不用PropertyChanged有什么区别?(不是很明白,请高手指点!)
msdn 是這樣說的:
‧如果不呼叫 PropertyChanged,就無法把控制項執行個體記號為需要儲存的。因此它們將不會收到 WriteProperties 事件,使用控制項的開發人員將遺失在設計階段設定的任何屬性值。
‧因為屬性值可能顯示在多個地方,因此當屬性值發生改變時必須通知開發環境,以便使它能夠同步顯示「屬性」視窗、「屬性頁」對話方塊等位置上的屬性值。
另外建議 initprop 裡使用 m_BackColor = Ambient.BackColor,還有 backcolor 的 get/let 型態使用 ole_color 而不是 long。最後建議在「工具-程式屬性-backcolor-進階-程序識別碼」裡選擇 backcolor。
public
class
MenuButtonModel : INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler PropertyChanged;
private
bool
? _hasFocus;
public
bool
? HasFocus
{
get
{
return
_hasFocus; }
set
{
_hasFocus = value;
if
(PropertyChanged !=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs(
"HasFocus"
))
}
}
}
}
//当这个类的属性比较多的时候(如多了一个IconPath属性),我们又将NotifyPropertyChanged独立出来,如:
public
class
MenuButtonModel : INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler PropertyChanged;
public
void
NotifyPropertyChanged(
string
propertyName)
{
if
(PropertyChanged !=
null
)
{
PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
private
bool
? _hasFocus;
public
bool
? HasFocus
{
get
{
return
_hasFocus; }
set
{
_hasFocus = value;
NotifyPropertyChanged(
"HasFocus"
);
}
}
private
string
_iconPath;
public
string
IconPath
{
get
{
return
_iconPath; }
set
{
_iconPath = value;
NotifyPropertyChanged(
"IconPath"
);
}
}
}
//当我们的类越来越多时,写这样的代码又显得有些疲于奔命。于是乎大家想到了为这些类建立一个共同的基类。所有该基类的派生类自然都实现了IPropertyChanged接口。如下例中的PropertyChangedBase类:
public
class
PropertyChangedBase : INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler PropertyChanged;
public
void
NotifyPropertyChanged(
string
propertyName)
{
if
(
this
.PropertyChanged !=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
}
//然后我们改动MenuButton类,使其继承PropertyChangedBase:
public
class
MenuButtonModel : PropertyChangedBase
{
private
bool
? _hasFocus;
public
bool
? HasFocus
{
get
{
return
_hasFocus; }
set
{
_hasFocus = value;
NotifyPropertyChanged(
"HasFocus"
);
}
}
private
string
_iconPath;
public
string
IconPath
{
get
{
return
_iconPath; }
set
{
_iconPath = value;
NotifyPropertyChanged(
"IconPath"
);
}
}
//现在看起来好多了。不过做为有洁癖的程序员,这样的方法显然不会满足俺们的要求,因为我们仍然需要硬编码一个Name(如:IconPath),这很不爽。即使写错了,编译器也不会发现这个潜在的错误。从成千上万个类中寻找一个硬编码问题会让人抓狂。老赵说过要尽量使用强类型,这绝对是至理名言。。。,那么表达式树正式登场!我们需要稍微的改动一下PropertyChangedBase类(这里注意需要using System.Linq.Expressions):
public
class
PropertyChangedBase : INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler PropertyChanged;
public
void
NotifyPropertyChanged<T>(Expression<Func<T>> propertyName)
{
if
(
this
.PropertyChanged !=
null
)
{
var
memberExpression = propertyName.Body
as
MemberExpression;
if
(memberExpression !=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(memberExpression.Member.Name));
}
}
}
}
//经过如上改动,我们就可以在派生类中使用优雅的强类型啦~,如下:
public
class
MenuButtonModel : PropertyChangedBase
{
private
bool
? _hasFocus;
public
bool
? HasFocus
{
get
{
return
_hasFocus; }
set
{
_hasFocus = value;
NotifyPropertyChanged<
bool
?>(() =>
this
.HasFocus);
}
}
private
string
_iconPath;
public
string
IconPath
{
get
{
return
_iconPath; }
set
{
_iconPath = value;
NotifyPropertyChanged<
string
>(() =>
this
.IconPath);
}
}
}
//看起来还不错。不过<bool?>,<string>依然不是很爽,如果可以this.NotifyPropertyChanged( p => p.HasFocus),那多帅啊。要实现这个目的,我们需要使用扩展方法。搞一个静态类出来,如下:
public
static
class
PropertyChangedBaseEx
{
public
static
void
NotifyPropertyChanged<T, TProperty>(
this
T propertyChangedBase,
Expression<Func<T, TProperty>> expression)
where
T : PropertyChangedBase
{
var
memberExpression = expression.Body
as
MemberExpression;
if
(memberExpression !=
null
)
{
string
propertyName = memberExpression.Member.Name;
propertyChangedBase.NotifyPropertyChanged(propertyName);
}
else
throw
new
NotImplementedException();
}
}
//然后我们就可以在类中用优雅的语法实现PropertyChanged了。
//全部代码如下:
//MenuButton.cs
public
class
MenuButtonModel : PropertyChangedBase
{
private
bool
? _hasFocus;
public
bool
? HasFocus
{
get
{
return
_hasFocus; }
set
{
_hasFocus = value;
this
.NotifyPropertyChanged(p => p.HasFocus);
}
}
private
string
_iconPath;
public
string
IconPath
{
get
{
return
_iconPath; }
set
{
_iconPath = value;
this
.NotifyPropertyChanged(p => p.IconPath);
}
}
}
PropertyChangedBase.cs以及PropertyChangedBaseEx.cs
public
class
PropertyChangedBase : INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler PropertyChanged;
public
void
NotifyPropertyChanged(
string
propertyName)
{
if
(
this
.PropertyChanged !=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
}
public
static
class
PropertyChangedBaseEx
{
public
static
void
NotifyPropertyChanged<T, TProperty>(
this
T propertyChangedBase,
Expression<Func<T, TProperty>> expression)
where
T : PropertyChangedBase
{
var
memberExpression = expression.Body
as
MemberExpression;
if
(memberExpression !=
null
)
{
string
propertyName = memberExpression.Member.Name;
propertyChangedBase.NotifyPropertyChanged(propertyName);
}
else
throw
new
NotImplementedException();
}
}
|
需要注意的是,因为是扩展方法,所有this关键字不能省略。
ok,就到这里,have fun~
本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2011/08/30/2159614.html,如需转载请自行联系原作者