如何在其他一切之上绘制 WPF 装饰器?

Posted

技术标签:

【中文标题】如何在其他一切之上绘制 WPF 装饰器?【英文标题】:How to draw WPF Adorners on top of everything else? 【发布时间】:2012-11-15 00:27:26 【问题描述】:

我在DateTimePicker control 中添加了一个装饰器,但它没有显示在其他控件之上。为什么?我该如何解决?

我的 XAML 目前是这样的:

<UserControl x:Class="IntelliMap.WPF.DateTimePicker"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:wpftc="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
             mc:Ignorable="d">
    ...
    <AdornerDecorator>
        <Grid>
            ...
            <TextBox x:Name="DateDisplay" 
                         HorizontalAlignment="Stretch" ...>
            </TextBox>
            ...
        </Grid>
    </AdornerDecorator>
</UserControl>

装饰器本身是一个独立于 UserControl 的类,并添加到构造函数中:

public DateTimePicker()

    InitializeComponent();
    ...

    AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(DateDisplay);
    if (adornerLayer != null)
    
        adornerLayer.Add(_upDownBtns = new TextBoxUpDownAdorner(DateDisplay));
        _upDownBtns.Click += (textBox, direction) =>  OnUpDown(direction); ;
    

【问题讨论】:

【参考方案1】:

这个问题显然是因为由AdornerDecorator 管理的 Adorner 只能保证出现在 AdornerDecorator 内的控件之上。有必要将窗口的大部分内容包装在 AdornerDecorator 中,但这样做之后,AdornerLayer.GetAdornerLayer() 在某些情况下显然看不到 AdornerDecorator 并返回 null。

文档声称“GetAdornerLayer 从指定的UIElement 开始沿着可视化树向上走,并返回它找到的第一个装饰层。”实际上,GetAdornerLayer 找不到位于UserControl 之外的AdornerDecorator,至少在 .NET 3.5 中找不到。我完全按照GetAdornerLayer 声称自己的方式解决了这个问题:

static AdornerLayer GetAdornerLayer(FrameworkElement subject)

    AdornerLayer layer = null;
    do 
        if ((layer = AdornerLayer.GetAdornerLayer(subject)) != null)
            break;
     while ((subject = subject.Parent as FrameworkElement) != null);
    return layer;

public DateTimePicker()

    InitializeComponent();
    ...
    this.Loaded += (s, e) =>
    
        // not null anymore!
        AdornerLayer adLayer = GetAdornerLayer(DateDisplay);
    ;

最后,GetAdornerLayer 必须从 Loaded 事件而不是构造函数中调用。

【讨论】:

【参考方案2】:

在默认的 Window 样式中已经有一个装饰层,并且该装饰层位于窗口内容的上方。

所以只需从 UserControl 中删除 AdornerLayer 就可以了。

【讨论】:

AdornerDecorator 存在是因为没有它,GetAdornerLayer 返回 null。 啊,在控件的激活事件中进行装饰器设置,而不是构造函数。 GetAdornerLayer 获取窗口 AdornerLayer 之前,控件需要在屏幕上。 什么活动? UserControl 上没有激活事件。我试过Loaded,但GetAdornerLayer 在那个事件中仍然返回null。 在用户控件中,覆盖ApplyTemplate

以上是关于如何在其他一切之上绘制 WPF 装饰器?的主要内容,如果未能解决你的问题,请参考以下文章

Adorner 装饰器

如何在 WPF 中为装饰器设置 Z 顺序索引

保存 WPF 装饰器

第十篇:装饰器

如何在vue.js 2应用程序中全局使用自定义装饰器?

python函数装饰器