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

WPF 自訂控件深度解析:Adorner 裝飾器機制與實戰

     編輯:關於.NET

WPF 的 Adorner(裝飾器) 是一種強大的視覺層擴充機制:不改變原控件視覺樹結構,即可在控件上方疊加任意視覺元素(如調整大小手柄、驗證錯誤圖示、拖曳預覽等)。本文深入剖析 AdornerLayer、Adorner 運作原理,並實作自訂遮罩控件。

核心架構:AdornerLayer 容器

AdornerLayer 是承載所有 Adorner 的容器,位於視覺樹中被裝飾控件的上層。其獲取方式:

var layer = AdornerLayer.GetAdornerLayer(targetVisual);

內部實現會沿視覺樹向上尋找 AdornerDecorator 或 ScrollContentPresenter(內建於 ScrollViewer、Window 等控件模板中),取得其 AdornerLayer 屬性。若視覺樹中缺乏這類容器(如自訂 Window 模板移除 ScrollViewer),需手動包裹 AdornerDecorator:

<AdornerDecorator>
  <TextBox Text="可獲得 AdornerLayer" />
</AdornerDecorator>

視覺樹層級固定:永遠在最上層

透過 Reflector 可見 ScrollContentPresenter.GetVisualChild:

case 0: return base.TemplateChild; // 被裝飾控件
case 1: return this._adornerLayer; // AdornerLayer 永遠是 index 1

WPF 先繪製 index 0 再繪製 index 1,故 Adorner 永遠覆蓋在原控件之上,ZIndex 設定無效。

自訂 Adorner 實作步驟

  1. 繼承抽象類別 Adorner,建構子傳入 adornedElement。
  2. 覆寫 VisualChildrenCount、GetVisualChild 回傳內部視覺元素(如 TextBlock)。
  3. 覆寫 ArrangeOverride 定位內部元素相對於被裝飾控件的位置。
public class SimpleTextAdorner : Adorner
{
    readonly TextBlock _text = new TextBlock { Foreground = Brushes.Green, Text = "AdornerText" };

    public SimpleTextAdorner(UIElement adorned) : base(adorned) { }

    protected override int VisualChildrenCount => 1;
    protected override Visual GetVisualChild(int index) => _text;

    protected override Size ArrangeOverride(Size finalSize)
    {
        _text.Arrange(new Rect(new Point(-10, 20), _text.DesiredSize));
        return base.ArrangeOverride(finalSize);
    }
}

掛載 Adorner

var layer = AdornerLayer.GetAdornerLayer(textBlock);
layer.Add(new SimpleTextAdorner(textBlock));

FlowDirection 自動反轉機制

當被裝飾控件 FlowDirection 變更,Adorner 會自動鏡像翻轉。原理在 AdornerLayer.ArrangeOverride 中呼叫 GetDesiredTransform 並套用 AdornerTransform(實際綁定至 RenderTransform),效能優於佈局重算。

內建控件應用 Adorner 實例

  • GridSplitter:拖曳分隔列時顯示預覽線,需 AdornerLayer 存在,否則拋 NullReferenceException。解決:外層包裹 AdornerDecorator 或 ScrollViewer。
  • Validation.ErrorTemplate:驗證錯誤的紅框、驚嘆號皆為 Adorner(TemplatedAdorner),預設模板寫死於程式碼,可透過 Validation.ErrorTemplate 自訂。

實戰:自訂遮罩控件

場景:資料載入時遮罩特定控件(如 ListView),顯示進度條與百分比,防止使用者操作。

<ListView ItemsSource="{Binding Employees}">
  <ctrl:MaskAttach.MaskAttach>
    <ctrl:MaskAttach DataContext="{Binding Progress}" Open="{Binding IsLoading}">
      <ctrl:MaskAttach.Template>
        <DataTemplate>
          <Grid>
            <Rectangle Fill="Black" Opacity="0.7" Grid.ColumnSpan="2"/>
            <TextBlock Grid.Column="1" Foreground="White" Text="{Binding}%"/>
            <ProgressBar Value="{Binding}" Height="20" Margin="10"/>
          </Grid>
        </DataTemplate>
      </ctrl:MaskAttach.Template>
    </ctrl:MaskAttach>
  </ctrl:MaskAttach.MaskAttach>
</ListView>

關鍵技術點:

  • 在 Dispatcher.BeginInvoke(DispatcherPriority.Render, ...) 中取得 AdornerLayer,確保視覺樹已建立。
  • 使用 DependencyPropertyDescriptor 監聽非 FrameworkElement 物件的屬性變更。
  • 將遮罩視覺元素加入視覺樹以攔截滑鼠事件;若需穿透則不加入。

Decorator 基類與設計模式對應

WPF Decorator 僅能裝飾單一子元素(Border、Viewbox、AdornerDecorator),對應 GoF Decorator 模式:以包裝方式擴充功能,不修改原物件。若需裝飾多元素則使用 Panel。

拖曳實作補充

標準 WPF 拖曳流程:MouseDown 取資料 → DragDrop.DoDragDrop → 目標端 PreviewDrop 取資料 → Drop 處理。跨 ItemsControl 拖曳可參考 Bea Stollnitz 經典實作。

心得與建議

初學 WPF 易陷入「全自繪」誤區,實則善用預設控件組合、樣式、模板、視覺/邏輯樹機制,才是團隊協作與維護的正道。自訂控件應作為最後手段,專注於效能極致或極特殊視覺需求。

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