WPF學習筆記一 依賴屬性及其資料繫結
本文想通過由淺入深的講解讓讀者比較深的理解依賴屬性. 首先,我們回顧一下依賴屬性的發展歷史.
最初,人們提出面向物件程式設計時,並沒有屬性這個說法,當時叫做成員變數.一個物件由成員變數和成員函式組成,如下:
Public Class A{
Public int Index;//成員變數
Public void Fun(){} //成員函式
}
後來,提出了對成員變數的改進,增加了get/set 方法,成員變數自然也叫屬性了。.net採用了這種方法:
Public Class A{
Private int index; //屬性
Public int Index{
Set{index = Value;}
Get{ return index ;}
}
Public void Fun(){} //方法
}
|
到了WPF,終於變成依賴屬性(WPF大部分屬性都是依賴屬性)。我們先看一個依賴屬性的實現,再理解。
public partial class MyButton : Button {
public static DependencyProperty PressedImageProperty; //依賴屬性
public string PressedImage {
get { return ( string )GetValue(PressedImageProperty); }
set { SetValue(PressedImageProperty, value); }
}
static MyButton() {
PressedImageProperty = DependencyProperty.Register(
"PressedImage" , typeof ( string ), typeof (MyButton),
new FrameworkPropertyMetadata( "" ));
}
public MyButton () {
InitializeComponent();
}
|
上面實現了一個PressedImage的依賴屬性。和.net屬性比較有幾點區別:
1。依賴屬性的實體是靜態的,型別為DependencyProperty。
如上例中的屬性PressedImage,如果在.net中會這樣定義它的實體
public string pressedImage;
依賴屬性卻是這樣:
public static DependencyProperty PressedImageProperty;
2。多了一個Register。
3. 它的Get/Set方法藉助DependencyObject的GetValue/SetValue來實現.
但憑這些區別是沒法理解依賴屬性,因為你不知道DependencyProperty,DependencyObject怎麼實現的.這裡我只能說一下依賴屬性一個特點:
在面向物件程式設計時,一個物件有自己的各種屬性,我們以往建立一個物件時,它的屬性值就儲存在物件本身.但依賴屬性卻不是把屬性值儲存在物件本身,而是儲存在一張公共的依賴屬性表裡,它包含所有物件的依賴屬性.因此,當我們要知道一個物件的某個屬性時,WPF不是從該物件直接取出,而是從那張公共的依賴屬性表裡取出.
呵呵,大概你明白了為什麼依賴屬性會變得複雜起來.實際上,功能也強大得多.
下面就來討論自定義依賴屬性和它的資料繫結.
通過例子說明。對上文MyButton進行修進,實現以下功能:當MyButton被按下時顯示圖片"c:\images\ButtonPressed.png",否則顯示圖片"c:\images\ButtonNormal.png". xaml程式碼
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image x:Name="imgNormal"
Source="c:\images\ButtonNormal.png" Visibility="Visible"/>
<Image x:Name="imgPressed"
Source="c:\images\ButtonPressed.png" Visibility="Hidden" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="imgNormal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="imgPressed" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
cs檔案程式碼:
public partial class MyButton : Button {
public MyButton () {
InitializeComponent();
}
|
上面的程式碼看不懂也沒關係,並沒有用到依賴屬性。但同時,這個MyButton並沒有使用價值,因為他的圖片是固定的。我們希望MyButton的圖片可以由使用者來設定。在上面的xaml檔案中,只需改變Image的Source屬性。WPF的方法就是資料繫結,也就是把Image的Source屬性繫結到一個變數,修改一下xaml:
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image x:Name="imgNormal" Source="{Binding Path=NormalImage}" Visibility="Visible"/>
<Image x:Name="imgPressed" Source="{Binding Path=PressedImage}" Visibility="Hidden" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="imgNormal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="imgPressed" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
imgNormal的圖片路徑繫結變數NormalImage, imgPressed的圖片路徑繫結到PressedImage,再在MyButton類中增加2個屬性NormalImage,PressedImage
public partial class MyButton : Button { public string NormalImage;
public string NormalImage;
public MyButton () { InitializeComponent(); }
在使用MyButton時用以下語句訪問:
<my:MyButton NormalImage="c:\images\ButtonNormal.png" PressedImage="c:\images\ButtonPressed.png" />搞定!!怎麼?編譯通不過?因為NormalImage和PressedImage不是依賴屬性,所以編譯無法通過。那就繼續改吧,把NormalImage和PressedImage改成依賴屬性。
MyButton.cs的最後程式碼如下:
public partial class MyButton : Button {
public static DependencyProperty NormalImageProperty;
public static DependencyProperty PressedImageProperty;
public string NormalImage {
get { return ( string )GetValue(NormalImageProperty); }
set { SetValue(NormalImageProperty, value); }
}
public string PressedImage {
get { return ( string )GetValue(PressedImageProperty); }
set { SetValue(PressedImageProperty, value); }
}
static MyButton () {
NormalImageProperty = DependencyProperty.Register(
"NormalImage" , typeof ( string ), typeof (MyButton ),
new FrameworkPropertyMetadata( "" ));
PressedImageProperty = DependencyProperty.Register(
"PressedImage" , typeof ( string ), typeof (MyButton ),
new FrameworkPropertyMetadata( "" ));
}
public MyButton () {
InitializeComponent();
}
|
為什麼xaml不支援普通屬性?
回顧一下WPF的基本理念:真正做到了分離介面設計人員與開發人員的工作。也就是說,xaml檔案呈現的是使用者介面,從xaml到使用者介面的轉化不是由編譯器直接編譯成使用者介面的執行程式碼(這樣和原來的WIN FORM又沒有區別了),而是由WPF本身類庫來來完成的,打個比方,XAML有如下程式碼:
<Button Background="White"></Button>
其中的“White”是如何與C#中的System.Windows.Media.Brushes.White等價的呢?。原來WPF提供了Brush資料型別的轉換器。
對於簡單屬性(如int型別的),WPF要解釋它並不難,如果是自定義型別的呢,WPF要解釋它也不難,比如要求開發人員在使用自定義型別的屬性時,同時提供該自定義型別的轉換器。因此,個人認為,xaml不支援普通屬性的原因就是WPF的設計者認為沒有必要。