文章目錄
(特別聲明:在學習WPF過程中,發現OReilly.Programming.WPF.2nd.Edition.Aug.2007的資料繫結部分講得非常好,所以以筆記形式整理出來,供大家學習參考, 如有著作權問題糾紛,請與本人聯絡,本人會立即在cnblogs上撤出該文章,並致歉)
//************************************************************
附WPF書籍推薦:
1、不要為書籍經銷商的炒作蒙蔽,國內翻譯的《WPF揭秘》被褒揚成經典,其實我認為該書淺嘗則止,不夠詳儘是其最致命的弱點
2、《WPF程式設計指南App=Code+markup》是本好書,仔細研讀還能學到很多開發技巧,但書籍將code與markup獨立開有失,如果能
將其結合起來並內容章節仔細組織將會更受歡迎。
3、《Pro WPF in C# 2008 Windows Presentation Foundation with .NET 3.5 SECOND EDITION》是本好書,
我認為和《OReilly.Programming.WPF.2nd.Edition.Aug.2007》不分伯仲,兩本書對WPF內容都解釋十分詳盡,章節設定也十分合理
適合快速掌握WPF開發知識,是學習WPF的首選。
4、至於其他書籍各位仁者見仁,智者見智,至於書店裡某本國產WPF編程書籍,我不想多言,只是我建議改名為《簡單瞭解WPF》也許更合理。
5、書有千種,各有特色,電腦編程開發書籍國產書籍有精品但不多,對新出現的技術如果沒有在市場上使用3年,還是建議首選國外書籍,
希望我這句話不要讓有些人不高興,事實如此,要反駁的話請象李小龍一樣站出來,打出一套好拳給大家看看。
//************************************************************
資料繫結就是將各種資料與使用者展現控制項進行關聯的過程。WPF的資料繫結機制可以以最少的代碼方便地處理這樣的關聯。
簡單資料繫結1 無資料繫結
在實現這樣的使用者互動的時候:
在沒有任何資料繫結機制實現的時候無非採用
this.nameTextBox.Text = person.Name;
this.ageTextBox.Text = person.Age.ToString();
這樣的賦值的方法,而一旦資料改變了,將介面的值寫回給person對象。
1.1對象改變
我們現在需要當對象發生改變的時候,UI的現實如何同步跟著改變呢?
實現起來還是比較複雜的,看如下代碼就明白了。
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 ;
}
}
2資料繫結
上面的樣本是使用人工的手段將對象屬性與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 Conversion2.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">