Avalonia学习(二十一)-自定义窗口标题框

看见网上一篇文章,写自定义窗口,借花献佛,整理了一下,延续界面演示。

1.定义窗口,去除边框、

设置属性:

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

2.定义顶端标题框

创建一个用户控件,用来实现顶端控件。

前端代码

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
             x:Class="CustomTitleBarTemplate.Views.CustomTitleBars.WindowsTitleBar"
             DockPanel.Dock="Top">
  <StackPanel>
    <Grid>
    <!--The proper way would be not to use white as default, but somehow retrieve the users' window chrome color.-->
      <DockPanel Background="White"
                 IsHitTestVisible="False"
                 Name="TitleBarBackground"></DockPanel>
      <DockPanel Name="TitleBar">
        <StackPanel Orientation="Horizontal"
                    DockPanel.Dock="Left"
                    Spacing="0">
          <Image Source="/Assets/Images/Icon.ico"
                 Height="20"
                 Width="20"
                 VerticalAlignment="Center"
                 Margin="5,0,3,0"
                 Name="WindowIcon"></Image>
          <NativeMenuBar DockPanel.Dock="Top"
                         Width="100"
                         HorizontalAlignment="Left"
                         Name="SeamlessMenuBar"></NativeMenuBar>
          <TextBlock Text="Custom TitleBar Demo"
                     FontSize="12"
                     Foreground="Black"
                     VerticalAlignment="Center"
                     IsHitTestVisible="False"
                     Name="SystemChromeTitle"></TextBlock>
        </StackPanel>
        <StackPanel Height="22" 
					HorizontalAlignment="Right"
                    Orientation="Horizontal"
                    Spacing="0"
					VerticalAlignment="Top">
          <Button Width="46"
				  VerticalAlignment="Stretch"
                  HorizontalContentAlignment="Center"
                  BorderThickness="0"
                  Name="MinimizeButton"
                  ToolTip.Tip="Minimize">
            <Button.Resources>
              <CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
            </Button.Resources>
            <Button.Styles>
              <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="#44AAAAAA"/>
              </Style>
              <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="Transparent"/>
              </Style>
            </Button.Styles>
            <Path Margin="10,0,10,0"
                  Stretch="Uniform"
				  VerticalAlignment="Center"
                  Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
                  Data="M2048 1229v-205h-2048v205h2048z"></Path>
          </Button>

          <Button Width="46"
                  VerticalAlignment="Stretch"
                  BorderThickness="0"
                  Name="MaximizeButton">
            <ToolTip.Tip>
              <ToolTip Content="Maximize"
                       Name="MaximizeToolTip"></ToolTip>
            </ToolTip.Tip>

            <Button.Resources>
              <CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
            </Button.Resources>
            <Button.Styles>
              <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="#44AAAAAA"/>
              </Style>
              <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="Transparent"/>
              </Style>
            </Button.Styles>
            <Path Margin="10,0,10,0"
                  Stretch="Uniform"
                  Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
                  Name="MaximizeIcon"
                  Data="M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z"></Path>
          </Button>

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

        </StackPanel>
      </DockPanel>
    </Grid>
    <NativeMenuBar HorizontalAlignment="Left"
                   Name="DefaultMenuBar"></NativeMenuBar>
  </StackPanel>
</UserControl>

后台代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace CustomTitleBarTemplate.Views.CustomTitleBars
{
    public partial class WindowsTitleBar : UserControl
    {
        private Button minimizeButton;
        private Button maximizeButton;
        private Path maximizeIcon;
        private ToolTip maximizeToolTip;
        private Button closeButton;
        private Image windowIcon;

        private DockPanel titleBar;
        private DockPanel titleBarBackground;
        private TextBlock systemChromeTitle;
        private NativeMenuBar seamlessMenuBar;
        private NativeMenuBar defaultMenuBar;

        public static readonly StyledProperty<bool> IsSeamlessProperty =
        AvaloniaProperty.Register<WindowsTitleBar, bool>(nameof(IsSeamless));

        /// <summary>
        /// 窗口移动
        /// </summary>
        public event EventHandler<PointerPressedEventArgs> OnPointerMouseHander;

        public bool IsSeamless
        {
            get { return GetValue(IsSeamlessProperty); }
            set
            {
                SetValue(IsSeamlessProperty, value);
                if (titleBarBackground != null && 
                    systemChromeTitle != null &&
                    seamlessMenuBar != null &&
                    defaultMenuBar != null)
                {
                    titleBarBackground.IsVisible = IsSeamless ? false : true;
                    systemChromeTitle.IsVisible = IsSeamless ? false : true;
                    seamlessMenuBar.IsVisible = IsSeamless ? true : false;
                    defaultMenuBar.IsVisible = IsSeamless ? false : true;

                    if (IsSeamless == false)
                    {
                        titleBar.Resources["SystemControlForegroundBaseHighBrush"] = new SolidColorBrush { Color = new Color(255, 0, 0, 0) };
                    }
                }
            }
        }

        public WindowsTitleBar()
        {
            this.InitializeComponent();
            this.PointerPressed += WindowsTitleBar_PointerPressed; ;
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == false)
            {
                this.IsVisible = false;
            }
            else
            {
                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;

                titleBar = this.FindControl<DockPanel>("TitleBar");
                titleBarBackground = this.FindControl<DockPanel>("TitleBarBackground");
                systemChromeTitle = this.FindControl<TextBlock>("SystemChromeTitle");
                seamlessMenuBar = this.FindControl<NativeMenuBar>("SeamlessMenuBar");
                defaultMenuBar = this.FindControl<NativeMenuBar>("DefaultMenuBar");
                

                SubscribeToWindowState();
            }
        }

        private void WindowsTitleBar_PointerPressed(object? sender, PointerPressedEventArgs e)
        {
            if (e.Pointer.Type == PointerType.Mouse)
            {


                if (OnPointerMouseHander != null)
                {
                    OnPointerMouseHander(sender, e);
                }
            }
        }

       

        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 = 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 = 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";

                    // This should be a more universal approach in both cases, but I found it to be less reliable, when for example double-clicking the title bar.
                    /*hostWindow.Padding = new Thickness(
                            hostWindow.OffScreenMargin.Left,
                            hostWindow.OffScreenMargin.Top,
                            hostWindow.OffScreenMargin.Right,
                            hostWindow.OffScreenMargin.Bottom);*/
                }
            });
        }

        private void InitializeComponent()
        {
            AvaloniaXamlLoader.Load(this);
        }
    }
}

