首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 自訂控制項的 API 設計:相依性屬性、路由事件與命令實作全解析

     編輯:關於.NET

為什麼選擇相依性屬性?

WPF 元素多數功能透過屬性暴露,相依性屬性(DP)系統提供標準 CLR 屬性無法比擬的能力:

  • 值繼承:父元素設定可自動傳播給子元素(如 FontFamily、Cursor)。
  • 資料繫結、樣式、動畫:皆建立在 DP 系統之上。
  • 高效記憶體管理:只儲存「實際被設定」的屬性值,未設定者共享預設值,大幅減少實例欄位佔用。
  • 變更通知:DP 系統內建追蹤值變更,資料繫結依賴此機制。

定義相依性屬性(含附屬屬性)

public class MyCustomControl : ContentControl {
    public static readonly DependencyProperty FooProperty =
        DependencyProperty.Register("Foo", typeof(Brush),
            typeof(MyCustomControl), new PropertyMetadata(Brushes.Green));

    public Brush Foo {
        get { return (Brush)GetValue(FooProperty); }
        set { SetValue(FooProperty, value); }
    }
}
  • 慣例:public static readonly DependencyProperty XxxProperty。
  • PropertyMetadata 指定預設值、變更回呼等。
  • CLR 包裝屬性僅呼叫 GetValue/SetValue,方便 XAML 與程式碼使用。

附屬屬性

用 RegisterAttached 註冊,提供靜態 GetXxx/SetXxx 存取器:

public static readonly DependencyProperty IsBarProperty =
    DependencyProperty.RegisterAttached("IsBar", typeof(bool),
        typeof(ControlWithAttachedProp), new PropertyMetadata(false));

public static bool GetIsBar(DependencyObject target) => (bool)target.GetValue(IsBarProperty);
public static void SetIsBar(DependencyObject target, bool value) => target.SetValue(IsBarProperty, value);

XAML 使用:<Button local:ControlWithAttachedProp.IsBar="true" />

值變更通知

在 PropertyMetadata 傳入 PropertyInvalidatedCallback:

static ControlWithAttachedProp() {
    var callback = new PropertyInvalidatedCallback(OnIsBarChanged);
    var meta = new PropertyMetadata(false, callback);
    IsBarProperty = DependencyProperty.RegisterAttached("IsBar", typeof(bool),
        typeof(ControlWithAttachedProp), meta);
}
static void OnIsBarChanged(DependencyObject target) {
    Debug.WriteLine("IsBar changed: " + GetIsBar(target));
}

路由事件

在靜態建構式用 EventManager.RegisterRoutedEvent 註冊,提供 CLR 事件包裝器轉呼 AddHandler/RemoveHandler:

public static readonly RoutedEvent BarEvent =
    EventManager.RegisterRoutedEvent("Bar", RoutingStrategy.Bubble,
        typeof(RoutedEventHandler), typeof(MyCustomControl));

public event RoutedEventHandler Bar {
    add { AddHandler(BarEvent, value); }
    remove { RemoveHandler(BarEvent, value); }
}

protected virtual void OnBar() {
    var args = new RoutedEventArgs(PreviewBarEvent);
    RaiseEvent(args);
    if (!args.Handled) {
        args = new RoutedEventArgs(BarEvent);
        RaiseEvent(args);
    }
}
  • 成對定義 PreviewXxx (Tunnel) 與 Xxx (Bubble)。
  • RaiseEvent 同步呼叫所有處理器。

附屬事件

路由事件天生支援附屬用法,無需特殊註冊:

myButton.AddHandler(MyCustomControl.BarEvent, handler);
myButton.RaiseEvent(new RoutedEventArgs(MyCustomControl.BarEvent));

命令

定義新命令

public static readonly RoutedCommand FooCommand =
    new RoutedCommand("Foo", typeof(MyControl),
        new InputGestureCollection { new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift) });

XAML 綁定:<Button Command="local:MyControl.FooCommand">Click me</Button>

處理現有命令(類別級處理器)

在靜態建構式用 CommandManager.RegisterClassCommandBinding 註冊,處理器必須為 static:

static MyCustomControl() {
    var binding = new CommandBinding(ApplicationCommands.Copy, HandleCopyCommand);
    CommandManager.RegisterClassCommandBinding(typeof(MyCustomControl), binding);
}
private static void HandleCopyCommand(object target, ExecutedRoutedEventArgs e) {
    var ctrl = (MyCustomControl)target;
    // 實作複製邏輯
}
  • 類別級綁定只需註冊一次,自動套用至所有實例。
  • 處理器第一個參數為目標元素實例。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved