WPF裏分三種Binding:Binding, PriorityBinding, MultiBinding,這三種Binding的基類都是BindingBase,而BindingBase又繼承於MarkupExtensionhtml
提供對綁定定義的高級別訪問,綁定將綁定目標對象(一般爲 WPF 元素)的屬性與任何數據源(例如數據庫、XML 文件或包含數據的任何對象)鏈接起來。數據庫
常見的使用Binding的代碼:
C#windows
Binding binding = new Binding(); // Set source object binding.Source = treeView; // Set source property binding.Path = new PropertyPath("SelectedItem.Header"); // Attach to target property currentFolder.SetBinding(TextBlock.TextProperty, binding);
XAML:性能
<TextBlock x:Name=」currentFolder」 DockPanel.Dock=」Top」
Text=」{Binding ElementName=treeView, Path=SelectedItem.Header}」
Background=」AliceBlue」 FontSize=」16」/>
全部FrameworkElement都包含SetBinding方法:SetBinding(DependencyProperty dp, String path), SetBinding(DependencyProperty dp, BindingBase binding),能夠看出,FrameworkElement中的SetBinding只對DependencyProperty有效。this
另外一種設置Binding的方法是:BindingOperations.SetBinding(currentFolder, TextBlock.TextProperty, binding);
BindingOperations.SetBinding的原型是spa
public static BindingExpressionBase SetBinding( DependencyObject target, DependencyProperty dp, BindingBase binding )
第一個參數是DependencyObject,因此咱們能夠對自定義DependencyObject或者繼承自DependencyObject的類進行綁定。固然第二個參數仍是DependencyProperty。.net
清除Binding:
BindingOperations.ClearBinding(currentFolder, TextBlock.TextProperty); //刪除currentFolder上的TextBlock.TextProperty綁定
BindingOperations.ClearAllBindings(currentFolder); //刪除currentFolder上的全部綁定debug
直接對dependency property賦值也能夠解除binding, 不過只對單向binding有效。 component
有三個屬性用來設置源:ElementName(string)、Source(Object) 和 RelativeSource(RelativeSource)。注:這三個只能指定一個,不然異常。
ElementName: 源爲一個元素(Element),這裏用的是此元素中設置的Name屬性。
Source:以object做爲源。<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
RelativeSource: 源相對於綁定目標的位置。
源是元素自己:{Binding RelativeSource={RelativeSource Self}}
源是Tempalte中元素的Parent:{Binding RelativeSource={RelativeSource TemplatedParent}}
源是綁定以collection形式的前一個數據:{Binding RelativeSource={RelativeSource PreviousData}},MSDN上關於PreviousData的說明並很少,這裏有一篇文章能夠參考
以上三項爲RelativeSource中的Static值,使用這些值能夠減小內存開銷
源是Ancestor(可能比parent還高):{Binding RelativeSource={RelativeSource FindAncestor,
AncestorLevel=n, AncestorType={x:Type desiredType}}}htm
Binding中的Path是 PropertyPath對象。
在最簡單的狀況下,Path 屬性值是要用於綁定的源對象的屬性名稱,如 Path=PropertyName。
經過相似於 C# 中使用的語法,能夠指定屬性的子屬性。例如,子句 Path=ShoppingCart.Order 將綁定設置爲對象的子屬性 Order 或屬性 ShoppingCart。
若要綁定到附加屬性,請將附加屬性用括號括起。例如,若要綁定到附加屬性 DockPanel.Dock,則語法爲 Path=(DockPanel.Dock)。
在應用了索引器的屬性名稱以後的方括號內,能夠指定屬性的索引器。例如,子句 Path=ShoppingCart[0] 將綁定設置爲與屬性的內部索引處理文本字符串「0」的方式對應的索引。此外,還支持多個索引器。
在 Path 子句中能夠同時使用索引器和子屬性,例如,Path=ShoppingCart.ShippingInfo[MailingAddress,Street]。
在索引器內部,能夠有多個由逗號 (,) 分隔的索引器參數。可使用圓括號指定每一個參數的類型。例如,可使用 Path="[(sys:Int32)42,(sys:Int32)24]",其中 sys 映射到 System 命名空間。
若是源爲集合視圖,則能夠用斜槓 (/) 指定當前項。例如,子句 Path=/ 設置到視圖中當前項的綁定。若是源爲集合,則此語法指定默認集合視圖的當前項。
能夠結合使用屬性名和斜槓來遍歷做爲集合的屬性。例如,Path=/Offices/ManagerName 指定源集合的當前項,該源集合包含一樣是集合的 Offices 屬性。其當前項是包含 ManagerName 屬性的對象。
也可使用句點 (.)路徑綁定到當前源。例如,Text=」{Binding}」 等效於 Text=」{Binding Path=.}」。
Binding 類是高級別類。BindingExpression 類是基礎對象,用於保持綁定源與綁定目標之間的鏈接。Binding 中包含可在多個 BindingExpression 對象之間共享的全部信息。也就是說,能夠把一個Binding對象綁定對n個元素上,而針對這n個元素,分別有相應的n個BindingExpresion對象。
Binding能夠直接綁定普通的.net實例,好比int值。可是若是後臺改變int值了,前臺不能顯示改變後的值,這時能夠調用UpdateTarget()方法更新綁定。以下:BindingExpression be = button.GetBindingExpression(Button.ContentProperty);
be.UpdateTarget();
還有UpdateSource方法用來更新源。
上面提到Binding綁到普通的.net屬性,若是source變化了,UI上是不會顯示的,除了用BindingExpression每次顯式更新Target外,還可使用以下技術:
綁定到單個對象需實現INotifyPropertyChanged接口,這個接口只有一個成員:
event PropertyChangedEventHandler PropertyChanged
實現INotifyPropertyChanged的示例以下:
using System.ComponentModel; namespace SDKSample { // This class implements INotifyPropertyChanged // to support one-way and two-way bindings // (such that the UI element updates when the source // has been changed dynamically) public class Person : INotifyPropertyChanged { private string name; // Declare the event public event PropertyChangedEventHandler PropertyChanged; public Person() { } public Person(string value) { this.name = value; } public string PersonName { get { return name; } set { name = value; // Call OnPropertyChanged whenever the property is updated OnPropertyChanged("PersonName"); } } // Create the OnPropertyChanged method to raise the event protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } } } 或者顯式實現INotifyPropertyChanged:
#region INotifyPropertyChanged Members event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add { this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Combine(this.PropertyChanged, value); } remove { this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Remove(this.PropertyChanged, value); } } #endregion
看了上面代碼着實沒看出source值改變了,前臺是經過什麼機制反映的,正常的狀況下公開了一個事件,必須有一個對此事件的實現體,而上面代碼並無實現PropertyChanged的方法。
我猜測是Binding內部獲取了這個接口並對PropertyChanged進行了賦值,由於在debug時,這個事件確實被賦值的,而賦值前的Stack是External Code調用的。
綁定到集合需實現INotifyCollectionChanged,可是推薦使用ObservableCollection<T>,這個類實現了INotifyCollectionChanged和INotifyPropertyChanged。
附:當綁定到普通的.net屬性時,WPF使用反射取得source的值,當對象實現ICustomTypeDescriptor時,WPF使用這個接口取得值,性能上會有所提高。
DataContext在共享資源時最有用。
<StackPanel x:Name="parent" DataContext="{StaticResource photos}"> <Label x:Name="numItemsLabel" Content="{Binding Path=Count}" DockPanel.Dock="Bottom"/>
也能夠在代碼這麼寫parent.DataContext = photos;
IValueConverter能夠在綁定時加入本身的邏輯,很好。
public class RawCountToDescriptionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // Let Parse throw an exception if the input is bad int num = int.Parse(value.ToString()); return num + (num == 1 ? " item" : " items"); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); } }
IValueConverter就兩個方法須要本身實現,Convert和ConvertBack,一個轉過來,一個轉過去。
XAML代碼以下
<Label Background="{Binding Path=Count, Converter={StaticResource myConverter}, Source={StaticResource photos}}"/>
這裏的myConverter是個resource,須要在xaml中預先定義:
<Window.Resources> <local:CountToBackgroundConverter x:Key="myConverter"/> </Window.Resources>
Path對應的Count值會做爲第一個參數value傳給Convert方法。
注意,返回的值必定要是綁定時對應的值,好比綁定時須要綁到Geometry類上,那麼Convert返回的也必須是Geometry類。
Convert方法還帶有一個parameter參數,能夠在xaml中這麼使用
<Label Background="{Binding Path=Count, Converter={StaticResource myConverter}, ConverterParameter=Yellow, Source={StaticResource photos}}"/>
ConverterParameter是object類型。
C#代碼中就能夠獲得parameter的值了。
TIP:
能夠用Binding.DoNothing做返回值,以指示綁定引擎不要執行任何操做。
可用使用[ValueConversion(typeof(DateTime), typeof(String))]來標識Converter要轉化和返回的值類型,第一個參數是soure,第二個參數是target。這樣在編譯時,若是類型不匹配的話,編譯器會拋出異常:error CS0592: Attribute 'ValueConversion' is not valid on this declaration type. It is only valid on 'class' declarations.
.net自帶一些converter,好比經常使用的BooleanToVisibilityConverter,能夠根據checkbox是否勾上來隱藏其餘控件。
在collection中使用converter:使用DateTemplate,在其中使用Converter。(也可使用Converter對整個collection進行轉化,可是可能效率很差)
指示源和目標間數據流的方向。
OneWay 源更新時,目標也更新
TwoWay 源更新時目標也更新,或者目標更新時同時更新源
OneTime 僅當應用程序啓動時或 DataContext 進行更改時更新目標屬性。綁一次就不更維護更新,目標至關於源的一次性鏡像
OneWayToSource 目標更新時更新源,和OneWay相反
大部分WPF自帶的控件的dependency property默認的是OneWay,像TextBox.Text默認的是TwoWay。
值得注意的事,只讀屬性只能設置成OneWay,不能是TwoWay,不然運行時異常。
對於 OneWay 或 TwoWay 綁定,對源的動態更改不會自動傳播到目標。必須在源對象上實現 INotifyPropertyChanged 接口。
對於 TwoWay 綁定,對目標的更改不會自動傳播到源,除非綁定目標是 Text 屬性。在這種狀況下,更新僅在 TextBox 失去焦點時發生。
對於 OneTime 和 OneWay 綁定,對 SetValue 的調用會自動更改目標值並刪除綁定。
再次提醒,源要實現了INotifyPropertyChanged 接口才能把改變反映到目標上。
OneWayToSource 用於多個目標更改一個源的狀況,能夠想像成多人錄入。或者用來實現源和目標倒置的狀況。
指示使用TwoWay或OneWayToSource時,目標在什麼狀況下更新源。有三個枚舉值
PropertyChanged:目標屬性改變時更新源
LostFocus:失去焦點時更新源
Explicit:只有在顯式調用BindingExpression.UpdateSource方法時才更新源。BindingExpression能夠經過BindingOperations.GetBindingExpression或FrameworkElement.GetBindingExpression方法得到
Binding類中提供了SourceUpdated和TargetUpdated事件,能夠用來記些log,不過必須相應的NotifyOnSourceUpdated或NotifyOnTargetUpdated設置成true纔會激發事件。
咱們固然能夠本身寫代碼驗證數據的正確性,不過WPF提供了能夠說是標準的方案,限於篇幅,我單獨開了一貼,點這裏查看。