WPF知識框架

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);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章