目录
- 简介
- 效果图
- 自定义最小化、最大化、关闭按钮
- 布局实现
- 结语
简介
截取Microsoft文章的一段话:
若要在保留其标准功能时自定义窗口,可以使用该 WindowChrome 类。 该 WindowChrome 类将窗口框架的功能与视觉对象分开,并允许你控制应用程序窗口的客户端和非客户端区域之间的边界。 通过 WindowChrome 该类,可以通过扩展工作区来覆盖非工作区,将 WPF 内容置于窗口框架中。 同时,它通过两个不可见区域保留系统行为: 调整边框 和 标题 区域的大小。
效果图
自定义最小化、最大化、关闭按钮
最小化按钮
<Style | |
x:Key="SystemCloseButtonStyle" | |
BasedOn="{StaticResource SystemButtonStyleBase}" | |
TargetType="{x:Type Button}"> | |
<Setter Property="Template"> | |
<Setter.Value> | |
<ControlTemplate TargetType="{x:Type Button}"> | |
<Grid Background="{TemplateBinding Background}"> | |
<Viewbox Width="12" Height="12"> | |
<Path Data="M550.848 502.496l308.64-308.896a31.968 31.968 0 1 0-45.248-45.248l-308.608 308.896-308.64-308.928a31.968 31.968 0 1 0-45.248 45.248l308.64 308.896-308.64 308.896a31.968 31.968 0 1 0 45.248 45.248l308.64-308.896 308.608 308.896a31.968 31.968 0 1 0 45.248-45.248l-308.64-308.864z" Fill="{TemplateBinding BorderBrush}" /> | |
</Viewbox> | |
<ContentPresenter | |
HorizontalAlignment="Center" | |
VerticalAlignment="Center" | |
Content="{TemplateBinding Content}" /> | |
</Grid> | |
<ControlTemplate.Triggers> | |
<Trigger Property="IsMouseOver" Value="True"> | |
<Setter Property="Background" Value="{StaticResource CloseColor}" /> | |
<Setter Property="BorderBrush" Value="{StaticResource DominantColor}" /> | |
</Trigger> | |
<Trigger Property="IsFocused" Value="True"> | |
<Setter Property="FocusVisualStyle" Value="{x:Null}" /> | |
</Trigger> | |
</ControlTemplate.Triggers> | |
</ControlTemplate> | |
</Setter.Value> | |
</Setter> | |
</Style> |
最大化按钮
<Style | |
x:Key="SystemMaxButtonStyle" | |
BasedOn="{StaticResource SystemButtonStyleBase}" | |
TargetType="{x:Type Button}"> | |
<Setter Property="Template"> | |
<Setter.Value> | |
<ControlTemplate TargetType="{x:Type Button}"> | |
<Grid Background="{TemplateBinding Background}"> | |
<Viewbox Width="12" Height="12"> | |
<Path Data="M959.72 0H294.216a63.96 63.96 0 0 0-63.96 63.96v127.92H64.28A63.96 63.96 0 0 0 0.32 255.84V959.4a63.96 63.96 0 0 0 63.96 63.96h703.56a63.96 63.96 0 0 0 63.96-63.96V792.465h127.92a63.96 63.96 0 0 0 63.96-63.96V63.96A63.96 63.96 0 0 0 959.72 0zM767.84 728.505V959.4H64.28V255.84h703.56z m189.322 0H831.8V255.84a63.96 63.96 0 0 0-63.96-63.96H294.216V63.96H959.72z" Fill="{TemplateBinding BorderBrush}" /> | |
</Viewbox> | |
<ContentPresenter | |
HorizontalAlignment="Center" | |
VerticalAlignment="Center" | |
Content="{TemplateBinding Content}" /> | |
</Grid> | |
<ControlTemplate.Triggers> | |
<Trigger Property="IsMouseOver" Value="True"> | |
<Setter Property="Background" Value="{StaticResource SuspensionColor}" /> | |
</Trigger> | |
<Trigger Property="IsFocused" Value="True"> | |
<Setter Property="FocusVisualStyle" Value="{x:Null}" /> | |
</Trigger> | |
</ControlTemplate.Triggers> | |
</ControlTemplate> | |
</Setter.Value> | |
</Setter> | |
</Style> |
关闭按钮
<Style | |
x:Key="SystemMinButtonStyle" | |
BasedOn="{StaticResource SystemButtonStyleBase}" | |
TargetType="{x:Type Button}"> | |
<Setter Property="Template"> | |
<Setter.Value> | |
<ControlTemplate TargetType="{x:Type Button}"> | |
<Grid Background="{TemplateBinding Background}"> | |
<Viewbox Width="12" Height="10"> | |
<Path Data="M928.2 548h-832c-17.7 0-32-14.3-32-32s14.3-32 32-32h832c17.7 0 32 14.3 32 32s-14.3 32-32 32z" Fill="{TemplateBinding BorderBrush}" /> | |
</Viewbox> | |
<ContentPresenter | |
HorizontalAlignment="Center" | |
VerticalAlignment="Center" | |
Content="{TemplateBinding Content}" /> | |
</Grid> | |
<ControlTemplate.Triggers> | |
<Trigger Property="IsMouseOver" Value="True"> | |
<Setter Property="Background" Value="{StaticResource SuspensionColor}" /> | |
</Trigger> | |
<Trigger Property="IsFocused" Value="True"> | |
<Setter Property="FocusVisualStyle" Value="{x:Null}" /> | |
</Trigger> | |
</ControlTemplate.Triggers> | |
</ControlTemplate> | |
</Setter.Value> | |
</Setter> | |
</Style> | |
/// <summary> | |
/// 窗口移动 | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void Move_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) => this.DragMove(); | |
/// <summary> | |
/// 最小化 | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void BtnMin_Click(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized; | |
/// <summary> | |
/// 最大化/还原 | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void BtnMax_Click(object sender, RoutedEventArgs e) => WindowState = WindowState is WindowState.Normal ? WindowState.Maximized : WindowState.Normal; | |
/// <summary> | |
/// 关闭 | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void BtnClose_Click(object sender, RoutedEventArgs e) => Application.Current.Shutdown(); |
布局实现
首先我们需要在 MainWindow 也就是我们的主窗口中的 Window.Resources 中实现 WindowChrome 的基本样式:
WindowChrome.ResizeBorderThickness 设置不可见边框宽度
WindowChrome.CaptionHeight> 设置属于标题栏的范围——高度
WindowChrome.UseAeroCaptionButtons 是否启用默认系统按钮功能——三大金刚键
WindowChrome.NonClientFrameEdges 设置客户区域,使用 bottom 可以实现加载时空白窗口而不显示默认窗口,提升用户体验
<Window | |
x:Class="SignalRClient.MainWindow" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
xmlns:local="clr-namespace:SignalRClient" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
Title="" | |
Width="880" | |
Height="620" | |
MinWidth="700" | |
MinHeight="500" | |
Style="{StaticResource mainWindow}" | |
WindowChrome.WindowChrome="{DynamicResource WindowChromeKey}" | |
WindowStartupLocation="CenterScreen" | |
mc:Ignorable="d"> | |
<Window.Resources> | |
<WindowChrome x:Key="WindowChromeKey"> | |
<WindowChrome.ResizeBorderThickness> | |
<Thickness>5</Thickness> | |
</WindowChrome.ResizeBorderThickness> | |
<WindowChrome.CaptionHeight>60</WindowChrome.CaptionHeight> | |
<WindowChrome.UseAeroCaptionButtons>false</WindowChrome.UseAeroCaptionButtons> | |
<WindowChrome.NonClientFrameEdges>bottom</WindowChrome.NonClientFrameEdges> | |
</WindowChrome> | |
</Window.Resources> | |
</Window> |
重写窗口,实现最大化窗口下,标题栏及客户区域偏移问题的修正。
通过代码获取当前窗口的工作区域,及任务栏以外的其他区域
System.Windows.SystemParameters.WorkArea.Width 获取工作区域的宽
System.Windows.SystemParameters.WorkArea.Height 获取工作区域的高
为什么要使用 ValueConverter 主要是因为 WorkArea 返回的类型无法直接 binding xaml
<ValueConverters:WorkAreaWidth x:Key="workAreaWidth" /> | |
<ValueConverters:WorkAreaHeight x:Key="workAreaHeight" /> | |
<Style x:Key="mainWindow" TargetType="{x:Type Window}"> | |
<Setter Property="Template"> | |
<Setter.Value> | |
<ControlTemplate TargetType="Window"> | |
<ContentControl x:Name="window" Content="{TemplateBinding Content}" /> | |
<ControlTemplate.Triggers> | |
<Trigger Property="WindowState" Value="Maximized"> | |
<Setter TargetName="window" Property="MaxHeight" Value="{Binding Converter={StaticResource workAreaHeight}}" /> | |
<Setter TargetName="window" Property="MaxWidth" Value="{Binding Converter={StaticResource workAreaWidth}}" /> | |
</Trigger> | |
</ControlTemplate.Triggers> | |
</ControlTemplate> | |
</Setter.Value> | |
</Setter> | |
</Style> | |
using System; | |
using System.Globalization; | |
using System.Windows.Data; | |
namespace SignalRClient.ValueConverters | |
{ | |
internal class WorkAreaWidth : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
return System.Windows.SystemParameters.WorkArea.Width; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
} | |
using System; | |
using System.Globalization; | |
using System.Windows.Data; | |
namespace SignalRClient.ValueConverters | |
{ | |
internal class WorkAreaHeight : IValueConverter | |
{ | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
return System.Windows.SystemParameters.WorkArea.Height; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
} |
结语
一开始,确实很难搞,Microsoft 的文档,里面也并没有,详细介绍窗口内容溢出的问题,但是只要仔细研究过 WPF 的同学都知道,很多东西是可以通过 Trigger
来实现的。Get 到这一点很多问题就迎刃而解了。