Window类继承自ContentControl类。
可以通过设置WindowStyle=“None”,完全移除窗口框架,从而创建一个可完全定制的窗口,但是有各种各样的不方便,所以本文使用WindowChrome.WindowChrome
来自定义窗口
属性 | 作用 | 值 |
---|---|---|
AllowsTransparency | 窗口是否透明 | True 允许其他窗口透过该窗口显示
False 窗口背后的内容永远不能显示,并且透明的背景被呈现为黑色背景
当与WindowStyle="None"属性结合使用,可创建形状不规则的窗口
|
Icon | 窗口图标 | |
Top&Left | 设置屏幕窗口左上角到
屏幕顶部与左侧的距离
| 当WindowStartupPosition属性设置为Manual,可在窗口显示之前设计窗口位置 |
ResizeMode | 用户是否可以改变窗口尺寸 | NoResize 完全锁定窗口尺寸
CanMinimize 只允许最小化窗口
CanResize允许任意改变窗口尺寸
CanResizeWithGrip 在窗口右下角添加图形细节,表示可以改变窗口尺寸
|
ReStroeBounds | 获取窗口边界 | |
ShowInTaskbar | 在任务栏Alt+Tab中显示 | True
False
|
SizeToContent | 创建自动放大缩小尺寸的窗口 | Manual 禁止窗口自动改变尺寸
Height、Whidth、Width And Height 允许窗口在不同方向进行扩展以适应动态内容。
当使用SIzeContent属性时窗口尺寸可以放大到超出屏幕边界
|
Title | 窗口标题栏 | |
Topmost | 在最上层显示 | True 在应用程序的所有所有其他窗口的上面显示(除非其他窗口的Topmost也为True) |
WindowStartupLocation | 窗口初始位置设置 | Manual 使用Left&Top设置窗口位置
CenterScreen 在屏幕中心显示窗口
CenterOwner 在父窗口中心显示
|
WindowState | 控制当前窗口是否最大化,最小化或处于正常状态 | Normal 正常
Maximized 最大化
|
WindowStyle | 决定窗口边框 | SingleBorderWindow 默认值None 在没有标题栏的区域周围有一条凸起的细边框 |
方法 | 效果 | 解释 |
---|---|---|
Show() | 显示非模态窗口 | |
ShowDialog() | 显示模态窗口 | 阻止用户访问父窗口 |
Window的标准布局很简单,大致上就是标题栏和内容。以下代码仅供参考,部分依赖文件未贴出
相关链接:【tilte拖动与双击相应的实现】、【HandyControl控件库】
<!--按钮样式-->
<Style x:Key="WindowBaseButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" >
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="#bbc2cc"/>
</Trigger >
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#8994a1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--关闭按钮-->
<Style x:Key="WindowClosedButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" >
<!--<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>-->
<Path x:Name="ClosedPath" Fill="{DynamicResource PrimaryContentBrush}" Height="15" Width="15" Data="{DynamicResource WindowsCloseGeometry}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{DynamicResource LightRedBrush}"/>
<Setter Property="Fill" Value="{DynamicResource LightTextBrush}" TargetName="ClosedPath"/>
</Trigger >
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource DarkRedBrush}"/>
<Setter Property="Fill" Value="{DynamicResource LightTextBrush}" TargetName="ClosedPath"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CommonWindowStyle" TargetType="{x:Type mw:CommonWindow}">
<Setter Property="HeaderHeight" Value="35"/>
<Setter Property="Background" Value="{DynamicResource LightTextBrush}"/>
<Setter Property="Title" Value="CommonWindow"/>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CornerRadius="0" GlassFrameThickness="1" UseAeroCaptionButtons="False" NonClientFrameEdges="None"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type mw:CommonWindow}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" x:Name="WindowBorder">
<Grid x:Name="root" Background="{TemplateBinding Background}" Margin="{TemplateBinding Padding}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="PART_WindowTitleGrid" Grid.Row="0">
<Border Name="header" Background="{DynamicResource WindowTitleBrush}"
BorderThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=HeaderHeight}">
<DockPanel Height="Auto">
<!--图标-->
<StackPanel VerticalAlignment="Center" Orientation="Horizontal" DockPanel.Dock="Left">
<Image Source="{TemplateBinding Icon}" MaxHeight="20" MaxWidth="20" Margin="10,0,0,0"/>
<TextBlock Text="{TemplateBinding Title}" FontSize="14" FontFamily="Microsoft Yihi"
VerticalAlignment="Center" Margin="6,0,0,0"></TextBlock>
</StackPanel>
<StackPanel DockPanel.Dock="Right" HorizontalAlignment="Right"
VerticalAlignment="Top" Orientation="Horizontal" Height="Auto"
WindowChrome.IsHitTestVisibleInChrome="True">
<Button x:Name="btnMin" Style="{StaticResource WindowBaseButton}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=HeaderHeight}">
<Button.Content>
<Path x:Name="MiniPath" Fill="{DynamicResource PrimaryContentBrush}" Data="{DynamicResource WindowsMinGeometry}" Width="15" Height="15"/>
</Button.Content>
</Button>
<Button x:Name="btnMax" Style="{StaticResource WindowBaseButton}" Height="{Binding
RelativeSource={RelativeSource TemplatedParent},Path=HeaderHeight}">
<Button.Content>
<mw:SimplePanel>
<Path x:Name="MaxPath" Stroke="{DynamicResource PrimaryContentBrush}" Height="15" Width="15" Stretch="Uniform" Data="{DynamicResource WindowsMaxGeometry}"/>
</mw:SimplePanel>
</Button.Content>
</Button>
<Button x:Name="btnClose"
Style="{StaticResource WindowClosedButton}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=HeaderHeight}">
</Button>
</StackPanel>
</DockPanel>
</Border>
</Grid>
<Border Grid.Row="1"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
DockPanel.Dock="Top" Height="Auto">
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="Data" Value="{DynamicResource WindowsRestoreGeometry}" TargetName="MaxPath"/>
</Trigger>
<Trigger Property="WindowState" Value="Normal">
<Setter Property="Data" Value="{DynamicResource WindowsMaxGeometry}" TargetName="MaxPath"/>
</Trigger>
<Trigger Property="WindowState" Value="Minimized">
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
using MaxwellControl.Tools;
using MaxwellControl.Tools.Interop;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Shell;
namespace MaxwellControl.Controls
{
public class CommonWindow : Window
{
private Grid root;
private Button minBtn;
private Button maxBtn;
private Button closeBtn;
private Border header;
private bool taskBarHide;
private readonly Thickness _commonPadding;
Thickness const_maxpadding = new Thickness(8, 8, 8, 8);
Thickness const_normalpadding = new Thickness(0, 0, 0, 0);
public CommonWindow()
{
Padding = const_normalpadding;
_commonPadding = Padding;
taskBarHide = false;
//绑定Window的关于title的相应处理
var chrome = WindowChrome.GetWindowChrome(this);
BindingOperations.SetBinding(chrome, WindowChrome.CaptionHeightProperty,
new Binding(nameof(HeaderHeight)) { Source = this });
}
static CommonWindow()
{
StyleProperty.OverrideMetadata(typeof(CommonWindow), new FrameworkPropertyMetadata(ResourceHelper.GetResource<Style>("CommonWindowStyle")));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//获取控件上的子部分
minBtn = base.GetTemplateChild("btnMin") as Button;
maxBtn = base.GetTemplateChild("btnMax") as Button;
closeBtn = base.GetTemplateChild("btnClose") as Button;
root = (Grid)Template.FindName("root", this);
header = base.GetTemplateChild("header") as Border;
minBtn.Click += (o, e) => WindowState = WindowState.Minimized;
maxBtn.Click += MaxBtn_Click;
closeBtn.Click += (o, e) => Close();
}
private void MaxBtn_Click(object sender, RoutedEventArgs e)
{
if (WindowState == WindowState.Normal)
{
WindowState = WindowState.Maximized;
}
else
{
WindowState = WindowState.Normal;
}
}
#region properties
//是否全屏
public bool IsFullScreen
{
get { return (bool)GetValue(IsFullScreenProperty); }
set { SetValue(IsFullScreenProperty, value); }
}
// Using a DependencyProperty as the backing store for FullScreen. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsFullScreenProperty =
DependencyProperty.Register("FullScreen", typeof(bool), typeof(CommonWindow), new PropertyMetadata(false));
//title的高度
public int HeaderHeight
{
get { return (int)GetValue(HeaderHeightProperty); }
set { SetValue(HeaderHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for HeaderHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderHeightProperty =
DependencyProperty.Register("HeaderHeight", typeof(int), typeof(CommonWindow), new PropertyMetadata(0));
#endregion
//状态改变执行的操作
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
if (WindowState == WindowState.Maximized)
{
if (taskBarHide == false)
{
Padding = const_maxpadding;
}
if (IsFullScreen)
{
HeaderHeight = 0;
header.Visibility = Visibility.Hidden;
}
else
{
HeaderHeight = 35;
header.Visibility = Visibility.Visible;
}
}
else if (WindowState == WindowState.Normal)
{
Padding = const_normalpadding;
}
}
//根据不同情况为Padding赋值
protected override void OnSourceInitialized(EventArgs e)
{
this.GetHwndSource()?.AddHook(HwndSourceHook);
base.OnSourceInitialized(e);
}
private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
switch (msg)
{
case InteropValues.WM_WINDOWPOSCHANGED:
Padding = WindowState == WindowState.Maximized ? WindowHelper.WindowMaximizedPadding : _commonPadding;
break;
case InteropValues.WM_GETMINMAXINFO:
WmGetMinMaxInfo(hwnd, lparam);
Padding = WindowState == WindowState.Maximized ? WindowHelper.WindowMaximizedPadding : _commonPadding;
break;
}
return IntPtr.Zero;
}
//判断任务栏是否隐藏,根据不同情况进行处理
private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
{
var mmi = (InteropValues.MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(InteropValues.MINMAXINFO));
var monitor = InteropMethods.MonitorFromWindow(hwnd, InteropValues.MONITOR_DEFAULTTONEAREST);
if (monitor != IntPtr.Zero && mmi != null)
{
InteropValues.APPBARDATA appBarData = default;
var autoHide = InteropMethods.SHAppBarMessage(4, ref appBarData) != 0;
taskBarHide = autoHide;
if (autoHide)
{
var monitorInfo = default(InteropValues.MONITORINFO);
monitorInfo.cbSize = (uint)Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
InteropMethods.GetMonitorInfo(monitor, ref monitorInfo);
var rcWorkArea = monitorInfo.rcWork;
var rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.X = Math.Abs(rcWorkArea.Left - rcMonitorArea.Left);
mmi.ptMaxPosition.Y = Math.Abs(rcWorkArea.Top - rcMonitorArea.Top);
mmi.ptMaxSize.X = Math.Abs(rcWorkArea.Right - rcWorkArea.Left);
mmi.ptMaxSize.Y = Math.Abs(rcWorkArea.Bottom - rcWorkArea.Top - 1);
}
}
Marshal.StructureToPtr(mmi, lParam, true);
}
}
}
<my:CommonWindow x:Class="Maxwell_VS2017Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
</Grid>
</my:CommonWindow>
最后发现是按钮中的path资源没有获取到,本来用的是StaticResource获取资源改为DynamicResource后正常,所以要注意StaticReSource与DynamicResource的区别