Skip to content

TopLevel.GetTopLevel() returns null from controls inside WindowDrawnDecorations #21487

@AVVI94

Description

@AVVI94

Describe the bug

Issue:

When using WindowDrawnDecorations, controls placed inside it cannot obtain the parent TopLevel via TopLevel.GetTopLevel(this).

var topLevel = TopLevel.GetTopLevel(this);

where this is a control hosted inside WindowDrawnDecorations.Overlay.

GetTopLevel() returns null because WindowDrawnDecorations does not appear to be part of the visual tree. The content is only connected through the logical tree, which makes APIs that rely on visual tree traversal unable to locate the owning window/top-level.

This is surprising because controls hosted in WindowDrawnDecorations are visually rendered as part of the window chrome, yet they behave differently from regular window content.

Is this intentional behavior? If so, what is the recommended way for controls inside WindowDrawnDecorations to access their owning TopLevel or Window?

It would be helpful if either:

  • TopLevel.GetTopLevel() worked as expected from controls hosted in WindowDrawnDecorations, or
  • the documentation explicitly described this limitation and the recommended alternative approach.

Actual behavior:

TopLevel.GetTopLevel(this) returns null because the control is not connected to the visual tree leading to the window.

To Reproduce

Place a custom control into WindowDrawnDecorations.Overlay and try resolving the TopLevel via TopLevel.GetTopLevel(this) from code-behind.

Expected behavior

TopLevel.GetTopLevel(this) should return the owning TopLevel for controls hosted inside WindowDrawnDecorations.

Avalonia version

12.0.4

OS

Windows

Additional context

We encountered this while implementing a DialogHost. We placed it in WindowDrawnDecorations.Overlay so that the dialog overlay would also cover the title bar and window content. In this scenario, TopLevel.GetTopLevel() returns null from controls inside the decorations content, making it impossible to obtain the owning window through the usual API. Additionally, Popups (combobox, context menu, flyout...) do not appear to work in this scenario.

TextBox events for copy/cut do not work either with this setup.

Our current WindowDrawnDecorations theme:

