【1.42 自定义窗体的几个关键问题】WPF案例代码解析
WPF自定义窗体的几个关键问题
- 自定义标题栏内容
- 标题栏背景色
- 最大化、最小化、关闭按钮样式
- 最大化后窗体尺寸覆盖任务栏问题
- 窗体拖拽问题
- 标题栏双击还原、最大化问题
自定义标题栏内容
xaml 里设置 WindowStyle 为 None,同时设置最大宽度和最大高度(后台设置也可)
WindowStyle="None"
Topmost="{Binding ElementName=MyTopMost,Path=IsChecked}"
MaxWidth="{StaticResource {x:Static SystemParameters.MaximizedPrimaryScreenWidthKey}}"
MaxHeight="{StaticResource {x:Static SystemParameters.MaximizedPrimaryScreenHeightKey}}"
xaml 里自定义标题栏内容
从左往右是图标、标题、自定义的几个按钮(置顶,最小化,最大化、向下还原,关闭)
<!-- 设计标题栏 -->
<Grid MouseMove="Move_MouseMove" MouseDown="Grid_MouseDown" Background="#FF63BBD0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Path x:Name="MyResizeIcon" Data="{DynamicResource IconWindowMax}"/>
<Image Source="icon.ico" Height="32" Margin="5,0"/>
<TextBlock Grid.Column="1" Text="GeometryX" FontSize="24" VerticalAlignment="Center" Margin="5"/>
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
<ToggleButton x:Name="MyTopMost" hc:IconElement.Geometry="{DynamicResource IconPin}" ToolTip="置顶" Style="{DynamicResource MyToggleButtonIconFont}"
hc:IconElement.Width="8" Width="32"/>
<Button hc:IconElement.Geometry="{DynamicResource IconWindowMin}" Style="{DynamicResource MyWindowButton}"
hc:IconElement.Width="10" ToolTip="最小化" Width="32" Click="ButtonMin_Click"/>
<Button x:Name="MyButtonResize" hc:IconElement.Geometry="{Binding ElementName=MyResizeIcon,Path=Data}" Style="{DynamicResource MyWindowButton}"
hc:IconElement.Width="10" ToolTip="最大化" Width="32" Click="ButtonResize_Click"/>
<Button hc:IconElement.Geometry="{DynamicResource IconWindowClose}" Style="{DynamicResource MyWindowButton}"
hc:IconElement.Width="10" ToolTip="关闭" Width="32" Click="ButtonClose_Click">
<Button.Resources>
<SolidColorBrush x:Key="MouseEnterBrush" Color="{DynamicResource WindowClosedMouseEnterColor}"/>
</Button.Resources>
</Button>
</StackPanel>
</Grid>
后台代码
/// <summary>
/// 设置窗体尺寸
/// </summary>
private void SetWindowSize()
if (WindowState == WindowState.Maximized)
WindowState = WindowState.Normal;
MyResizeIcon.Data = (Geometry)FindResource("IconWindowMax");
MyButtonResize.ToolTip = "最大化";
WindowState = WindowState.Maximized;
MyResizeIcon.Data = (Geometry)FindResource("IconWindowRestore");
MyButtonResize.ToolTip = "向下还原";
private void ButtonMin_Click(object sender, RoutedEventArgs e)
WindowState = WindowState.Minimized;
private void ButtonClose_Click(object sender, RoutedEventArgs e)
Close();
private void ButtonResize_Click(object sender, RoutedEventArgs e)
SetWindowSize();
private void Move_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
DragMove();
// 拖动后窗体变成默认的
WindowState = WindowState.Normal;
MyResizeIcon.Data = (Geometry)FindResource("IconWindowMax");
MyButtonResize.ToolTip = "最大化";
private void Grid_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
if (e.ChangedButton == System.Windows.Input.MouseButton.Left && e.ClickCount == 2)
SetWindowSize();
标题栏背景色
直接设置 Background 即可
最大化、最小化、关闭按钮样式
先到 Iconfont 官网下载一些图标,放到资源文件 Geometry.xaml
定义一个资源文件 Brush.xaml 保存颜色资源
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryColor">#FF326CF3</Color>
<Color x:Key="DarkPrimaryColor">#FF326CF3</Color>
<Color x:Key="AccentColor">#FFF8491E</Color>
<Color x:Key="DarkAccentColor">#FFF8491E</Color>
<Color x:Key="DangerColor">#FFDB3340</Color>
<Color x:Key="DarkDangerColor">#FFDB3340</Color>
<Color x:Key="ErrorColor">#FFDB3340</Color>
<Color x:Key="DarkErrorColor">#FFDB3340</Color>
<Color x:Key="WarningColor">#FFE9AF20</Color>
<Color x:Key="DarkWarningColor">#FFE9AF20</Color>
<Color x:Key="InfoColor">#FF00BCD4</Color>
<Color x:Key="DarkInfoColor">#FF00BCD4</Color>
<Color x:Key="SuccessColor">#FF2DB84d</Color>
<Color x:Key="DarkSuccessColor">#FF2DB84d</Color>
<Color x:Key="MouseEnterColor">#FFDCDCDC</Color>
<Color x:Key="DarkMouseEnterColor">#FFDCDCDC</Color>
<Color x:Key="MousePressedColor">#FFD3D3D3</Color>
<Color x:Key="DarkMousePressedColor">#FFD3D3D3</Color>
<Color x:Key="WindowClosedMouseEnterColor">#FFF07C82</Color>
<LinearGradientBrush x:Key="PrimaryBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource PrimaryColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkPrimaryColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkPrimaryBrush" Color="{DynamicResource DarkPrimaryColor}"/>
<LinearGradientBrush x:Key="AccentBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource AccentColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkAccentColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkAccentBrush" Color="{DynamicResource DarkAccentColor}"/>
<LinearGradientBrush x:Key="DangerBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource DangerColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkDangerColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkDangerBrush" Color="{DynamicResource DarkDangerColor}"/>
<LinearGradientBrush x:Key="ErrorBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource ErrorColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkErrorColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkErrorBrush" Color="{DynamicResource DarkErrorColor}"/>
<LinearGradientBrush x:Key="WarningBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource WarningColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkWarningColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkWarningBrush" Color="{DynamicResource DarkWarningColor}"/>
<LinearGradientBrush x:Key="InfoBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource InfoColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkInfoColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkInfoBrush" Color="{DynamicResource DarkInfoColor}"/>
<LinearGradientBrush x:Key="SuccessBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource SuccessColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkSuccessColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkSuccessBrush" Color="{DynamicResource DarkSuccessColor}"/>
<LinearGradientBrush x:Key="MouseEnterBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource MouseEnterColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkMouseEnterColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkMouseEnterBrush" Color="{DynamicResource DarkMouseEnterColor}"/>
<LinearGradientBrush x:Key="MousePressedBrush" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="{DynamicResource MousePressedColor}" Offset="0"/>
<GradientStop Color="{DynamicResource DarkMousePressedColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="DarkMousePressedBrush" Color="{DynamicResource DarkMousePressedColor}"/>
</ResourceDictionary>
在 App.xaml 里添加对资源的引用
<Application x:Class="GeometryX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<!-- 主题样式 -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
<ResourceDictionary Source="/Resources/Geometry.xaml"/>
<ResourceDictionary Source="/Resources/Brush.xaml"/>
<ResourceDictionary Source="/Resources/Style.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
置顶按钮是一个 ToggleButton,样式如下
<Style x:Key="MyToggleButtonIconFont" BasedOn="{StaticResource ToggleButtonBaseStyle}" TargetType="ToggleButton">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
<Setter Property="hc:BorderElement.CornerRadius" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<ContentControl HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<Path x:Name="MyPath" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" Stretch="Uniform" Width="{TemplateBinding hc:IconElement.Width}"
Height="{TemplateBinding hc:IconElement.Height}" Data="{TemplateBinding hc:IconElement.Geometry}"/>
</Grid>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<!-- 鼠标停留时 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Gainsboro"/>
</Trigger>
<!-- 选中时 -->
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="MyPath" Property="Fill" Value="{DynamicResource PrimaryBrush}"/>
<Setter Property="Background" Value="Gainsboro"/>
</Trigger>
<!-- 鼠标按下时 -->
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="LightGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
最小化,最大化、向下还原,关闭按钮样式
关闭按钮在鼠标停留时背景色为红色(和 Windows 类似),使用时修改引用的资源颜色值即可
<Style x:Key="MyWindowButton" BasedOn="{StaticResource ButtonBaseStyle}" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
<Setter Property="hc:BorderElement.CornerRadius" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<ContentControl HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<Path x:Name="MyPath" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" Stretch="Uniform" Width="{TemplateBinding hc:IconElement.Width}"
Height="{TemplateBinding hc:IconElement.Height}" Data="{TemplateBinding hc:IconElement.Geometry}"/>
</Grid>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<!-- 鼠标停留时 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource MouseEnterBrush}"/>
</Trigger>
<!-- 鼠标按下时 -->
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource MousePressedBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
最大化后窗体尺寸覆盖任务栏问题
设置 WindowState = WindowState.Maximized 有一个问题就是,最大化后会覆盖任务栏
只需要设置最大高度和最大宽度即可
窗体拖 拽问题
在标题栏的 MouseMove 事件里设置窗体拖拽
private void Move_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
DragMove();
// 拖动后窗体变成默认的
WindowState = WindowState.Normal;
MyResizeIcon.Data = (Geometry)FindResource("IconWindowMax");
MyButtonResize.ToolTip = "最大化";
标题栏双 击还原、最大化问题
在标题栏的 MouseDown 事件里判断
private void Grid_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)