How to draw WPF Adorners on top of everything else?

5.6k Views Asked by At

I've added an Adorner to my DateTimePicker control but it's not shown on top of the other controls. Why? How do I fix it?

My adorner

My XAML currently goes like this:

<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>

The adorner itself is a separate class from the UserControl and added in the constructor:

public DateTimePicker()
{
    InitializeComponent();
    ...

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

There are 2 best solutions below

5
On

There's already an adorner layer in the default Window style, and that adorner layer sits above the content of the window.

So just remove the AdornerLayer from the UserControl and that should work.

0
On

The problem apparently occurs because the Adorners governed by the AdornerDecorator are only guaranteed to appear on top of the controls inside the AdornerDecorator. It is necessary to wrap most of the contents of the window in an AdornerDecorator instead, but after doing this, AdornerLayer.GetAdornerLayer() apprently can't see the AdornerDecorator under some circumstances and returns null.

The documentation claims "GetAdornerLayer walks up the visual tree, starting at the specified UIElement, and returns the first adorner layer it finds." In reality, GetAdornerLayer cannot find an AdornerDecorator located outside of the UserControl, at least not in .NET 3.5. I fixed the problem by doing exactly what GetAdornerLayer claims to do itself:

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);
    };
}

Finally, GetAdornerLayer must be called from the Loaded event instead of the constructor.