【问题标题】:Easier way in .net Avalonia to change the background color of the Window's System Top bar?在 .net Avalonia 中更容易更改 Window 系统顶部栏的背景颜色?
【发布时间】:2020-12-17 00:23:10
【问题描述】:

在 dotnet 的 Avalonia-UI 框架中。 我使用的是深色 UI,我设法按照 this example 将所有内容都设置为深色,但有一件事:Windows 操作系统中窗口的系统顶部栏。

我在this issue in github 中看到我可以设置属性HasSystemDecorations="false" 以使其消失,但是我必须自己实现具有拖动功能、标题、关闭、最大化、最小化等的顶部栏,当我只想更改背景颜色时,这很痛苦。

将窗口顶部栏更改为深色背景颜色的更简单方法是什么?

如果唯一的方法是使用HasSystemDecorations,那么实现具有关闭/最小化/最大化/拖动的通用功能的深色顶栏的最小示例是什么?

【问题讨论】:

    标签: c# .net-core avaloniaui


    【解决方案1】:

    是的,你必须设置HasSystemDecorations="false" 并实现你自己的标题栏。我在Github 上有一个基本模板,说明如何使用 0.10 版和流利的主题来做到这一点。

    其实很简单,因为 Avalonia 提供了很多方便的方法来实现这一点。

    概述:

    设置

    ExtendClientAreaToDecorationsHint="True"
    ExtendClientAreaChromeHints="NoChrome"
    ExtendClientAreaTitleBarHeightHint="-1"
    

    为Window然后实现一个titlebar。例如,关闭按钮可能如下所示:

    <Button Width="46"
            VerticalAlignment="Stretch"
            BorderThickness="0"
            Name="CloseButton"
            ToolTip.Tip="Close">
        <Button.Resources>
          <CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
        </Button.Resources>
        <Button.Styles>
          <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Background" Value="Red"/>
          </Style>
          <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Background" Value="Transparent"/>
          </Style>
          <Style Selector="Button:pointerover > Path">
            <Setter Property="Fill" Value="White"/>
          </Style>
          <Style Selector="Button:not(:pointerover) > Path">
            <Setter Property="Fill" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
          </Style>
        </Button.Styles>
        <Path Margin="10,0,10,0"
              Stretch="Uniform"
              Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z"></Path>
      </Button>
    
    

    如果您在控件上设置IsHitTestVisible="False",则可以在该区域拖动下面的窗口。因此,将整个标题栏包装在例如 DockPanel 中:

    <DockPanel Background="Black"
               IsHitTestVisible="False"
               Name="TitleBarBackground"></DockPanel>
    

    现在您显然仍然需要模仿按钮的行为。原则上可以这样做(再次查看上面的 Github 存储库的具体示例):

    minimizeButton = this.FindControl<Button>("MinimizeButton");
    maximizeButton = this.FindControl<Button>("MaximizeButton");
    maximizeIcon = this.FindControl<Path>("MaximizeIcon");
    maximizeToolTip = this.FindControl<ToolTip>("MaximizeToolTip");
    closeButton = this.FindControl<Button>("CloseButton");
    windowIcon = this.FindControl<Image>("WindowIcon");
    
    minimizeButton.Click += MinimizeWindow;
    maximizeButton.Click += MaximizeWindow;
    closeButton.Click += CloseWindow;
    windowIcon.DoubleTapped += CloseWindow;
    
    private void CloseWindow(object sender, Avalonia.Interactivity.RoutedEventArgs e)
    {
          Window hostWindow = (Window)this.VisualRoot;
          hostWindow.Close();
    }
    
    private void MaximizeWindow(object sender, Avalonia.Interactivity.RoutedEventArgs e)
    {
         Window hostWindow = (Window)this.VisualRoot;
    
         if (hostWindow.WindowState == WindowState.Normal)
         {
               hostWindow.WindowState = WindowState.Maximized;
         }
         else
         {
               hostWindow.WindowState = WindowState.Normal;
         }
    }
    
    private void MinimizeWindow(object sender, Avalonia.Interactivity.RoutedEventArgs e)
    {
          Window hostWindow = (Window)this.VisualRoot;
          hostWindow.WindowState = WindowState.Minimized;
    }
    

    现在最后一步是您需要根据窗口状态更改最大化按钮的图标。例如,如果您拖动一个最大化的窗口,它将自动恢复向下,并且最大化按钮的图标需要更改。因此,您需要订阅宿主窗口的窗口状态,可以这样做:

    private async void SubscribeToWindowState()
    {
            Window hostWindow = (Window)this.VisualRoot;
    
            while (hostWindow == null)
            {
                hostWindow = (Window)this.VisualRoot;
                await Task.Delay(50);
            }
    
            hostWindow.GetObservable(Window.WindowStateProperty).Subscribe(s =>
            {
                if (s != WindowState.Maximized)
                {
                    maximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z");
                    hostWindow.Padding = new Thickness(0,0,0,0);
                    maximizeToolTip.Content = "Maximize";
                }
                if (s == WindowState.Maximized)
                {
                    maximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 1638h-410v410h-1638v-1638h410v-410h1638v1638zm-614-1024h-1229v1229h1229v-1229zm409-409h-1229v205h1024v1024h205v-1229z");
                    hostWindow.Padding = new Thickness(7,7,7,7);
                    maximizeToolTip.Content = "Restore Down";
                }
            });
        }
    
    

    其实上面的sn-p还有一个细节,需要注意。至少在 Windows 上,最大化的窗口实际上比屏幕大。如果您不希望您的内容超出屏幕范围,则需要在窗口内的主控件中添加边距。因此hostWindow 的Padding 也相应改变。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-17
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 2015-09-26
      • 1970-01-01
      • 2017-09-19
      • 2016-01-03
      相关资源
      最近更新 更多