Window类继承自ContentControl类。
可以通过设置WindowStyle=“None”,完全移除窗口框架,从而创建一个可完全定制的窗口,但是有各种各样的不方便,所以本文使用WindowChrome.WindowChrome
来自定义窗口
基础知识
Window类基本属性
属性 | 作用 | 值 |
---|---|---|
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控件库】
- xaml文件
<!--按钮样式--> <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>
- 2.Cs文件
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); } } }
参考
遇到的一些难缠问题
- 进行如下方式使用自定义控件,创建不了窗口资源,在OnApplyTemplate中使用GetTemplateChild无法获取子控件,返回null,但是在cs中使用CommonWindow window=new CommonWindow();可以创建窗口并正常显示。
最后发现是按钮中的path资源没有获取到,本来用的是StaticResource获取资源改为DynamicResource后正常,所以要注意StaticReSource与DynamicResource的区别<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>