在Window窗口中使用

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:WindowAvalonia.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="WindowAvalonia.Views.MainWindow"
          xmlns:titlebars="clr-namespace:CustomTitleBarTemplate.Views.CustomTitleBars;assembly=WindowAvalonia"
        x:DataType="vm:MainWindowViewModel"
        WindowStartupLocation="CenterScreen"
        ExtendClientAreaToDecorationsHint="True"
        ExtendClientAreaChromeHints="NoChrome"
        ExtendClientAreaTitleBarHeightHint="-1"
        Icon="/Assets/avalonia-logo.ico"
        Title="WindowAvalonia">

    <Design.DataContext>
        <!-- This only sets the DataContext for the previewer in an IDE,
             to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
        <vm:MainWindowViewModel/>
    </Design.DataContext>

  <NativeMenu.Menu>
    <NativeMenu>
      <NativeMenuItem Header="File">
        <NativeMenuItem.Menu>
          <NativeMenu>
            <NativeMenuItem Header="Open"/>
            <NativeMenuItem Header="Recent">
              <NativeMenuItem.Menu>
                <NativeMenu/>
              </NativeMenuItem.Menu>
            </NativeMenuItem>
            <NativeMenuItem Header="Quit Avalonia"
                            Gesture="CTRL+Q"
                            Command="{Binding QuitProgramCommand}"/>
          </NativeMenu>
        </NativeMenuItem.Menu>
      </NativeMenuItem>
      <NativeMenuItem Header="Edit">
        <NativeMenuItem.Menu>
          <NativeMenu>
            <NativeMenuItem Header="Copy"/>
            <NativeMenuItem Header="Paste"/>
          </NativeMenu>
        </NativeMenuItem.Menu>
      </NativeMenuItem>
    </NativeMenu>
  </NativeMenu.Menu>

  <Window.KeyBindings>
    <KeyBinding Gesture="CTRL+Q"
                Command="{Binding QuitProgramCommand}"></KeyBinding>
  </Window.KeyBindings>
  
  <DockPanel HorizontalAlignment="Stretch"
           VerticalAlignment="Stretch"
           Name="ContentWrapper">
    
    <titlebars:WindowsTitleBar IsSeamless="True"  Name="titleBar"    Background="rgb(30,159,255)" BorderBrush="rgb(196,230,255)"> </titlebars:WindowsTitleBar>
   
    <DockPanel Background="Transparent">
      <StackPanel HorizontalAlignment="Left"
                  VerticalAlignment="Top"
                  Orientation="Horizontal"
                  Spacing="10">
       
       
        <SplitButton Content="连接">
          <SplitButton.Flyout>
            <MenuFlyout Placement="Bottom">
              <MenuItem Header="项目1">
                <MenuItem Header="子项1" />
                <MenuItem Header="子项2" />
                <MenuItem Header="子项3" />
              </MenuItem>
              <MenuItem Header="项目2"
                        InputGesture="Ctrl+A" />
              <MenuItem Header="项目3" />
            </MenuFlyout>
          </SplitButton.Flyout>
        </SplitButton>
        <Button Content="新建"></Button>
      </StackPanel>
      <DockPanel>
        <TreeView BorderBrush="Green">
          
        </TreeView>
        <TabControl BorderBrush="Red"></TabControl>
        <TextBlock Padding="10"></TextBlock>
      </DockPanel>
    </DockPanel>
  </DockPanel>

</Window>
using Avalonia.Controls;

namespace WindowAvalonia.Views
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            titleBar.OnPointerMouseHander += TitleBar_OnPointerMouseHander; ;
        }

        private void TitleBar_OnPointerMouseHander(object? sender, Avalonia.Input.PointerPressedEventArgs e)
        {
            this.BeginMoveDrag(e);
        }
    }
}

运行效果

目前没有收集到大家 感兴趣的东西写,先以此界面,仿照navicat吧

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值