1. 程式人生 > >WPF 資料繫結詳解。。。

WPF 資料繫結詳解。。。

資料繫結

控制元件之間的繫結


繫結源 source  資料提供者
繫結物件 target  接受資料的物件,被繫結的物件

對 繫結物件 進行資料繫結,將其的屬性,和繫結源的某個屬性進行繫結

純 xaml 繫結

   < Grid >
         < TextBox  Name ="tbxValue"  Width ="100"  Height ="30"  Text ="{ Binding  ElementName =slvolum,  Path =Value}"></ TextBox >
         < Slider  Name ="slvolum"  Width ="105"  Height ="20"  Minimum ="1"  Maximum ="100"  Margin ="0,100,0,0"></ Slider >      
   </ Grid >

這裡面,TextBox 為繫結物件 ,他的 Text 屬性繫結 到 繫結源 Slider 的 Value 屬性上。只需要設定 繫結源 的 Element 和 Path 屬性的繫結

後臺繫結

這段程式碼,去掉  xaml 的繫結語法,後臺設定如下 

 < Grid >
     < TextBox  Name = "tbxValue"   Width = "100"   Height = "30"   Text = "{ Binding  ElementName =slvolum,  Path =Value}" > < / TextBox >
     < Slider  Name = "slvolum"   Width = "105"   Height = "20"   Minimum = "1"   Maximum = "100"   Margin = "0,100,0,0" > < / Slider >       
 < / Grid >
 
  
   public  TDataBlind()
   {
      InitializeComponent();         
                      
      Binding  bding =  new   Binding ();                          // 設定繫結關係
      bding.Source = slVolum;                                      // 設定繫結源
      bding.Path =  new   PropertyPath ( "Value" );                // 設定繫結屬性 
      tbxValue.SetBinding( TextBox .TextProperty, bding);          // 新增幫頂關係和繫結物件


      // tbxValue.SetBinding(TextBox.TextProperty, new Binding("Value") { Source = slVolum });  // 等價於上面的設定
    }

前臺 xaml 和 後臺 資料物件繫結

< StackPanel >
         < TextBox  Name ="tbName"  Width ="100"  Height ="30"></ TextBox >
         < TextBox  x : Name ="tbAge"  Width ="100"  Height ="30"></ TextBox >
     </ StackPanel >
 
    public   partial   class   TDataBlind  :  Window
    {
         public  TDataBlind()
        {
            InitializeComponent();


            Language  lg =  new   Language () { Name =  "python" , Age = 12 };             // 例項物件


            Binding  bdname =  new   Binding ();
            bdname.Source = lg;
            bdname.Path =  new   PropertyPath ( "Name" );
            tbName.SetBinding( TextBox .TextProperty, bdname);    
            // // tbName.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = lg });


            Binding  bdage =  new   Binding ();
            bdage.Source = lg;
            bdage.Path =  new   PropertyPath ( "Age" );
            tbAge.SetBinding( TextBox .TextProperty, bdage);
            // // tbAge.SetBinding(TextBox.TextProperty, new Binding("Age") { Source = lg });
        }
    }
     class   Language
    {
         public   string  Name {  get ;  set ; }
         public   int  Age {  get ;  set ; }    
    }


xmal 屬性拓展和 後臺物件資料繫結


     < StackPanel  Name ="stLanguage">
             < TextBox  Name ="tbName"  Width ="100"  Height ="30"  Text ="{ Binding  Name }"></ TextBox >
             < TextBox  x : Name ="tbAge"  Width ="100"  Height ="30"  Text ="{ Binding  Age }" ></ TextBox >          
     </ StackPanel >
 
     public   partial   class   TDataBlind  :  Window
    {
         public  TDataBlind()
        {
             InitializeComponent();


             Language  lg =  new   Language () { Name =  "python" , Age = 12 };   // 例項物件


             stLanguage.DataContext = lg;                                         // 設定資料上下文 (設定繫結)   
        }
    }
     class   Language
    {
         public   string  Name {  get ;  set ; }
         public   int  Age {  get ;  set ; }   
    }

多級繫結

< StackPanel >
             < TextBox  Name ="txtCountry"  Width ="100"  Height ="30"></ TextBox >
             < TextBox  Name ="txtProvince"  Width ="100"  Height ="30"></ TextBox >
             < TextBox  Name ="txtCity"  Width ="100"  Height ="30"></ TextBox >          </ StackPanel >
 
  public   partial   class   TDataBlind  :  Window
    {
         public  TDataBlind()
        {
            InitializeComponent();
             City  city =  new   City () { Name =  "QingDao"  };
             Province  province =  new   Province () { Name =  "Shangdong" , CityList =  new   List < City > { city } };
             Country  country =  new   Country () { Name =  "China" , ProvinceList =  new   List < Province > { province } };
 
            txtCountry.SetBinding( TextBox .TextProperty,  new   Binding ( "Name" ) { Source = country });
            txtProvince.SetBinding( TextBox .TextProperty,  new   Binding ( "Name" ) { Source = province });
            txtCity.SetBinding( TextBox .TextProperty,  new   Binding ( "Name" ) { Source = city });
 




            //txtCountry.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = country });                     // 表示 country 資料來源 下的 Name 屬性
            //txtProvince.SetBinding(TextBox.TextProperty, new Binding("ProvinceList/Name") { Source = country });       // 表示 country 資料來源 下的 ProvinceList 下的 Name 屬性
            //txtCity.SetBinding(TextBox.TextProperty, new Binding("ProvinceList/CityList/Name") { Source = country });  // 表示 country 資料來源 下的 ProvinceList 下的  CityList 下的 Name 屬性
        }
    }
     class   City
    {
         public   string  Name {  get ;  set ; }
    }
     class   Province
    {
         public   string  Name {  get ;  set ; }
         public   List < City > CityList {  get ;  set ; }
    }
     class   Country
    {
         public   string  Name {  get ;  set ; }
         public   List < Province > ProvinceList {  get ;  set ; }     
    }

前臺多級資料繫結 


    < StackPanel  Name ="spCountry">
       < TextBox  Name ="txtCountry"  Width ="100"  Height ="30"  Text ="{ Binding  Name }"></ TextBox >
       < TextBox  Name ="txtProvince"  Width ="100"  Height ="30"  Text ="{ Binding  ProvinceList / Name}"></ TextBox >
       < TextBox  Name ="txtCity"  Width ="100"  Height ="30"  Text ="{ Binding  ProvinceList / CityList / Name}"></ TextBox >
    </StackPanel >
 
    City  city =  new   City () { Name =  "QingDao"  };
    Province  province =  new   Province () { Name =  "Shangdong" , CityList =  new   List < City > { city } };
    Country  country =  new   Country () { Name =  "China" , ProvinceList =  new   List < Province > { province } };


    spCountry.DataContext = country;    // 資料上下文

集合繫結


ItemsSource 設定的時候,ListBox 裡面必須為空

<ListBox x:Name="lb" ItemsSource="{Binding}"></ListBox>


List<string> strlist = new List<string>{"python", "ruby", "lisp"};
lb.DataContent = strlist;

< ListBox  x : Name ="lb"></ ListBox >
 
             List < Language > strlist =  new   List < Language >();
            strlist.Add( new   Language () { Name =  "python" , Age = 12 });
            strlist.Add( new   Language () { Name =  "perl" , Age = 16 });
            strlist.Add( new   Language () { Name =  "php" , Age = 14 });
 
            lb.ItemsSource = strlist;              // 設定繫結關係             
            lb.DisplayMemberPath =  "Name" ;         // 顯示繫結的屬性


如果 設定資料上下文,則前臺需要手動指定 itermsSource 繫結。當繫結為空的時候,其實就是繫結自身的 DataContent

< ListBox  x : Name ="lb"  ItemsSource ="{ Binding }"></ ListBox >
 
            List < Language > strlist =  new   List < Language >();
            strlist.Add( new   Language () { Name =  "python" , Age = 12 });
            strlist.Add( new   Language () { Name =  "perl" , Age = 16 });
            strlist.Add( new   Language () { Name =  "php" , Age = 14 });
 
            lb.ItemsSource = strlist;             
            lb.DisplayMemberPath =  "Name" ;

集合繫結中,如果需要填寫 集合裡面的控制元件繫結,設定 itemsSource的時候,集合空間裡面必須是空的,也就是必須使用 資料模版

       < ListBox  Name ="lb">
             < ListBox.ItemTemplate >
                 < DataTemplate >
                     < StackPanel >
                         < TextBox  Width ="100"  Height ="30"  Text ="{ Binding  Name }"></ TextBox >
                         < TextBox  Width ="100"  Height ="30"  Text ="{ Binding  Age }"></ TextBox >
                     </ StackPanel >
                 </ DataTemplate >
             </ ListBox.ItemTemplate >
         </ ListBox >


            List < Language > strlist =  new   List < Language >();
            strlist.Add( new   Language () { Name =  "python" , Age = 12 });
            strlist.Add( new   Language () { Name =  "perl" , Age = 16 });
            strlist.Add( new   Language () { Name =  "php" , Age = 14 });
             //lb.DataContext = strlist;             
            lb.ItemsSource = strlist;

繫結型別轉換

繫結型別轉換需要實現一個 IValueConverter 的介面,裡面有兩個方法 

        // 引數:
        //   value:
        //     繫結源生成的值。
        //   targetType:
        //     繫結目標屬性的型別。
        //   parameter:
        //     要使用的轉換器引數。
        //   culture:
        //     要用在轉換器中的區域性。
        // 返回結果:
        //     轉換後的值。 如果該方法返回 null,則使用有效的 null 值。
        object Convert(object value, Type targetType, object parameter, CultureInfo culture);
        // 引數:
        //   value:
        //     繫結目標生成的值。
        //   targetType:
        //     要轉換到的型別。
        //   parameter:
        //     要使用的轉換器引數。
        //   culture:
        //     要用在轉換器中的區域性。
        // 返回結果:
        //     轉換後的值。 如果該方法返回 null,則使用有效的 null 值。
        object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);




namespace  WpfAppTest
{
     ///   <summary>
     ///  TDataConverter.xaml 的互動邏輯
     ///   </summary>
     public   partial   class   TDataConverter  :  Window
    {
         public  TDataConverter()
        {
            InitializeComponent();
        }
    }
     public   class   boolToVisibily  :  IValueConverter
    {
         public   object  Convert( object  value,  Type  targetType,  object  parameter, System.Globalization. CultureInfo  culture)
        {
             bool  b = ( bool )value;
             if  (b)
            {
                 return   Visibility .Visible;
            }
             else
            {
                 return   Visibility .Hidden;
            }
        }
         public   object  ConvertBack( object  value,  Type  targetType,  object  parameter, System.Globalization. CultureInfo  culture)
        {
             return   true ;
        }
    } 
}

前臺

< Window  x : Class ="WpfAppTest.TDataConverter"
         xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns : x ="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns : ivalueconvert ="clr-namespace:WpfAppTest"
         Title ="TDataConverter"  Height ="407"  Width ="528">
     < Grid >


         < StackPanel >
             < StackPanel.Resources >
                 < ivalueconvert : boolToVisibily  x : Key ="ivalconvert" />
             </ StackPanel.Resources >
             < Image  Name ="imgShow"   Source ="media/free.png"  Stretch ="None"  Visibility ="{ Binding  ElementName =ckbConImg,  Path =IsChecked,  Converter ={ StaticResource  ResourceKey =ivalconvert}}"></ Image >
             < CheckBox  Name ="ckbConImg"  Width ="30"  Margin ="30"></ CheckBox >
         </ StackPanel >
     </ Grid > </ Window >




< Image  Name = "imgShow"    
         Source = "media/free.png"   
         Stretch = "None"   
         Visibility = "{ Binding  ElementName =ckbConImg,  Path =IsChecked,  Converter ={ StaticResource  ResourceKey =ivalconvert}}" >
< / Image >

將名字空間的 WpfAppTest 對映 到 ivalueconvert,然後將這個名稱空間下的 class boolToVisibily  寫入 StackPanel 的 Resources 

然後 繫結物件 Image 設定 其 Visibility 屬性的繫結 

{ Binding  ElementName =ckbConImg,  Path =IsChecked,  Converter ={ StaticResource  ResourceKey =ivalconvert}}

多向繫結


多向繫結 需要在 xaml 裡設定 mode 方式為 TwoWay。然後在繫結的資料來源的物件 繼承實現一個 INotifyPropertyChanged 。實現其 PropertyChanged 事件。在 PropertyChanged 事件中指定 繫結源的發生變化的屬性

< StackPanel  Name ="sp">
     < TextBox  Name ="txbName"  Width ="100"  Height ="30"  Text ="{ Binding  Name ,  Mode =TwoWay}"></ TextBox >
     < Button  Name ="btnShow"  Width ="100"  Height ="30"  Click ="btnShow_Click"> show </ Button >
     < Button  Name ="btnShowName"  Width ="100"  Height ="30"  Click ="btnShowName_Click"> btnShowName </ Button >
     < Button  Name ="btnChange"  Width ="100"  Height ="30"  Click ="btnChange_Click"> change </ Button >
</ StackPanel >


public   partial   class   TDatachanged  :  Window
{
    Student  lg =  new   Student () { Name =  "python"  };   // 設定資料上下文
 
    public  TDatachanged()
    {
        InitializeComponent();
        sp.DataContext = lg;
    }
    private   void  btnShow_Click( object  sender,  RoutedEventArgs  e)
    {
        MessageBox .Show(txbName.Text);
    }
    private   void  btnChange_Click( object  sender,  RoutedEventArgs  e)
    {
        string  time =  DateTime .Now.Millisecond.ToString();
        lg.Name = time;
        MessageBox .Show(time);      
    }
    private   void  btnShowName_Click( object  sender,  RoutedEventArgs  e)
    {
        MessageBox .Show(lg.Name);
    }      
}     
 
 
class   Student  :  INotifyPropertyChanged                           // 繼承介面
{
     private   string  name;
     public   string  Name
     {
         get
         {
            return  name;
         }
         set
         {
            name =  value ;
            if  (PropertyChanged !=  null )                // 如果 PropertyChanged 繫結關係發生
            {
                // 觸發 PropertyChanged 事件, 並監聽 繫結源物件的 變動屬性
                PropertyChanged( this ,  new   PropertyChangedEventArgs ( "Name" ));
             }
          }
      }
      public   event   PropertyChangedEventHandler  PropertyChanged;            // 宣告委託
}


原始碼下載   https://github.com/rsj217/WPFTest