是的,你必须设置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 也相应改变。