Search code examples
c#wpfxamladorner

Custom window class and adorner layer


For design purposes I have implemented a custom window class with a style. The StylableWindow inherits from Window and mainly implements some dependency properties (e.g. for a title bar background color), does some border resizing logic and handles mouse interaction on the icon, titlebar and the additional menubay.

When I was trying to get an AdornerLayer (with the AdornerLayer.GetAdornerLayer(uiElement) method) I always got null as result.

After some investigation I tried to replace my custom window with the regular Window and voilà my adorners worked as intended.

No my question is: What part have I done wrong in my custom window implementation so that WPF cannot find any AdornerLayer?

Here is my implementation of the style. As the StylableWindow.cs mostly consists of dependency properties and interaction logic I don't think that the code is very helpful for this purpose.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:stylableWindow="clr-namespace:StylableWindow">

    <!--Base style for title bar buttons-->
    <Style x:Key="CaptionButtonStyle" TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="30">
                        <TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="Segoe MDL2 Assets" FontSize="10" 
                                   Foreground="{TemplateBinding Foreground}" HorizontalAlignment="Center" VerticalAlignment="Center"
                                   RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased"  TextOptions.TextFormattingMode="Display"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="LayoutRoot" Property="Background" Value="#E5E5E5"/>
                            <Setter TargetName="txt" Property="Foreground" Value="#000000"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--Minimize-->
    <Style x:Key="MinimizeButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
        <Setter Property="Content" Value="&#xE949;"/>
    </Style>

    <!--Maximize-->
    <Style x:Key="MaximizeButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
        <Setter Property="Content" Value="&#xE739;"/>
    </Style>

    <!--Restore-->
    <Style x:Key="RestoreButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
        <Setter Property="Content" Value="&#xE923;"/>
    </Style>

    <!--Close-->
    <Style x:Key="CloseButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
        <Setter Property="Content" Value="&#xE106;"/>
    </Style>

    <Style TargetType="stylableWindow:StylableWindow" x:Key="StylableWindowStyle">
        <Setter Property="Background" Value="{Binding Background}"/>
        <Setter Property="BorderBrush" Value="Black"/>
        <Setter Property="MinHeight" Value="10"/>
        <Setter Property="MinWidth" Value="200"/>
        <Setter Property="RenderOptions.BitmapScalingMode" Value="HighQuality"/>
        <Setter Property="Title" Value="{Binding Title}"/>
        <Setter Property="Icon" Value="{Binding Icon}"/>
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome GlassFrameThickness="1" 
                              ResizeBorderThickness="4"
                              CaptionHeight="0"/>
            </Setter.Value>
        </Setter>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type stylableWindow:StylableWindow}">

                    <Grid Background="Transparent" x:Name="WindowRoot">

                        <Grid x:Name="LayoutRoot"
                              Background="{TemplateBinding Background}">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="30"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>

                            <!--TitleBar-->
                            <Grid x:Name="PART_HeaderBar"
                                  Background="{TemplateBinding HeaderBarBackground}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>

                                <ContentControl Grid.Column="0"
                                                x:Name="AppIcon"
                                                ContentTemplate="{TemplateBinding HeaderBarIcon}"
                                                Margin="6 0 10 0">
                                </ContentControl>

                                <ContentControl Grid.Column="1" 
                                                Content="{TemplateBinding HeaderBarMenu}">
                                </ContentControl>

                                <Rectangle Grid.Column="2" Width="Auto" Height="20" Stroke="{TemplateBinding HeaderBarForeground}" Visibility="{TemplateBinding TitleBorderVisibility}" Margin="0 2 0 0"/>
                                <TextBlock Text="{TemplateBinding Title}" 
                                           Grid.Column="2"
                                           TextTrimming="CharacterEllipsis"
                                           FontSize="14"
                                           TextAlignment="Center"
                                           VerticalAlignment="Center"
                                           Padding="10 0 10 0"
                                           Foreground="{TemplateBinding HeaderBarForeground}"
                                           Panel.ZIndex="0"
                                           IsEnabled="{TemplateBinding IsActive}"/>

                                <Grid x:Name="WindowControlsGrid" Grid.Column="4" Background="Transparent">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                    </Grid.ColumnDefinitions>

                                    <Button x:Name="MinimizeButton"
                                            Foreground="{TemplateBinding HeaderBarForeground}"
                                            Style="{StaticResource MinimizeButtonStyle}" 
                                            Visibility="{TemplateBinding MinMaxVisibility}"
                                            Grid.Column="0"/>
                                    <Button x:Name="MaximizeButton" 
                                            Foreground="{TemplateBinding HeaderBarForeground}"
                                            Style="{StaticResource MaximizeButtonStyle}" 
                                            Visibility="{TemplateBinding MinMaxVisibility}"
                                            Grid.Column="1"/>
                                    <Button x:Name="RestoreButton" 
                                            Foreground="{TemplateBinding HeaderBarForeground}"
                                            Style="{StaticResource RestoreButtonStyle}" 
                                            Visibility="Collapsed"
                                            Grid.Column="1"/>
                                    <Button x:Name="CloseButton" 
                                            Foreground="{TemplateBinding HeaderBarForeground}"
                                            Style="{StaticResource CloseButtonStyle}" 
                                            Grid.Column="2"/>
                                </Grid>
                            </Grid>

                            <Grid x:Name="PART_MainContentGrid"
                                  Grid.Row="1"
                                  Panel.ZIndex="10">
                                <ContentPresenter x:Name="PART_MainContentPresenter" Grid.Row="0"/>
                            </Grid>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--<Style TargetType="{x:Type Window}" BasedOn="{StaticResource StylableWindowStyle}"/>-->

</ResourceDictionary>
<StylableWindow Style="{StaticResource StylableWindowStyle}">
    <StylableWindow.HeaderBarMenu>
        <!-- a menu bar for the title -->
    </StylableWindow.HeaderBarMenu>

    <Grid>
   <!-- content goes here -->
    </Grid>
</StylableWindow>

Solution

  • In your control template wrap your ContentPresenter with an AdornerDecorator like so:

    ...
    <Grid x:Name="PART_MainContentGrid"
        Grid.Row="1"
        Panel.ZIndex="10">
        <AdornerDecorator>
            <ContentPresenter x:Name="PART_MainContentPresenter" Grid.Row="0"/>
        </AdornerDecorator>
    </Grid>
    ...