小瘋最近的項目組工做開發中須要用到WPF,因此最近在學習WPF,小瘋對於WPF中的綁定比較迷糊,認爲這裏須要多注意。而後小瘋在這裏找到了一篇文章,轉過來分享一下:html
數據綁定就是將各類數據與用戶展示控件進行關聯的過程。WPF的數據綁定機制能夠以最少的代碼方便地處理這樣的關聯。數據庫
在實現這樣的用戶交互的時候:windows
在沒有任何數據綁定機制實現的時候無非採用ide
this.nameTextBox.Text = person.Name;工具
this.ageTextBox.Text = person.Age.ToString();學習
這樣的賦值的方法,而一旦數據改變了,將界面的值寫回給person對象。開發工具
1.1對象改變this
咱們如今須要當對象發生改變的時候,UI的現實如何同步跟着改變呢?spa
實現起來仍是比較複雜的,看以下代碼就明白了。orm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;//INotifyPropertyChanged
namespace SimpleDataBinding
{
class Person:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string PropName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropName));
}
}
public Person()
{
_Age = 0;
_name = "Null";
}
private string _name;
public string Name
{
get
{ return _name; }
set
{
if (value == _name)
{ return; }
_name = value;//注意:不能用this.Name來賦值,若是這樣造成循環調用,棧溢出
Notify("Name");
}
}
private int _Age;
public int Age
{
get
{ return _Age; }
set
{
if (value == _Age) return;
_Age = value;
Notify("Age");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace SimpleDataBinding
{
/// <summary>
/// Window1.xaml 的交互邏輯
/// </summary>
public partial class WithoutDataBinding : Window
{
private Person _person;
public WithoutDataBinding()
{
InitializeComponent();
//能夠採用以下對象初始化,但本示例爲了使用使用屬性改變UI改變就先不賦值。
//_person = new Person
//{
// Name = "zhangying",
// Age = 28
//};
_person = new Person();
_person.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "Name":
this.txt_name.Text = _person.Name;
break;
case "Age":
this.txt_age.Text = _person.Age.ToString();
break;
}
};
}
private void button2_Click(object sender, RoutedEventArgs e)
{
_person.Name = "zhangying";
_person.Age = 28;
}
}
}
1.2 UI改變
當UI的輸入值發生變化的時候,如何同步對象的值呢?這須要給他們設置事件代碼:
private void txt_name_TextChanged(object sender, TextChangedEventArgs e)
{
_person.Name = this.txt_name.Text;
}
private void txt_age_TextChanged(object sender, TextChangedEventArgs e)
{
int age = 0;
if(int.TryParse(this.txt_age.Text,out age)
{
_person.Age = age ;
}
}
上面的示例是使用人工的手段將對象屬性與UI顯示進行同步,而WPF數據綁定則是註冊兩個屬性與數據綁定引擎,來處理同步,合適的數據轉換,以下圖:
2.1 Bindings
1.使用元素屬性綁定:
<TextBox>
<TextBox.Text>
<Binding Path="Age" />
</TextBox.Text>
</TextBox>
2. 簡化屬性綁定
<TextBox Text="{Binding Path=Age}" />
更簡化方式:
<TextBox Text="{Binding Age}" />
看一個更多特性的綁定方式(屬性元素的方式):
<TextBox>
<TextBox.Foreground>
<Binding Path="Age" Mode="OneWay" Source="{StaticResource Tom}"
Converter="{StaticResource ageConverter}" />
</TextBox.Foreground>
</TextBox>
上面的綁定能夠採用以下簡潔方式:
Foreground="{Binding Path=Age, Mode=OneWay, Source={StaticResource Tom},
Converter={StaticResource ageConverter}}" />
注意上面的引號的用法和不用引號的用法,使用屬性元素的方式必須用引號。
下面圖表列出綁定到一個對象時候可能用到的屬性:
這些屬性後面將一一介紹到。
2.2 隱式數據源Implicit Data Source
在WPF中,每一個FrameworkElement 和FrameworkContentElement對象都有一個DataContex屬性,該屬性是Object類型,因此你能夠給它指定任何類型的值。當咱們定義個對象做爲綁定對象,則綁定引擎在邏輯樹上尋找該數據庫綁定源,以下圖所示:
2.3 數據島Data Islands
看如下代碼示例就明白了:
private void btn_Click(object sender, RoutedEventArgs e)
{
Person person = (Person)this.FindResource("zy");
MessageBox.Show(string.Format("I am {0},Age is {1}",person.Name,person.Age.ToString()));
}
2.4 顯示數據源Explicit Data Source
顯示數據源設置對於多個數據源是很重要的,咱們能夠經過綁定屬性的Source來實現。
注意上面兩個文本框設置了不一樣的數據源的屬性。
2.5 綁定到其餘控件binding to other controls
以下代碼所示:
<Grid>
<Slider Name="slider1" />
<TextBlock Text="{Binding Path=Value,ElementName=slider1}"/>
</Grid>
2.6 值轉換 Value Conversation
上面的代碼咱們發現綁定是將一個數值類型綁定到一個字符串,類型不符合,這到底是如何作到的呢?原來這是一個號稱Value Converter類的功勞,它實現了IValueConverter接口,該接口有兩個方法:Convert和ConvertBack。(該接口在System.Windows.Data命名空間,PresentationFramework.dll程序集中),實現 IValueConverter 接口時,最好用ValueConversionAttribute 屬性來修飾此實現,以便向開發工具指示轉換所涉及的數據類型,以下面的示例所示:(如下示例摘錄自MSDN)
看下面的使用方法:
使用方法,先指定數據源
2.7 Editable Value Conversion
2.8 Validation
一個驗證規則就是在一條目標數據更新來源數據的時候的一段數據驗證代碼,這段代碼通常是繼承ValidationRule類並覆蓋Validate方法,一個內置的規則稱爲ExceptionValidationRule
以下代碼咱們能夠看到使用最基本的驗證的方式:
當咱們運行程序的時候
紅色邊框顯示輸入框數據格式錯誤。可是咱們發現這樣仍然不夠友好,由於沒有消息提示到底爲何錯誤。
爲了顯示錯誤,須要使用綁定的事件來偵聽ValidationError,代碼示例很容易說明這點:
這樣還不行,還必須以下圖設置:
運行結果你們確定很明白了,固然這也許還不是咱們須要的,咱們能夠更多地控制行爲,這就要求咱們自定義驗證規則。下面咱們以一個實例來說解自定義驗證規則。
首先咱們經過繼承ValidationRule類而後覆寫validate方法,仍是讓代碼說明一切吧:
在實際項目中,可能你們對錯誤提示採用對話框不是很喜歡,最好的方式是WindowsForm的驗證方式Tooltip,但有一點須要注意ValidateRule沒有相似ValidateSucess事件讓咱們清除錯誤的時候產生的Tooltip,以致於整個Tooltip的顯示很奇怪。
2.9綁定路徑句法Binding Path Syntax
如下列出MSDN上的描述:
使用 Path 屬性能夠指定您要綁定到的源值:
· 在最簡單的狀況下,Path 屬性值是要用於綁定的源對象的屬性名,如 Path=PropertyName。
· 在 C# 中能夠經過相似語法指定屬性的子屬性。 例如,子句 Path=ShoppingCart.Order 設置與對象或屬性 ShoppingCart 的 Order 子屬性的綁定。
· 若要綁定到附加屬性,應在附加屬性周圍放置圓括號。 例如,若要綁定到附加屬性 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=.}」。
轉義機制
· 在索引器 ([ ]) 內部,插入符號 (^) 用於對下一個字符進行轉義。
· 若是您在 XAML 中設置 Path,則還須要使用 XAML 實體對 XAML 分析程序專用的某些字符進行轉義:
· 使用 & 對字符「&」進行轉義。
· 使用 > 對結束標記「>」進行轉義。
· 此外,若是您使用標記擴展語法描述屬性中的整個綁定,則須要使用反斜槓 \ 對 WPF 標記擴展分析程序專用的字符進行轉義:
· 反斜槓 \ 自己是轉義字符。
· 等號 (=) 將屬性名與屬性值隔開。
· 逗號 (,) 用於分隔屬性。
· 右大括號 (}) 是標記擴展的結尾。
Binding the ToolTip property to the validation error message
<TextBox
Name="ageTextBox" ...
ToolTip="{Binding
ElementName=ageTextBox,
Path=(Validation.Errors)[0].ErrorContent}">
<TextBox.Text>
<Binding Path="Age">
<!-- No need for NotifyOnValidationError="true" -->
<Binding.ValidationRules>
<local:NumberRangeRule Min="0" Max="128" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
2.10相對數據源Relative Source
仍是代碼來講明更易理解
<TextBox ...
ToolTip="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}">
在上面實例中咱們採用 ElementName=txtDate這樣的方式指定的,這裏使用self來表示引用該標籤所在的元素生成的對象,固然咱們還可使用相似於parent指向上一級,關於Relative resource在後續章節會繼續討論。
2.11更新源數據觸發器Update Source Trigger
在前面咱們基本上是在目標控件的失去焦點的時候發生相關驗證並更新數據源的,而實際上更新數據源的機制有多種類型,以下:
namespace System.Windows.Data
{
public enum UpdateSourceTrigger
{
Default = 0, // updates "naturally" based on the target control
PropertyChanged = 1, // updates the source immediately
LostFocus = 2, // updates the source when focus changes
Explicit = 3, // must call BindingExpression.UpdateSource()
}
}
而在XAML裏面必須以下聲明:
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged">