WPF and Silverlight 學習筆記(十三):依賴項屬性和路由事件

iDotNetSpace發表於2009-04-17

一、依賴項屬性(Dependency Property)

Windows Presentation Foundation (WPF) 提供了一組服務,這些服務可用於擴充套件公共語言執行時 (CLR) 屬性的功能。這些服務通常統稱為 WPF 屬性系統。由 WPF 屬性系統支援的屬性稱為依賴項屬性。本概述介紹 WPF 屬性系統以及依賴項屬性的功能,這包括如何在可擴充套件應用程式標記語言 (XAML) 中和程式碼中使用現有的依賴項屬性。

依賴項屬性的用途在於提供一種方法來基於其他輸入的值計算屬性值。這些其他輸入可以包括系統屬性(如主題和使用者首選項)、實時屬性確定機制(如資料繫結和動畫/演示圖板)、重用模板(如資源和樣式)或者通過與元素樹中其他元素的父子關係來公開的值。另外,可以通過實現依賴項屬性來提供獨立驗證、預設值、監視其他屬性的更改的回撥以及可以基於可能的執行時資訊來強制指定屬性值的系統。派生類還可以通過重寫依賴項屬性後設資料(而不是重寫現有屬性的實際實現或者建立新屬性)來更改現有屬性的某些具體特徵。

1、依賴項屬性與CLR 包裝屬性

以Button的Backgroud為例,設定或獲取其值可以有以下幾種方式:

XAML檔案中

   1: <StackPanel>
   2:     <!--
   3:         在所生成的程式碼中,XAML載入器將 XAML 屬性的簡單字串值的
   4:         型別轉換為 WPF 型別(一種 Color,通過 SolidColorBrush)。
   5:     --&gt
   6:     <Button Margin="3" Background="Yellow" Content="Button A" />
   7:     
   8:     <!--
   9:         使用巢狀元素的方式,設定Button.Backgroud的值
  10:     --&gt
  11:     <Button Margin="3" Content="Button B" x:Name="btn_ButtonB">
  12:         <Button.Background>
  13:             <SolidColorBrush Color="Gold" />
  14:         Button.Background>
  15:     Button>
  16:     
  17:     <!--預留給程式碼使用的控制元件--&gt
  18:     <Button Margin="3" Content="Button C" x:Name="btn_ButtonC" />
  19:     <Button Margin="3" Content="Button D" x:Name="btn_ButtonD" />
  20:     <TextBox Margin="3" x:Name="txt_Value1" />
  21:     <TextBox Margin="3" x:Name="txt_Value2" />
  22: StackPanel>

程式碼檔案中:

   1: // 通過包裝的屬性設定按鈕的背景顏色
   2: btn_ButtonC.Background = new SolidColorBrush(Colors.Red);
   3:  
   4: // 通過依賴性屬性的SetValue設定按鈕的背景顏色
   5: SolidColorBrush brush = new SolidColorBrush(Colors.Blue);
   6: btn_ButtonD.SetValue(
   7:     Button.BackgroundProperty, brush);
   8:  
   9: // 通過包裝的屬性獲取ButtonB的背景顏色
  10: SolidColorBrush b_Brush1 = (SolidColorBrush) (btn_ButtonB.Background);
  11: txt_Value1.Text = b_Brush1.Color.ToString();
  12:  
  13: // 通過依賴性屬性的GetValue獲取ButtonB的背景顏色
  14: SolidColorBrush b_Brush2 = (SolidColorBrush) (btn_ButtonB.GetValue(
  15:     Button.BackgroundProperty));
  16: txt_Value2.Text = b_Brush2.Color.ToString();

如果使用的是現有屬性,則上述操作通常不是必需的(使用包裝會更方便,並能夠更好地向開發人員工具公開屬性)。但是在某些情況下適合直接呼叫 API。

2、使用由依賴項屬性提供的屬性功能

依賴項屬性提供用來擴充套件屬性功能的功能,這與欄位支援的屬性相反。每個這樣的功能通常都表示或支援整套 WPF 功能中的特定功能:

  • 資源

  • 資料繫結

  • 樣式

  • 動畫

  • 後設資料重寫

  • 屬性值繼承

  • WPF 設計器整合

3、自定義依賴項屬性及重寫依賴項屬性

對於自定義依賴項屬性,其所在的型別必須直接或間接繼承System.Windows.DependencyObject類,依賴項屬性是通過呼叫 Register 方法(或 RegisterReadOnly,自定義的只讀的依賴項屬性)在 WPF 屬性系統中註冊,並通過 DependencyProperty 識別符號欄位備份的屬性。依賴項屬性只能由 DependencyObject 型別使用,但 DependencyObject 在 WPF 類層次結構中的級別很高,因此,WPF 中的大多數可用類都支援依賴項屬性。在對依賴項屬性及CLR包裝屬性命名時必須滿足:CLR包裝屬性名+Property=依賴項屬性名。

例如:在某DependencyObject類的子類中定義:

   1: // 定義並註冊依賴項屬性
   2: public static readonly DependencyProperty AquariumGraphicProperty = 
   3:     DependencyProperty.Register(
   4:         "AquariumGraphic",              // 要註冊的依賴項物件的名稱
   5:         typeof(Uri),                    // 屬性的型別
   6:         typeof(AquariumObject),         // 正註冊依賴項物件的所有者型別
   7:         new FrameworkPropertyMetadata(  // 依賴項物件的屬性後設資料
   8:             null,
   9:             FrameworkPropertyMetadataOptions.AffectsRender,
  10:             new PropertyChangedCallback(OnUriChanged)
  11:         )   
  12:     );
  13:  
  14: // 定義CLR包裝屬性
  15: public Uri AquariumGraphic
  16: {
  17:     get { return (Uri)GetValue(AquariumGraphicProperty); }
  18:     set { SetValue(AquariumGraphicProperty, value); }
  19: }

 

二、路由事件(RoutedEvent)

先看以下的應用程式:

   1: <StackPanel>
   2:     <StackPanel Background="Blue" Margin="3" x:Name="panel_Blue">
   3:         <Button Margin="3" Content="Button A" x:Name="btn_B1" Click="btn_B_Click" />
   4:         <Button Margin="3" Content="Button B" x:Name="btn_B2" Click="btn_B_Click" />
   5:     StackPanel>
   6:     <StackPanel Background="Green" Margin="3" x:Name="panel_Green" ButtonBase.Click="panel_G_Click">
   7:         <Button Margin="3" Content="Button A" x:Name="btn_G1" />
   8:         <Button Margin="3" Content="Button B" x:Name="btn_G2" />
   9:     StackPanel>
  10:     <StackPanel Background="Red" Margin="3" x:Name="panel_Red" ButtonBase.Click="panel_R_Click">
  11:         <Button Margin="3" Content="Button A" x:Name="btn_R1" Click="btn_R1_Click" />
  12:         <Button Margin="3" Content="Button B" x:Name="btn_R2" Click="btn_R2_Click" />
  13:     StackPanel>
  14: StackPanel>

   1: private void btn_B_Click(object sender, RoutedEventArgs e)
   2: {
   3:     MessageBox.Show(
   4:         "Click Blue Panel Button !",
   5:         "System Infomation",
   6:         MessageBoxButton.OK,
   7:         MessageBoxImage.Information);
   8:  
   9:     MessageBox.Show(
  10:         string.Format("sender Type is {0}.", sender.GetType().Name));
  11:  
  12:     Button sourceButton = (Button) (sender);
  13:     MessageBox.Show(
  14:         string.Format("Source Button is {0} !", sourceButton.Name),
  15:         "System Infomation",
  16:         MessageBoxButton.OK,
  17:         MessageBoxImage.Information);
  18: }
  19:  
  20: private void panel_G_Click(object sender, RoutedEventArgs e)
  21: {
  22:     MessageBox.Show(
  23:         "Click Green Panel Button !",
  24:         "System Infomation",
  25:         MessageBoxButton.OK,
  26:         MessageBoxImage.Information);
  27:     
  28:     MessageBox.Show(
  29:         string.Format("sender Type is {0}.", sender.GetType().Name));
  30:  
  31:     MessageBox.Show(
  32:         string.Format("e.Source Type is {0}.", e.Source.GetType().Name));
  33:  
  34:     Button sourceButton = (Button)(e.Source);
  35:     MessageBox.Show(
  36:         string.Format("Source Button is {0} !", sourceButton.Name),
  37:         "System Infomation",
  38:         MessageBoxButton.OK,
  39:         MessageBoxImage.Information);
  40: }
  41:  
  42: private void panel_R_Click(object sender, RoutedEventArgs e)
  43: {
  44:     MessageBox.Show(
  45:         "Click Red Panel Button !",
  46:         "System Infomation",
  47:         MessageBoxButton.OK,
  48:         MessageBoxImage.Information);
  49: }
  50:  
  51: private void btn_R1_Click(object sender, RoutedEventArgs e)
  52: {
  53:     MessageBox.Show(
  54:         "Click Red Panel Button A !",
  55:         "System Infomation",
  56:         MessageBoxButton.OK,
  57:         MessageBoxImage.Information);
  58: }
  59:  
  60: private void btn_R2_Click(object sender, RoutedEventArgs e)
  61: {
  62:     MessageBox.Show(
  63:         "Click Red Panel Button B !",
  64:         "System Infomation",
  65:         MessageBoxButton.OK,
  66:         MessageBoxImage.Information);
  67:  
  68:     e.Handled = true;
  69: }

1、在panel_Blue中定義的三個按鈕的Click事件屬於“類事件”,即在型別物件中宣告事件的繫結。此時事件響應方法中的sender指的就是由哪個物件引發的事件

2、在panel_Green中定義ButtonBase.Click="xxx",將其容器內所有的ButtonBase型別及其子型別的事件,統一繫結到一個事件處理方法上,統一處理。此時事件響應方法中的sender指的是panel_Green物件,而e.Source指的是引發ButtonBase.Click的某個按鈕

3、路由事件的處理模型常用的有兩種:

  • 冒泡事件:由子控制元件位次向父容器傳遞,大部分的路由事件都是冒泡事件
  • 隧道事件:由父容器位次向其子容器、控制元件傳遞,一般PreXXX事件屬性隧道事件

4、使用路由事件響應方法中的e.Handled = true;意味著此事件已經被處理,將不再傳遞,預設e.Handled的值為false,意味著此路由事件還未處理完整,事件將依據其模型繼續向下處理(即執行其他的事件處理方法)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-590782/,如需轉載,請註明出處,否則將追究法律責任。

相關文章