<ResourceDictionary xmlns="https://github.com/avaloniaui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
                    xmlns:dialogHostEx="clr-namespace:ICAFluentUINet.AvaloniaUI.Controls.ContentDialog;assembly=ICAFluentUINet.AvaloniaUI"
                    xmlns:ic="clr-namespace:ICAFluentUINet.AvaloniaUI.Controls.WindowChrome;assembly=ICAFluentUINet.AvaloniaUI">
    <!--  Add Resources Here  -->
    <ControlTheme x:Key="{x:Type WindowDrawnDecorations}"
                  TargetType="WindowDrawnDecorations">
        <Setter Property="DefaultTitleBarHeight" Value="{Binding $parent[Window].(ic:TitleBarEx.TitleBarHeight)}" />
        <Setter Property="DefaultFrameThickness" Value="1" />
        <Setter Property="DefaultShadowThickness" Value="8" />
        <!--<Setter Property="ic:TitleBarEx.TitleBarHeight" Value="50" />-->
        <Setter Property="Template">
            <WindowDrawnDecorationsTemplate>
                <WindowDrawnDecorationsContent>

                    <WindowDrawnDecorationsContent.Underlay>
                        <Panel x:Name="PART_UnderlayWrapper">
                            <Border x:Name="PART_WindowBorder"
                                    Background="{DynamicResource SystemControlTransparentBrush}"
                                    BorderBrush="{DynamicResource SystemControlForegroundBaseMediumBrush}"
                                    BorderThickness="{TemplateBinding FrameThickness}"
                                    IsHitTestVisible="False" />
                        </Panel>
                    </WindowDrawnDecorationsContent.Underlay>

                    <WindowDrawnDecorationsContent.Overlay>
                        <!--  Overlay: only interactive caption buttons  -->
                        <Panel x:Name="PART_OverlayWrapper">
                            <Grid x:Name="PART_Container"
                                  Grid.Row="0"
                                  Grid.Column="0"
                                  HorizontalAlignment="Stretch"
                                  VerticalAlignment="Stretch"
                                  RowDefinitions="auto,*">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="auto" />
                                    <ColumnDefinition Width="auto" />
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="auto" />
                                    <ColumnDefinition Width="auto" />
                                </Grid.ColumnDefinitions>
                                <dialogHost:DialogHost x:Name="PART_DialogHost"
                                                       Grid.RowSpan="2"
                                                       Grid.Column="0"
                                                       Grid.ColumnSpan="5"
                                                       HorizontalAlignment="Stretch"
                                                       VerticalAlignment="Stretch"
                                                       AutomationProperties.ControlTypeOverride="Pane"
                                                       CloseOnClickAway="False"
                                                       Identifier="{Binding $parent[Window].(dialogHostEx:DialogHostEx.Identifier)}"
                                                       ZIndex="99" />
                                <!--  ColumnDefinitions="auto,*,auto,135"  -->
                                <Panel x:Name="PART_TitleBar"
                                       Grid.Column="0"
                                       Grid.ColumnSpan="5"
                                       Height="{TemplateBinding TitleBarHeight}"
                                       VerticalAlignment="Top"
                                       Background="Transparent"
                                       Classes.TopMost="{Binding #PART_DialogHost.IsOpen}"
                                       IsVisible="{TemplateBinding HasTitleBar}"
                                       WindowDecorationProperties.ElementRole="TitleBar">
                                    <Panel.Styles>
                                        <Style Selector="Panel.TopMost">
                                            <Setter Property="ZIndex" Value="2147483647" />
                                        </Style>
                                    </Panel.Styles>
                                </Panel>
                                <Panel x:Name="PART_TitleBarBackground"
                                       Grid.Column="0"
                                       Grid.ColumnSpan="5"
                                       Height="{TemplateBinding TitleBarHeight}"
                                       VerticalAlignment="Top"
                                       Background="{Binding $parent[Window].(ic:TitleBarEx.Background)}"
                                       IsVisible="{TemplateBinding HasTitleBar}"
                                       ZIndex="-1" />

                                <Panel x:Name="PART_TitleTextPanel"
                                       VerticalAlignment="Stretch"
                                       IsVisible="{TemplateBinding HasTitleBar}">
                                    <StackPanel Grid.Row="0"
                                                Grid.Column="1"
                                                VerticalAlignment="Top"
                                                Orientation="Horizontal"
                                                Spacing="12">
                                        <Button x:Name="PART_BackButton"
                                                MinHeight="30"
                                                MaxHeight="30"
                                                VerticalAlignment="Top"
                                                AutomationProperties.Name="Back"
                                                Command="{Binding $parent[Window].(ic:TitleBarEx.BackButtonCommand)}"
                                                IsVisible="{Binding $parent[Window].(ic:TitleBarEx.BackButtonIsVisible)}"
                                                Theme="{StaticResource FluentDrawnCaptionButton}"
                                                WindowDecorationProperties.ElementRole="User">
                                            <Viewbox Width="15"
                                                     Margin="2">
                                                <Path Data="{StaticResource ArrowLeftRegularIcon_Path}"
                                                      Fill="{Binding $parent[Window].Foreground}"
                                                      Stretch="UniformToFill" />
                                            </Viewbox>
                                        </Button>
                                        <TextBlock x:Name="PART_Title"
                                                   Margin="12,0,0,0"
                                                   VerticalAlignment="Center"
                                                   AutomationProperties.Name="{Binding $self.Text}"
                                                   Classes.MarginTop="{Binding !#PART_BackButton.IsVisible}"
                                                   IsHitTestVisible="False"
                                                   IsVisible="{Binding $parent[Window].(ic:TitleBarEx.TitleIsVisible)}"
                                                   Text="{Binding $parent[Window].Title}">
                                            <TextBlock.Styles>
                                                <Style Selector="TextBlock.MarginTop">
                                                    <Setter Property="Margin" Value="12 6 0 0" />
                                                </Style>
                                                <Style Selector="TextBlock:not(TextBlock.MarginTop)">
                                                    <Setter Property="Margin" Value="12 0 0 0" />
                                                </Style>
                                            </TextBlock.Styles>
                                        </TextBlock>
                                    </StackPanel>
                                </Panel>
                                <ContentPresenter x:Name="PART_TitleBarExContent"
                                                  Grid.Row="0"
                                                  Grid.Column="2"
                                                  HorizontalAlignment="Stretch"
                                                  VerticalAlignment="Top"
                                                  Content="{Binding $parent[Window].(ic:TitleBarEx.Content)}"
                                                  WindowDecorationProperties.ElementRole="User" />
                                <StackPanel x:Name="PART_OverlayPanel"
                                            Grid.Column="4"
                                            HorizontalAlignment="Right"
                                            VerticalAlignment="Top"
                                            IsVisible="{TemplateBinding HasTitleBar}"
                                            Orientation="Horizontal"
                                            Spacing="2"
                                            TextElement.FontSize="10"
                                            ZIndex="2147483647">
                                    <Button x:Name="PART_HelpButton"
                                            Grid.Column="3"
                                            MinHeight="30"
                                            MaxHeight="30"
                                            VerticalAlignment="Top"
                                            AutomationProperties.Name="Help"
                                            Classes.Overlayed="{Binding #PART_DialogHost.IsOpen}"
                                            Command="{Binding $parent[Window].(ic:TitleBarEx.HelpButtonCommand)}"
                                            FontSize="14"
                                            IsVisible="{Binding $parent[Window].(ic:TitleBarEx.HelpButtonIsVisible)}"
                                            Theme="{StaticResource FluentDrawnCaptionButton}">
                                        <Button.Styles>
                                            <Style Selector="Button.Overlayed">
                                                <Setter Property="IsEnabled" Value="False" />
                                            </Style>
                                        </Button.Styles>
                                        <Grid HorizontalAlignment="Stretch"
                                              VerticalAlignment="Stretch">
                                            <TextBlock HorizontalAlignment="Center"
                                                       VerticalAlignment="Center"
                                                       Text="?" />
                                        </Grid>
                                    </Button>
                                    <Button x:Name="PART_FullScreenButton"
                                            Theme="{StaticResource FluentDrawnCaptionButton}"
                                            WindowDecorationProperties.ElementRole="FullScreenButton">
                                        <Viewbox Width="11"
                                                 Margin="2">
                                            <Path Name="FullScreenButtonPath"
                                                  Data="M2048 2048v-819h-205v469l-1493 -1493h469v-205h-819v819h205v-469l1493 1493h-469v205h819z"
                                                  Fill="{DynamicResource CaptionButtonForeground}"
                                                  Stretch="UniformToFill" />
                                        </Viewbox>
                                    </Button>
                                    <Button x:Name="PART_MinimizeButton"
                                            Theme="{StaticResource FluentDrawnCaptionButton}"
                                            WindowDecorationProperties.ElementRole="MinimizeButton">
                                        <Viewbox Width="11"
                                                 Margin="2">
                                            <Path Data="M2048 1229v-205h-2048v205h2048z"
                                                  Fill="{DynamicResource CaptionButtonForeground}"
                                                  Stretch="UniformToFill" />
                                        </Viewbox>
                                    </Button>
                                    <Button x:Name="PART_MaximizeButton"
                                            Theme="{StaticResource FluentDrawnCaptionButton}"
                                            WindowDecorationProperties.ElementRole="MaximizeButton">
                                        <Viewbox Width="11"
                                                 Margin="2">
                                            <Viewbox.RenderTransform>
                                                <RotateTransform Angle="-90" />
                                            </Viewbox.RenderTransform>
                                            <Path Name="RestoreButtonPath"
                                                  Data="M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z"
                                                  Fill="{DynamicResource CaptionButtonForeground}"
                                                  Stretch="UniformToFill" />
                                        </Viewbox>
                                    </Button>
                                    <Button x:Name="PART_CloseButton"
                                            Background="#ffe81123"
                                            BorderBrush="#fff1707a"
                                            Theme="{StaticResource FluentDrawnCaptionButton}"
                                            WindowDecorationProperties.ElementRole="CloseButton">
                                        <Viewbox Width="11"
                                                 Margin="2">
                                            <Path Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z"
                                                  Fill="{DynamicResource CaptionButtonForeground}"
                                                  Stretch="UniformToFill" />
                                        </Viewbox>
                                    </Button>
                                </StackPanel>
                            </Grid>
                        </Panel>
                    </WindowDrawnDecorationsContent.Overlay>

                    <WindowDrawnDecorationsContent.FullscreenPopover>
                        <Panel Height="{TemplateBinding DefaultTitleBarHeight}"
                               VerticalAlignment="Top"
                               Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
                               WindowDecorationProperties.ElementRole="TitleBar">
                            <TextBlock Margin="12,0,0,0"
                                       VerticalAlignment="Center"
                                       FontSize="12"
                                       Text="{TemplateBinding Title}" />
                            <StackPanel HorizontalAlignment="Right"
                                        Orientation="Horizontal"
                                        Spacing="2"
                                        TextElement.FontSize="10">
                                <Button x:Name="PART_PopoverFullScreenButton"
                                        Theme="{StaticResource FluentDrawnCaptionButton}"
                                        WindowDecorationProperties.ElementRole="FullScreenButton">
                                    <Viewbox Width="11"
                                             Margin="2">
                                        <Path Data="M205 1024h819v-819h-205v469l-674 -674l-145 145l674 674h-469v205zM1374 1229h469v-205h-819v819h205v-469l674 674l145 -145z"
                                              Fill="{DynamicResource CaptionButtonForeground}"
                                              Stretch="UniformToFill" />
                                    </Viewbox>
                                </Button>
                                <Button x:Name="PART_PopoverCloseButton"
                                        Background="#ffe81123"
                                        BorderBrush="#fff1707a"
                                        Theme="{StaticResource FluentDrawnCaptionButton}"
                                        WindowDecorationProperties.ElementRole="CloseButton">
                                    <Viewbox Width="11"
                                             Margin="2">
                                        <Path Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z"
                                              Fill="{DynamicResource CaptionButtonForeground}"
                                              Stretch="UniformToFill" />
                                    </Viewbox>
                                </Button>
                            </StackPanel>
                        </Panel>
                    </WindowDrawnDecorationsContent.FullscreenPopover>

                </WindowDrawnDecorationsContent>
            </WindowDrawnDecorationsTemplate>
        </Setter>

        <!--  Shadow: inset border and add drop shadow  -->
        <Style Selector="^:has-shadow /template/ Panel#PART_UnderlayWrapper">
            <Setter Property="Margin" Value="{TemplateBinding ShadowThickness}" />
        </Style>
        <Style Selector="^:has-shadow /template/ Border#PART_WindowBorder">
            <Setter Property="BoxShadow" Value="0 2 10 2 #80000000" />
        </Style>
        <Style Selector="^:has-shadow /template/ Panel#PART_OverlayWrapper">
            <Setter Property="Margin" Value="{TemplateBinding ShadowThickness}" />
        </Style>

        <!--  Border: inset titlebar and buttons inside frame  -->
        <Style Selector="^:has-border /template/ Panel#PART_TitleTextPanel">
            <Setter Property="Margin" Value="1,1,1,0" />
        </Style>
        <Style Selector="^:has-border /template/ Panel#PART_TitleBar">
            <Setter Property="Margin" Value="1,1,1,0" />
        </Style>
        <Style Selector="^:has-border /template/ StackPanel#PART_OverlayPanel">
            <Setter Property="Margin" Value="0,1,1,0" />
        </Style>

        <!--  Maximized: restore button path changes  -->
        <Style Selector="^:maximized /template/ Path#RestoreButtonPath">
            <Setter Property="Data" Value="M2048 410h-410v-410h-1638v1638h410v410h1638v-1638zM1434 1434h-1229v-1229h1229v1229zM1843 1843h-1229v-205h1024v-1024h205v1229z" />
        </Style>

        <!--  Disabled buttons  -->
        <Style Selector="^ /template/ Button:disabled">
            <Setter Property="Opacity" Value="0.2" />
        </Style>

        <!--  Hide caption buttons when the platform does not support the action  -->
        <Style Selector="^:not(:has-minimize) /template/ Button#PART_MinimizeButton">
            <Setter Property="IsVisible" Value="False" />
        </Style>
        <Style Selector="^:not(:has-maximize) /template/ Button#PART_MaximizeButton">
            <Setter Property="IsVisible" Value="False" />
        </Style>
        <Style Selector="^:not(:has-fullscreen) /template/ Button#PART_FullScreenButton">
            <Setter Property="IsVisible" Value="False" />
        </Style>

        <Style Selector="^:not(:has-fullscreen) /template/ Button#PART_PopoverFullScreenButton">
            <Setter Property="IsVisible" Value="False" />
        </Style>

        <!--  Fullscreen: hide overlay and titlebar (popover takes over)  -->
        <Style Selector="^:fullscreen /template/ Panel#PART_TitleTextPanel">
            <Setter Property="IsVisible" Value="False" />
        </Style>
        <Style Selector="^:fullscreen /template/ StackPanel#PART_OverlayPanel">
            <Setter Property="IsVisible" Value="False" />
        </Style>
        <Style Selector="^:fullscreen /template/ Panel#PART_TitleBar">
            <Setter Property="IsVisible" Value="False" />
        </Style>
    </ControlTheme>
</ResourceDictionary>

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions