WPF知識點:
A、樣式 B、觸發器 http://www.cnblogs.com/libaoheng/archive/2011/11/20/2255963.html
引用外部樣式 http://www.cnblogs.com/therock/articles/2135997.html
http://blog.csdn.net/waitforfree/article/details/8723146
C、資源
http://www.cnblogs.com/lzhp/archive/2012/09/27/2703481.html
http://blog.csdn.net/fwj380891124/article/details/8153229
D、模板 http://www.cnblogs.com/dingli/archive/2011/07/20/2112150.html
E、數據綁定
F、行爲
G、轉換器
H、動畫
http://www.cnblogs.com/libaoheng/archive/2012/04/23/2466242.html
1、爲什麼要使用INotifyPropertyChanged?
{地址來源:http://www.cnblogs.com/bcmeng/p/3966931.html
http://blog.csdn.net/cselmu9/article/details/8275255 }
2、爲什麼要使用ObservableCollection<T>?
{地址來源:http://blog.csdn.net/swarb/article/details/7945755 }
一、何時實現INotifyPropertyChanged接口
官方解釋:INotifyPropertyChanged 接口用於向客戶端(通常是執行綁定的客戶端)發出某一屬性值已更改的通知。官方解釋的很模糊,估計是個人看了都不知道到底什麼時候需要實現INotifyPropertyChanged接口.小夢通過實際測試給出明確結論:
首先:OneTime模式:毫無意義,因爲它的綁定只有初始時候綁定一次,根本談不上改變!自然也就談不上實現INotifyPropertyChanged接口.
然後是OneWay模式:我們知道OneWay模式的含義是:綁定源的每一次變化都會通知綁定目標,但是綁定目標的改變不會改變綁定源.當綁定源的數據實體類沒有實現INotifyPropertyChanged接口時,當我們改變了數據源,我們會發現綁定目標的UI上的相應的數據不會立即變化.所以這時候就需要我們來實現INotifyPropertyChanged接口.
最後是TwoWay模式:在TwoWay模式下,當綁定源的數據實體類沒有實現INotifyPropertyChanged接口時,我們發現.控件的更改會讓數據源立即發改變,但是改變數據源,綁定目標控件卻不會立即發生改變!所以當我們需要數據源改變時相對應的UI立即改變時,就需要實現INotifyPropertyChanged接口.
總之:就是當數據源改變並需要UI立即改變時我們需要實現INotifyPropertyChanged接口.
我們可以通過這個示例來明確的體會這一點:
<StackPanel> <TextBox Header="編號" Text="{Binding ID,Mode=OneTime}" Name="tbxID" ></TextBox> <TextBox Header="書名" Text="{Binding Title,Mode=OneWay}" Name="tbxTitle" ></TextBox> <TextBox Header="價格" Text="{Binding Price,Mode=TwoWay}" Name="tbxPrice" ></TextBox> <Button Content="通過數據源修改控件的值" Click="Button_Click"></Button> <Button Content="直接修改控件的值" Click="Button_Click_1" /> <Button Content="通過控件修改數據源的值" Click="Button_Click_2" /> </StackPanel>
後臺代碼:
namespace INotifyPropertyChangedDEMO { /// <summary> /// 可用於自身或導航至 Frame 內部的空白頁。 /// </summary> public sealed partial class MainPage : Page { Book book = new Book(); public MainPage() { this.InitializeComponent(); this.NavigationCacheMode = NavigationCacheMode.Required; book.ID = 0; book.Title = "ASP.NET 開發手冊"; book.Price = 40; st.DataContext = book; } private void Button_Click(object sender, RoutedEventArgs e)//通過修改數據源修改控件的值 { book.ID = 100; book.Price = 50; book.Title = "SL開發手冊"; } private async void Button_Click_1(object sender, RoutedEventArgs e)//顯示數據源的值 { await new MessageDialog(book.ID.ToString() + " " + book.Title.ToString() + " " + book.Price.ToString()).ShowAsync(); } public class Book : INotifyPropertyChanged //INotifyPropertChanged 接口定義了一個當屬性值更改時執行的事件,事件名稱爲PropertyChanged。 //這個是在繼承這個接口的類必須要實現的事件 { private int _id; public int ID { get { return _id; } set { _id = value; //NotifyPropertyChange("ID"); } } private string _title; public string Title { get { return _title; } set { _title = value; //NotifyPropertyChange("Title"); } } private double _price; public double Price { get { return _price; } set { _price = value; //NotifyPropertyChange("Price"); } } public event PropertyChangedEventHandler PropertyChanged; //PropertyChangedEventArgs類型,這個類用於傳遞更改值的屬性的名稱,實現向客戶端已經更改的屬性發送更改通知。屬性的名稱爲字符串類型。 private void NotifyPropertyChange(string propertyName) { if (PropertyChanged != null) { //根據PropertyChanged事件的委託類,實現PropertyChanged事件: PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } } }
大家運行這個示例可以明顯體會INotifyPropertyChanged接口的作用.
如何實現INotifyPropertyChanged接口
上面示例的INotifyPropertyChanged接口的實現方式是最常見和最普遍的.
我們可以利用CallerMemberNameAttribute特性來簡化一下,這個特性可以根據調用方來決定傳入哪個屬性的名字.:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { var eventHandler = this.PropertyChanged; if (eventHandler != null) eventHandler(this, new PropertyChangedEventArgs(propertyName)); }
這樣我們在調用時可以這樣調用:
NotifyPropertyChange("ID") 改爲:OnPropertyChanged();
INotifyPropertyChanged接口的最佳實現方式:
這個所謂的最佳實現方式 是channel 9的視頻中說的,實現方式如下:
public class ModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) { if (object.Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; } protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { var eventHandler = this.PropertyChanged; if (eventHandler != null) eventHandler(this, new PropertyChangedEventArgs(propertyName)); } }
相應的調用方式進一步簡化:
private string name; public string Name { get { return name; } set { this.SetProperty(ref this.name, value); } }
二、爲什麼是用ObservableCollection<T>?
ist<T>與ObservableCollection<T>的用法基本上是一樣的。
區別:
list<T>:
當T繼承於INotifyPropertyChanged時,如果list<T>中的屬性發生改變,則通知UI屬性值已發生改變。但當list<T>添加一項時,list<T>就無法通知前端UI了(此時,ObservableCollection<T>閃亮登場)。
ObservableCollection<T>:
當ObservableCollection<T>添加一行時,會自動通知綁定該ObservableCollection<T>的控件並做相應修改。如果希望當ObservableCollection<T>中的屬性發生改變時通知UI,則T也需要繼承於INotifyPropertyChanged。
/////////////////////////////////////////////////////////////////////////////////
MSDN中說ObservableCollection是一個動態的數據集合,在添加項、移除項或刷新整個列表的時候,此集合將提供通知。我是在WPF中用了這個,但是我在網上找資料的時候發現,有在WinForm中也用到的,我並沒有去驗證,使用方式應該一樣吧,只是WPF中是提供與前臺UI界面中的控件進行綁定操作時通知更新的,WinForm中怎麼做就不知道了。
這個,上面MSDN那麼說了,但是按照我的理解就是,ObservableCollection數據集中的條目有變動,就出發通知流程,引發更新操作。這是一個泛型類,ObservableCollection<T>的形式,我之前有一個例子,是使用ListBox做柱狀圖的,裏面用到了ObservableCollection,裏面提到一個ReptClass類,作爲數據基類,ObservableCollection創建數據源,與前臺的ListBox進行綁定,顯示柱狀圖。可以參考《WPF-用ListBox做簡單的柱狀圖》,這次還是用柱狀圖中的類做例子。
我今天上午一直思考一個問題,就是ObservableCollection集合中的單個條目值改變前臺UI會不會更新,按照MSDN上的說法是會自動更新的,但是我試了一下,發現,只有增加條目,刪除條目的時候會引發通知,當更改條目總某個值得時候並沒有引發通知。
但是當我設置斷點後發現,值確實改變了,只是UI並沒有更新,現在的問題是,增加項、刪除項,會引發通知;條目值發生變化,未進行通知。
我以爲是我的寫法有問題,我又查閱了一下資料,順便看了一下今年夏天那個項目中我是如何操作的,然後發現了一個不同,就是作爲基類的ReptClass,原來的寫法是:
//數據類
public class ReptClass
{
public int count { set; get; }//數量
public Brush color { set; get; }//顏色
public string mouth { set; get; }//月份
public int height { get { return count * 3; } }//高度
}
但是,這裏需要提到一個接口:INotifyPropertyChanged,MSDN中說他是向客戶端發出某一屬性已更改的通知。在MVVM框架中也用到了這樣的組合方式。
於是我讓上面的類繼承此接口:
//數據類
public class ReptClass : INotifyPropertyChanged
{
private int _count;
public int count
{
set
{
_count = value;
this.Changed("count");
this.Changed("height");
}
get { return _count; }
}//數量
public Brush color { set; get; }//顏色
public string mouth { set; get; }//月份
public int height
{
get { return count * 3; }
}//高度
#region 屬性更改通知
public event PropertyChangedEventHandler PropertyChanged;
private void Changed(string PropertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
#endregion
}
通過上面這樣的更改,當我再次對條目進行更改的時候,UI就更新了。
比如,我要爲每一項的count都加1,我可以這樣寫:
foreach (var item in listboxSource)
{
item.count += 1;
}
或者進行下面這樣的操作
//移除mouth=十二月的條目
listboxSource.Remove(listboxSource.FirstOrDefault(t => t.mouth == "十二月"));
//移除第一個
listboxSource.RemoveAt(0);
//移除最後一個
listboxSource.RemoveAt(listboxSource.Count - 1);
//將第一個移動到最後
listboxSource.Move(0, listboxSource.Count - 1);