自定义 window class 和装饰层

Custom window class and adorner layer

出于设计目的,我实现了带有样式的自定义 window class。 StylableWindow 继承自 Window 并主要实现一些依赖属性(例如标题栏背景颜色),执行一些边框调整逻辑并处理鼠标交互图标、标题栏和附加菜单栏。

当我尝试获取 AdornerLayer(使用 AdornerLayer.GetAdornerLayer(uiElement) 方法)时,我总是得到 null 作为结果。

经过一些调查后,我尝试用常规 Window 替换我的自定义 window,瞧,我的装饰器按预期工作了。

不,我的问题是:我在自定义 window 实现中哪里做错了,以至于 WPF 找不到任何 AdornerLayer?

这是我实现的样式。由于 StylableWindow.cs 主要由依赖属性和交互逻辑组成,我认为该代码对此目的没有太大帮助。

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

在您的控件模板中,用 AdornerDecorator 包装您的 ContentPresenter,如下所示:

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