*.WindowChrome方式实现自定义窗体的拖动、大小拖拽、系统菜单、双击最大化、双击正常等功能。
效果图如下:
1.创建资源字典:NBorderWindow.xaml,代码如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:o="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
xmlns:control="clr-namespace:MainApp.Client.UI.Controls">
<Geometry o:Freeze="True" x:Key="WindowMinGeometry">M0,4 L10,4 L10,5 L0,5 z</Geometry>
<Geometry o:Freeze="True" x:Key="WindowMaxGeometry">M1,1 L1,9 L9,9 L9,1 z M0,0 L10,0 L10,10 L0,10 z</Geometry>
<Geometry o:Freeze="True" x:Key="WindowRestoreGeometry">M1,3 L1,9 L7,9 L7,3 z M3,1 L3,2 L8,2 L8,7 L9,7 L9,1 z M2,0 L10,0 L10,8 L8,8 L8,10 L0,10 L0,2 L2,2 z</Geometry>
<Geometry o:Freeze="True" x:Key="CloseGeometry">M0.7,0 L5,4.3 L9.3,0 L10,0.7 L5.7,5 L10,9.3 L9.3,10 L5,5.7 L0.7,10 L0,9.3 L4.3,5 L0,0.7 z</Geometry>
<Style x:Key="NBorderWindow" TargetType="{x:Type control:NBorderWindow}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FF1B2A3C" Offset="1"/>
<GradientStop Color="#FF4D5F6F" Offset="0"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="1" />
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome UseAeroCaptionButtons="False" ResizeBorderThickness="6"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:NBorderWindow">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Border.Effect>
<DropShadowEffect Color="#FF1B2A3C" ShadowDepth="0" BlurRadius="5" Opacity="0.3" Direction="0"/>
</Border.Effect>
<Grid x:Name="windowGrid" Background="{TemplateBinding Background}">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
<Grid shell:WindowChrome.IsHitTestVisibleInChrome="True">
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Top" Orientation="Horizontal">
<control:PathGeometryButton x:Name="minimizedButton" Width="34" Height="30" PathGeometry="{StaticResource WindowMinGeometry}" Background="Transparent"
ToolTip="最小化" BgFocus="#F76B69" Foreground="White" BorderThickness="0" Cursor="Hand"
Command="SystemCommands.MinimizeWindowCommand"/>
<control:PathGeometryButton x:Name="restoreButton" Visibility="Collapsed" Width="34" Height="30" PathGeometry="{StaticResource WindowRestoreGeometry}" Background="Transparent"
ToolTip="还原" BgFocus="#F76B69" Foreground="White" BorderThickness="0" Cursor="Hand"
Command="SystemCommands.RestoreWindowCommand"/>
<control:PathGeometryButton x:Name="maximizedButton" Width="34" Height="30" PathGeometry="{StaticResource WindowMaxGeometry}" Background="Transparent"
ToolTip="最大化" BgFocus="#F76B69" Foreground="White" BorderThickness="0" Cursor="Hand"
Command="SystemCommands.MaximizeWindowCommand"/>
<control:PathGeometryButton Width="34" Height="30" PathGeometry="{StaticResource CloseGeometry}" Background="Transparent"
ToolTip="关闭" BgFocus="#F76B69" Foreground="White" BorderThickness="0" Cursor="Hand"
Command="SystemCommands.CloseWindowCommand"/>
</StackPanel>
</Grid>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode" Value="NoResize">
<Setter TargetName="minimizedButton" Property="Visibility" Value="Hidden" />
<Setter TargetName="maximizedButton" Property="Visibility" Value="Hidden" />
<Setter TargetName="restoreButton" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="ResizeMode" Value="CanMinimize">
<Setter TargetName="maximizedButton" Property="Visibility" Value="Hidden" />
<Setter TargetName="restoreButton" Property="Visibility" Value="Collapsed" />
<Setter TargetName="minimizedButton" Property="Margin" Value="0,0,40,0" />
</Trigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter TargetName="maximizedButton" Property="Visibility" Value="Collapsed" />
<Setter TargetName="restoreButton" Property="Visibility" Value="Visible" />
<Setter TargetName="windowGrid" Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(shell:WindowChrome.WindowChrome).ResizeBorderThickness}" />
<Setter TargetName="border" Property="BorderThickness" Value="0" />
</Trigger>
<Trigger Property="WindowState" Value="Normal">
<Setter TargetName="restoreButton" Property="Visibility" Value="Collapsed" />
<Setter TargetName="maximizedButton" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
2.这里使用了自定义Button封装控件,需创建一个UserControl,修改基类为Button,相关代码如下:
/// <summary>
/// 按钮:根据路径绘制
/// </summary>
public partial class PathGeometryButton : Button
{
public PathGeometryButton()
{
InitializeComponent();
}
#region PathGeometryProperty
/// <summary>
///路径
/// </summary>
public Geometry PathGeometry
{
get
{
return (Geometry)GetValue(PathGeometryProperty);
}
set
{
SetValue(PathGeometryProperty, value);
}
}
public static readonly DependencyProperty PathGeometryProperty =
DependencyProperty.RegisterAttached("PathGeometry", typeof(Geometry), typeof(PathGeometryButton), new PropertyMetadata(default(Geometry)));
#endregion
#region BgFocus
public Brush BgFocus
{
get
{
return (Brush)GetValue(BgFocusProperty);
}
set
{
SetValue(BgFocusProperty, value);
}
}
public static readonly DependencyProperty BgFocusProperty =
DependencyProperty.RegisterAttached("BgFocus", typeof(Brush), typeof(PathGeometryButton), new PropertyMetadata(default(Brush)));
#endregion
#region CornerRadiusProperty
/// <summary>
/// 边框圆角
/// </summary>
public CornerRadius CornerRadius
{
get
{
return (CornerRadius)GetValue(CornerRadiusProperty);
}
set
{
SetValue(CornerRadiusProperty, value);
}
}
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.RegisterAttached("CornerRadius", typeof(CornerRadius), typeof(PathGeometryButton), new PropertyMetadata(default(CornerRadius)));
#endregion
}
<Button x:Class="MainApp.Client.UI.Controls.PathGeometryButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MainApp.Client.UI.Controls"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" FocusVisualStyle="{x:Null}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Name="bg" Background="{TemplateBinding Background}" CornerRadius="{Binding Path=CornerRadius,RelativeSource={RelativeSource TemplatedParent}}"
BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<Path x:Name="p" Width="10" Height="10" Data="{Binding Path=PathGeometry,RelativeSource={RelativeSource TemplatedParent}}" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" Stretch="Uniform"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bg" Property="Background" Value="{Binding Path=BgFocus,RelativeSource={RelativeSource TemplatedParent},FallbackValue=Transparent,TargetNullValue=Transparent}"/>
<Setter Property="Fill" Value="White" TargetName="p"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
3.创建一个NBorderWindow类,继承至Window,相关代码如下:
/// <summary>
/// 无边框窗体
/// </summary>
public class NBorderWindow : Window
{
#region Constructor
static NBorderWindow()
{
StyleProperty.OverrideMetadata(typeof(NBorderWindow), new FrameworkPropertyMetadata(NBorderWindow.GetResource<Style>(nameof(NBorderWindow))));
}
public NBorderWindow()
{
this.Loaded += NBorderWindow_Loaded;
}
#endregion
#region NBorderWindow_Loaded
private void NBorderWindow_Loaded(object sender, RoutedEventArgs e)
{
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand,
(s, ex) => WindowState = WindowState.Minimized));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand,
(s, ex) => WindowState = WindowState.Maximized));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand,
(s, ex) => WindowState = WindowState.Normal));
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, (s, ex) =>
{
if (this.CloseCommand != null)
{
this.CloseCommand.Execute(null);
}
else
{
Close();
}
}));
}
#endregion
#region GetResource
/// <summary>
///获取资源
/// </summary>
public static T GetResource<T>(string key)
{
if (Application.Current.TryFindResource(key) is T resource)
{
return resource;
}
return default(T);
}
#endregion
#region CloseCommand
public ICommand CloseCommand
{
get
{
return (ICommand)GetValue(CloseCommandProperty);
}
set
{
SetValue(CloseCommandProperty, value);
}
}
public static readonly DependencyProperty CloseCommandProperty =
DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(NBorderWindow),
new FrameworkPropertyMetadata((ICommand)null));
#endregion
}
4.在App.xaml中绑定所创建的资源字典,如下:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/NBorderWindow.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
5.关于WindowChrome相关属性,可查阅WindowChrome 类 (System.Windows.Shell) | Microsoft Learn