WPF中的數據綁定提供了很強大的功能。與普通的WinForm程序相比,其綁定功能爲咱們提供了不少便利,例如Binding對象的自動通知/刷新,Converter,Validation Rules,Two Way Binding等功能,省去了不少維護的繁瑣工做。另外對於WPF中提供的數據模板功能,讓咱們能夠輕鬆定製能夠被複用的控制呈現的模塊—但這是以數據綁定爲前提來作到輕鬆易用的效果的。數據提供者例如XmlDataProvider和ObjectDataProvider更是簡化了將對象以特定方式綁定並呈現的過程。能夠說,數據綁定是WPF中讓咱們真正可以開始體現其便利性的特徵之一,而對以數據驅動的應用來說,其重要性不言而喻。ide
數據綁定的關鍵是System.Windows.Data.Binding對象,它會把兩個對象(UI對象與UI對象之間,UI對象與.NET數據對象之間)按照指定的方式粘合在一塊兒,並在他們之間創建一條通訊通道,綁定一旦創建,接下來的應用生命週期中它能夠本身獨立完成全部的同步工做。根據其應用場合的不一樣咱們將在本文中從如下幾個部分分別討論:ui
UI對象間的綁定,也是最基本的形式,一般是將源對象Source的某個屬性值綁定 (拷貝) 到目標對象Destination的某個屬性上。源屬性能夠是任意類型,但目標屬性必須是依賴屬性(Dependency Property)。一般狀況下咱們對於UI對象間的綁定源屬性和目標屬性都是依賴屬性 (有些屬性不是) ,由於依賴屬性有垂直的內嵌變動通知機制,WPF能夠保持目標屬性和源屬性的同步。this
看個簡單的例子是如何在XAML中實現數據綁定的:spa
<Window x:Class="Allan.WpfBinding.Demo.Window1"設計 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"code xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"orm Title="Basic Bindings" Height="400" Width="700" Style="{StaticResource windowStyle}">xml <Grid>對象 <Grid.RowDefinitions>繼承 <RowDefinition Height="40" /> <RowDefinition Height="*" /> <RowDefinition Height="40" /> </Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right"> <Button x:Name="btnBasicBinding" Content="Basic" Style="{StaticResource buttonStyle}"></Button> <Button x:Name="btnCollectionBinding" Content="Collection" Style="{StaticResource buttonStyle}"></Button> <Button x:Name="btnDataTemplate" Content="Data Template" Style="{StaticResource buttonStyle}"></Button> <Button x:Name="btnAdvanceBindings" Content="Advance" Style="{StaticResource buttonStyle}"></Button> <Button x:Name="btnExit" Content="Exit" Style="{StaticResource buttonStyle}"></Button> </StackPanel>
<StackPanel Grid.Row="1" HorizontalAlignment="Left"> <TextBox x:Name="txtName" Margin="5" Width="400" BorderThickness="0" Height="50" Text="Source Element"></TextBox> <TextBlock x:Name="tbShowMessage" Margin="5" Width="400" Height="50" Text="{BindingElementName=txtName,Path=Text }" /> </StackPanel> </Grid> </Window> |
上邊的代碼咱們將名爲txtName的對象的Text屬性做爲源對象分別綁定給了兩個TextBlock的Text屬性。這裏咱們用了Binding關鍵字並指定了ElementName和Path,這兩個就是指定源對象(Source)和源屬性(Source Property). 一般咱們在設定綁定時都用與StaticResource標記相似的語法{Binding… }並設置ElementName和Path屬性:
Text=」{Binding ElementName=SourceObjectName, Path=SourceProperty}」 |
而對於C#裏和綁定相關的代碼,則看起來會羅嗦不少。但它們都一樣的使用了Binding對象,而後指定PropertyPath的一個實例爲源屬性,而後能夠有兩個方法來加載綁定規則:
如下代碼實現了和上邊XAML文件相似的功能:
Binding binding = new Binding(); //設置源對象 binding.Source = txtName; //設置源屬性 binding.Path = new PropertyPath("Text"); //添加到目標屬性 this.tbShowMessage.SetBinding(TextBlock.TextProperty, binding); //or //BindingOperations.SetBinding(tbShowMessage, TextBlock.TextProperty, binding); |
當你在應用程序中某個地方添加了綁定,而在某個時候又不想這個綁定在接下來繼續有效時,你能夠有兩種方式來斷開這個綁定:
例如BindingOperations.ClearBinding(currentTextBlock, TextBlock.TextProperty); BindingOperations同時還提供了ClearAllBindings方法,只須要傳入要清除綁定的目標對象的名稱,它就會將全部這個對象的綁定移除。
這個簡單的方法一樣有效,能夠斷開與前邊設置的binding的鏈接。簡單的設置爲任何值便可:如:currentTextBlock.Text = 「it’s a new value.」;
Property |
Description |
|
轉換器 |
|
綁定的源對象 |
|
綁定沒法返回有效值時的默認顯示。 |
|
綁定方式 |
|
屬性 |
|
經常使用於自身綁定或者數據模板中來指定綁定的源對象。 |
|
源對象 |
|
格式化表達式 |
|
Sets the events on which binding will occur. |
|
驗證規則 |
總結:對於對象間的綁定,綁定源爲ElementName,Path爲綁定源屬性。ElementName必須爲如下可選項之一:
|
DataContext是WPF最後才試圖查找的源。一旦RelativeSource和Source對象都沒有被設置,則會在邏輯樹種向上搜尋。 |
|
用來標識和當前控件關聯的對象,一般用於自我引用或數據模板。 |
|
數據提供者/對象 |
經常使用標記:{Binding Path =」」} ItemSource DisplayMemberPath
一般來講這是咱們在作以數據驅動爲主的應用時最常常用到的綁定方式。WPF支持任何類型的.NET對象做爲數據源綁定到WPF對象。對於全部的ItemsControl對象都有一個ItemsSource依賴屬性,這是專門爲數據綁定而準備的。ItemsSource的類型是IEnumerable,因此對於咱們幾乎全部的集合類型咱們均可以輕易的改變成ItemsSource的源對象。經過如下語句咱們能夠將一個名爲photos的集合賦予ListBox對象,並以顯示Name屬性的值:
<ListBox x:Name=」pictureBox」 DisplayMemberPath=」Name」 ItemsSource=」(Binding {DynamicResource photos}」 |
咱們知道,依賴屬性內建的垂直通知功能讓UI對象間的綁定能夠本身負責同步處理,可是對於.NET集合/對象來說,它不具有這樣的能力。爲了讓目標屬性與源集合的更改保持同步,源集合必須實現一個叫INotifyCollectionChanged的接口,但一般咱們只須要將集合類繼承於ObservableCollection類便可。由於ObservableCollection實現了INotifyPropertyChanged和INotifyCollectionChanged接口。示例代碼中咱們這麼去定義Photos集合類:
public class Photos : ObservableCollection<Photo> |
經常使用標記:{Binding Path=」」} DataContext
顧名思義,DataContext就是數據上下文對象,它是爲了不多個對象共享一個數據源時重複的對全部對象顯式地用binding標記每一個Source/RelativeSource/ElementName,而把同一個數據源在上下文對象的某個範圍內共享,這樣當一個綁定沒有顯式的源對象時,WPF會便利邏輯數找到一個非空的DataContext爲止。
例如咱們能夠經過如下代碼給ListBox和Title設置綁定:
<StackPanel Orentation=」Vertical」 Margin=」5」 DataContext=」{DynamicResource photos}」> <Label x:Name=」TitleLabel」 Content=」{Binding Path=Count}」 DockPanel.Dock=」Bottom」 /> <ListBox x:Name=」pictureBox」 DisplayMemeberPath=」Name」 ItemSource=」{Binding}」 /> </StackPanel> |
對於這些簡單的綁定咱們能夠很靈活的組合他們的應用來達到咱們的要求,這也是咱們一般使用的方法。例如:
<Window.Resources> <local:Employee x:Key="MyEmployee" EmployeeNumber="123" FirstName="John" LastName="Doe" Department="Product Development" Title="QA Manager" /> </Window.Resources> <Grid DataContext="{StaticResource MyEmployee}"> <TextBox Text="{Binding Path=EmployeeNumber}"></TextBox> <TextBox Text="{Binding Path=FirstName}"></TextBox> <TextBox Text="{Binding Path=LastName}" /> <TextBox Text="{Binding Path=Title}"></TextBox> <TextBox Text="{Binding Path=Department}" /> </Grid> |
總結:對於集合的綁定,一般會須要用到如下幾個標記:
|
指定源對象中被顯示的屬性。ToString()方法會被默認調用。 |
|
指定要顯示的數據源 |
|
指定以什麼樣的格式來顯示數據(相似於符合控件,能夠在數據模板中利用多種控件來控制展示方式) |
|
數據源對象中的屬性—控制顯示 |
|
共享數據源 |
當源屬性和目標屬性爲兼容的數據類型,且源所顯示的東西正是你須要顯示的東西時,數據綁定確實很簡單,你只須要向Section 1中講的來匹配對象關係便可。而一般狀況下咱們對數據綁定都要作一些定製,特別對於.NET對象的綁定,你須要將數據源按照不一樣的方式分割顯示。Data Template就負責來完成這樣的功能:按照預想的數據展示模式將數據源的不一樣部分顯示,而其做爲能夠被複用的獨立結構,一旦定義能夠被添加到一個對象內部,將會建立一個全新的可視樹。
數據模板一般會被應用到如下幾類控件來填充其類型爲DataTemplate的屬性:
每一個數據模板的定義都是相似的方式,你能夠像設計普通的窗體同樣來設計其展示的方式,並且他們共享數據模板父空間所賦予的綁定源。例以下邊的代碼咱們用一個圖片來替代ListBox中的每一項:
<ListBox x:Name="pictureBox" ItemsSource="{Binding}"ScrollViewer.HorizontalScrollBarVisibility="Disabled"> <ListBox.ItemTemplate> <DataTemplate> <Image Source="{Binding Path=FullPath}" Margin="3,8" Height="35"> <Image.LayoutTransform> <StaticResource ResourceKey="st"/> </Image.LayoutTransform> <Image.ToolTip> <StackPanel> <TextBlock Text="{Binding Path=Name}"/> <TextBlock Text="{Binding Path=DateTime}"/> </StackPanel> </Image.ToolTip> </Image> </DataTemplate> </ListBox.ItemTemplate> </ListBox> |
最終的ListBox中每一項的展示將按照咱們在數據模板中設定的樣式以圖片來顯示:
一般數據模板是不須要被內聯聲明的,它能夠被定義成一個資源存放在Application.Resources這樣的全局資源辭典中,或者單獨的Resource Dictionary中在多個元素間共享。
不管你的綁定XAML寫得多麼漂亮,全部的綁定值毫無疑問你均可以獲得,可是它不老是能夠知足你不通過任何程序變化顯示出來就能知足要求的。例如對於本文示例代碼的照片總數的顯示,咱們還想顯示得更爲智能一些:對於一些符合某種要求的數據咱們將其背景顯示爲黃色,而對於有多於一條記錄時咱們顯示15 Items,僅有一條時顯示1 Item。這時Value Converter就派上用場了。
要定義一個Value Converter須要聲明一個類讓其繼承於System.Windows.Data.IValueConverter接口,並實現其中的兩個方法Convert和ConvertBack方法。
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(); } } |
在XAML中聲明資源,而後將其經過靜態資源引用的方式賦予Binding對象的Converter屬性。
<Window.Resources> <local:CountToBackgroundConverter x:Key="myConverter"/> <local:RawCountToDescriptionConverter x:Key="myConverter2"/> </Window.Resources> <TextBlock x:Name="filePath" DockPanel.Dock="Top" Style="{StaticResource titleStyle}" Text="{Binding Count, Converter={StaticResource myConverter2}}"></TextBlock> |
一樣,咱們能夠對輸入進行轉換。若是數據的輸入是被驗證規則(若是有的話)標記爲有效的,那麼值轉換器將會被調用,來對輸入進行轉換後反應出來。 (參考附件代碼中的BindingConverter窗體)
每一個Binding對象都有一個ValidationRules屬性,能夠被設置爲一個或多個派生自ValidationRule的對象,每一個規則都會檢查特定的條件並更具結果來標記數據的有效性。就像咱們在ASP.NET中應用RequiredValidator, CustomValidator同樣,你只須要定義本身的規則,WPF會在每次調用數據時(一般是TextBox等輸入控件失去焦點)會調用驗證檢查。這些是在值轉換器以前發生的,若是數據無效,它會標記這次更新無效,並將數據標記爲無效—這是經過設置目標元素的Validation.HasError屬性爲true並觸發Validation.Error事件(ValidationResult會被返回,而且其IsValid屬性爲false)。咱們能夠經過一個觸發器來設定當數據無效時對用戶的提示。例以下邊的代碼咱們就經過定義一個JpgValidationRule,當數據無效時經過tooltip來提示用戶輸入無效。
public class JpgValidationRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { string filename = value.ToString();
// Reject nonexistent files: if (!File.Exists(filename)) { return new ValidationResult(false, "Value is not a valid file."); }
// Reject files that don’t end in .jpg: if (!filename.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase)) { return new ValidationResult(false, "Value is not a .jpg file."); } else { return new ValidationResult(true, null); } } } |
上邊的代碼定義了咱們驗證的規則。接下來在XAML中來應用這個規則。咱們將這個規則用來檢測輸入框中的數據是否合法:
<TextBox Style="{StaticResource validateTextBoxStyle}"> <TextBox.Text> <Binding UpdateSourceTrigger="PropertyChanged" Path="Department"> <Binding.ValidationRules> <local:JpgValidationRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> |
當數據不合法時咱們以什麼樣的方式來告訴用戶呢?這裏有兩個方法能夠作,一個是定義你本身的ErrorTemplate,另一個是根據Trigger來設置一些可見信息。一般咱們均可以來本身定義一些Error Provider和能夠複用的ErrorTemplate,這個話題咱們會在下一篇文章中講。這裏咱們只讓背景作改變並用tooltip來提示用戶—顯示的是ValidationRule返回的出錯信息。由於都是控制顯示的,因此定義成共用的Style:
<Style x:Key="validateTextBoxStyle" TargetType="{x:Type TextBox}"> <Setter Property="Width" Value="300" /> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="Background" Value="Red"/> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/> </Trigger> </Style.Triggers> </Style> |
總的來講,對於驗證,咱們經常使用一下幾個屬性來定義錯誤驗證規則和錯誤展示方式:
Coming Next:
本文咱們瞭解了有關Binding以及和綁定有關的附加驗證規則,轉換器等。附加驗證規則咱們將在下一篇中瞭解更多自定義Error Provider,Error Template等。附加的Demo裏提供了全部本文中的實例。在下一篇中咱們會了解如下幾個問題: