如何自訂WPF的DependencyProperty相依屬性

撰文:吳俊毅
時間:2010/02/16

  在WPF當中若沒有相依屬性幾乎無法做任何事情,因為您可能要重複定義許多共同的物件屬性,這不僅增加Pg許多無意義的工作且程式設計較沒有彈性。要實做WPF的相依屬性我們得在一個class中先宣告一個DependencyProperty物件,再定義一個屬性,這個屬性做為屬性變更告知之用,而透過DependencyProperty的連結,比較重要的是在相依屬性裡並不是使用我們在.NET應用程式裡慣用的get;set;的方式,而是使用在DependencyObject物件裡定義的SetValue()與GetValue()方法,如下程式:

class ColorTextBlock: TextBlock
{
  public static readonly DependencyProperty BackgroundColorProperty;
  public bool ChangedBackgroundColor
  {
    get
    {
    return (bool)GetValue(BackgroundColorProperty);
    }
    set { SetValue(BackgroundColorProperty, value); }
    }
  }

筆者希望透過ChangedBackgroundColor屬性被變更為true時,將自身物件的Background改為Blue,因此必須註冊BackgroundColorProperty這個DependencyProperty,以便告訴WPF的子系統有這個相依的事件必須在這個屬性遭到變更時觸發!怎麼做呢?首先我們必須在constructor裡放置FrameworkPropertyMetadata物件存放與DependencyProperty相關的屬性資料並設定其DefaultValue屬性與PropertyChangedCallback的事件處理常式,最後註冊相依屬性ChangedBackgroundColor,程式碼如下:
static ColorTextBlock()
{
  FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
  meta.DefaultValue = false;
  meta.Inherits = true;
  meta.PropertyChangedCallback += OnBackgroundColorPropertyChanged;
  BackgroundColorProperty = DependencyProperty.Register("ChangedBackgroundColor", typeof(bool), typeof(ColorTextBlock), meta);
}

  如程式,要註冊DependencyProperty只要執行DependencyProperty.Register方法,傳入(引數1
:相依屬性名稱,引數2:為觸發該相依屬性的屬性type,引數3:本身class的型別,引數4:PropertyMetadata物件)。OnBackgroundColorPropertyChanged的事件的實作如下:
static void OnBackgroundColorPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
  ColorTextBlock ctb = obj as ColorTextBlock;
  ctb.Background = Brushes.Blue;
}

  這裡所傳入的DependencyObject即是ColorTextBlock物件,而這個事件只有當屬性ChangedBackgroundColor被變更時會觸發,所以筆者只是將背景設為Blue而已,要測試此程式我們可以在Grid_Load裡加入如下:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
  ColorTextBlock ctb = new ColorTextBlock();
  ctb.Text = "abcdefghijk";
  ctb.HorizontalAlignment = HorizontalAlignment.Left;
  ctb.VerticalAlignment = VerticalAlignment.Top;
  ctb.ChangedBackgroundColor = true;
  Grid1.Children.Add(ctb);
}

  程式的行結果如下:


  若將ctb.ChangedBackgroundColor = true 這行拿掉底色就不會變成藍色,這點讀者可以自行測試看看。

  而將ctb.ChangedBackgroundColor設定為false底色也不會改變這是因為我們對這個DependencyProperty的DefaultValue屬性給的是false,所以您再塞false進去當然對於系統而言屬性不算是被變更!所以不會觸發OnBackgroundColorPropertyChanged 這個事件。

留言

  1. 自訂特別的控制項,這樣的方法蠻實用的,只是很不解的是只要繼承任何控制項,原本在Xmal可以參考到的事件,全部都無法正常參考

    例如這篇文章自訂的ColorTextBlock控制項,在一個視窗加入兩個控制項,一個是自訂的,一個是原本的



    在code behind的地方,會發現ColorTextBlock_IsEnabledChanged的參考是0,TextBlock_IsEnabledChanged是1,不過程式可以正常運作,事件也可以正常引發

    平常這樣就算了,在Xmal分別把程式碼改成這樣,但是code behind不改



    只要一編譯,TextBlock_IsEnabledChanged_Error這個會報錯誤,ColorTextBlock_IsEnabledChanged_Error會過關
    如果只修改TextBlock_IsEnabledChanged_Error的錯誤,一執行,引發ColorTextBlock_IsEnabledChanged_Error事件就會錯誤

    回覆刪除
    回覆
    1. <local:ColorTextBlock IsEnabledChanged="ColorTextBlock_IsEnabledChanged"/>
      <TextBlock IsEnabledChanged="TextBlock_IsEnabledChanged"/>

      刪除

張貼留言

這個網誌中的熱門文章

軟體工程師 - 成長的 10 個階段

常見的程式碼壞味道(Code Smell or Bad Smell)

什麼是 gRPC ?