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

WPF 控件模板化深度解析:從默認模板到可替換控件庫構建

     編輯:關於.NET

WPF 將控件邏輯與視覺外觀徹底分離,通過 ControlTemplate 實現無代碼修改外觀。本文由淺入深,講解元素與控件差異、默認模板在 DLL 中的打包規範、Control 派生要點、代碼與模板交互協定 (TemplateBinding、Trigger、Command、Part)、以及模板替換實戰。

元素 vs 控件:核心區別

  • FrameworkElement:基礎視覺單元 (TextBlock, Image, Panel, Decorator),用於「顯示」,無 Tab 導航,無默認模板機制,視覺樹靠重寫 MeasureOverride/ArrangeOverride/OnRender 構建。
  • Control (派生自 FrameworkElement):增加 Foreground/Background/Font* 等外觀屬性、IsTabStop/TabIndex (支援 Tab 導航)、核心屬性 Template (ControlTemplate)。視覺樹完全由 Template 定義,無需重寫渲染方法。控件用於「交互」。
  • UserControl:組合控件快捷方式,視覺樹定義在 XAML Content 中,默認模板固定 (Border + ContentPresenter),不建議替換模板,代碼與視覺樹緊耦合。複雜可替換控件請繼承 Control。

DLL 中默認模板的嚴格打包規範

  1. 專案包含 Themes 文件夾,內有 generic.xaml (根元素 ResourceDictionary)。
  2. generic.xaml 定義針對控件類型的 Style,Setter Property="Template" 設置 ControlTemplate。
  3. 模板可拆分為獨立 XAML (如 CalendarMonthStyle.xaml),通過 ResourceDictionary.MergedDictionaries 在 generic.xaml 中引用 (Pack URI: AssemblyName;Component/Themes/xxx.xaml)。
  4. 控件類靜態構造函數必須覆蓋 DefaultStyleKey 元數據:
    DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
  5. 程序集級屬性聲明主題位置:
    [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]

Control 派生核心技術要點

  • 依賴屬性:所有公共屬性 (甚至唯讀) 皆用依賴屬性,支援綁定、動畫、樣式、繼承、通知。
  • AddOwner 復用現有屬性:如 FrameworkElement.WidthProperty.AddOwner(typeof(MyControl)),避免重複定義。
  • 改變默認值:用 OverrideMetadata (低優先級) 或默認模板 Style 中的 Setter,禁止在構造函數賦值 (本地值優先級最高,阻斷樣式/繼承)。
  • 屬性變更通知:OverrideMetadata 註冊 PropertyChangedCallback。

代碼與模板交互四大協定

  1. TemplateBinding:模板綁定控件屬性 (如 Background={TemplateBinding Background}),雙向同步。
  2. Trigger (Property/Event/Multi/Event):模板響應屬性變化/事件 (如 IsToday=true 觸發高亮)。
  3. RoutedCommand:控件定義靜態 RoutedCommand 字段,模板按鈕 Command="ns:ControlClass.CmdName" 綁定,構造函數 CommandBindings.Add 掛接執行邏輯,支援鍵勢 (InputGestures) 自動觸發。
  4. Part 命名約定:控件標記 [TemplatePart(Name="PART_Panel", Type=typeof(Panel))],OnApplyTemplate 中 Template.FindName("PART_Panel", this) 獲取模板內關鍵元素 (面板、按鈕等),類型盡量寬泛 (Panel 而非 UniformGrid),找不到優雅降級。

模板替換實戰:從垂直日歷到 3D 月相

  • 外部替換:應用層定義 Style TargetType="cc:CalendarMonth" 完全重寫視覺樹 (如垂直列表 12 個月並排)。
  • 派生擴展:CalendarMonth.DayType 屬性指定日生成類型 (預設 CalendarDay),替換為 CalendarDayNotes (含備忘錄 TextBox) 或 MoonDisk (3D Viewport3D 模擬月相),無需修改月曆邏輯。
  • 樣式微調:針對內部生成的 CalendarDay 定義隱式 Style (TargetType="cc:CalendarDay") 設置邊框、對齊、字體,無需替換整個模板。

完整示例代碼參考《Applications = Code + Markup》第 25 章及配套 DumpControlTemplate 工具提取標準控件默認模板學習。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved