本文将介绍Window构成、基本属性、使用、以及如何自定义,提供详细的代码示例
概述
Window
类是 WPF 应用程序中用于显示和管理窗口的核心类。
主要功能:
- 显示窗口:使用
Show
方法显示窗口。 - 配置窗口:设置窗口的大小、位置和外观。
- 托管内容:在窗口的客户端区域内添加应用程序特定的内容。
- 管理生命周期:处理窗口的打开、关闭和其他生命周期事件。
窗口的组成部分
窗口分为两个区域:非工作区和工作区。
窗口的非工作区由 WPF实现,它包括大多数窗口所共有的窗口部分,包括:
- 非工作区:包括标题栏、图标、最小化/最大化/关闭按钮、系统菜单和边框。
- 工作区:用于添加应用程序特定的内容,如菜单栏、工具栏和控件。
下图展示了窗口的构成部分:
- 标题栏 (1-5)。
- 图标 (1)。
- 标题 (2)。
- 最小化 (3)、最大化 (4) 和关闭 (5) 按钮。
- 包含菜单项的系统菜单 (6)。 单击图标 (1) 时出现。
- 边框 (7)。
- 工作区 (8)。
- 大小调整手柄 (9)。 这是添加到工作区 (8) 的控件。
窗口属性速览
1. 外观属性
- Title:窗口的标题。
- Icon:窗口的图标。
- WindowStyle:窗口的样式(如单一边框、无边框等)
- WindowStyle.None
2.WindowStyle.SingleBorderWindow 、ThreeDBorderWindow
在win11看不出区别
3.WindowStyle.ToolWindow :不可最大化
-
ResizeMode:窗口的调整模式(如可调整大小、不可调整大小等)。
-
NoResize 无法调整大小
-
Can Resize Mode Window
-
Can Minimize Mode Window
-
ResizeMode.CanResizeWithGrip
-
AllowsTransparency:是否允许窗口透明。
打开会消耗性能,一般用于异形窗口,设置为true时,必须设置WindowStyle = None,否则报错
-
2. 位置属性
- Left:窗口左边缘相对于屏幕左边缘的距离。
- Top:窗口上边缘相对于屏幕上边缘的距离。
- WindowStartupLocation:窗口启动位置(如手动、屏幕中心等)。
3. 大小属性
-
Width:窗口的宽度。
-
Height:窗口的高度。
-
MinWidth:窗口的最小宽度。
-
MinHeight:窗口的最小高度。
-
MaxWidth:窗口的最大宽度。
-
MaxHeight:窗口的最大高度。
-
SizeToContent:窗口大小是否根据内容自动调整。
4. 可见性属性
- Visibility:窗口的可见性(如可见、隐藏等)。
- ShowInTaskbar:窗口是否显示在任务栏中。
- Topmost:窗口是否总是位于其他窗口之上。
5. 生命周期属性
- 创建:使用
new
关键字创建窗口实例。 - 显示:使用
Show
或ShowDialog
方法显示窗口。 - 关闭:使用
Close
方法关闭窗口。 - 事件处理:
- IsActive:窗口是否为活动窗口。
- Activated:窗口激活事件。
- Deactivated:窗口失活事件。
- Closing:窗口关闭前事件。
- Closed:窗口关闭后事件。
示例
using System.Windows;
namespace WpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.Activated += MainWindow_Activated;
this.Deactivated += MainWindow_Deactivated;
this.Closing += MainWindow_Closing;
this.Closed += MainWindow_Closed;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// 窗口加载完成
}
private void MainWindow_Activated(object sender, EventArgs e)
{
// 窗口激活
}
private void MainWindow_Deactivated(object sender, EventArgs e)
{
// 窗口失去焦点
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// 窗口关闭前
}
private void MainWindow_Closed(object sender, EventArgs e)
{
// 窗口关闭后
}
}
}
窗口使用
创建
在 WPF 中,可以使用 XAML 和代码来实现窗口的外观和行为。通常,窗口的外观使用 XAML 标记实现,而行为使用代码隐藏实现。
示例
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<!-- 在此处添加窗口的内容 -->
using System.Windows;
namespace MyApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
打开窗口
窗口的打开模式主要分为两种:模态窗口(Modal Window)和非模态窗口(Modeless Window)。
模态(Modal)这个词在编程和用户界面设计中,模态通常指的是一种状态或模式。
// 我想你应该听说过莫代尔棉*,就是这个单词Modal *
模态窗口
在这种模式下,打开模态窗口后,调用窗口会被锁定,无法进行其他操作。
用户必须完成某个特定任务或操作,才能返回到主程序或其他任务。
常见场景例如,打开一个文件对话框选择文件时,必须选择文件或取消对话框,才能返回到主窗口。
var window = new Margins();
window.Owner = this;
window.ShowDialog();
非模态窗口
非模态模式下,用户可以自由切换和操作其他窗口或任务,而不必先完成当前窗口的任务。
常见场景例如,打开一个工具窗口(如调色板或属性窗口)时,你可以继续操作主窗口,而不必先关闭工具窗口。
使用 Show
方法打开非模态窗口。以下是一个示例代码:
var window = new Margins();
window.Owner = this;
window.Show();
启动窗口
WPF 应用程序启动窗口的两种方式
- XAML中快速启动
使用 StartupUri 来指定应用程序中 XAML 文件的路径。 应用程序将自动创建并显示由该属性指定的窗口。
<Application x:Class="WindowsOverview.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WindowsOverview"
StartupUri="ClippedWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
- 后端代码中启动
<Application
x:Class="WPFTopMost.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFTopMost"
Startup="Application_Startup">
<Application.Resources />
</Application>
using System.Configuration;
using System.Data;
using System.Windows;
namespace WPFTopMost
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// 创建并显示主窗口
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
}
}
激活窗口与防止激活
窗口激活
窗口激活是指将一个窗口设置为当前活动窗口,使其获得用户输入焦点。
使用 Activate
方法激活
Window window = new Window();
window.Show();
window.Activate(); // 激活窗口
**IsActive **属性 检查窗口是否为当前活动窗口。
if (window.IsActive)
{
// 窗口是活动窗口
}
防止窗口激活
- 通知窗口:例如电子邮件或聊天应用程序的通知窗口,这些窗口弹出时不应打断用户当前的操作
- 工具提示或帮助窗口:这些窗口提供额外信息或帮助,但不应抢占用户的焦点
- 后台任务窗口:例如下载进度窗口或后台处理任务的状态窗口,这些窗口显示信息但不需要用户立即互动
使用 ShowActivated
属性
ShowActivated
属性用于控制窗口在显示时是否自动激活。
默认情况下,此属性为 true
,表示窗口在显示时会自动激活。
如果将其设置为 false
,则窗口在显示时不会自动激活。
private void OpenWindowButton_Click(object sender, RoutedEventArgs e)
{
Window nonActiveWindow = new Window
{
Title = "非激活窗口",
Width = 300,
Height = 200,
ShowInTaskbar = false,
ShowActivated = false,
};
nonActiveWindow.Show();
nonActiveWindow.Content = string.Format("非激活窗口,IsActive={0}.主窗口仍保持激活状态", nonActiveWindow.IsActive);
}
private void OpenActiveWindowButton_Click(object sender, RoutedEventArgs e)
{
Window activeWindow = new Window
{
Title = "激活窗口",
Width = 300,
Height = 200,
ShowInTaskbar = false,
ShowActivated = true,
};
activeWindow.Show();
activeWindow.Content = string.Format("激活窗口,IsActive={0},主窗口变为激活状态", activeWindow.IsActive);
}
-
可以看到,当打开激活窗口,被打开的窗口状态为激活,主程序窗口状态变为未激活
-
当打开非激活窗口,被打开的窗口状态为未激活,主程序窗口状态保持为激活
窗口所有权
窗口所有权(Window Ownership)是指一个窗口(子窗口)由另一个窗口(父窗口)拥有的关系。
通过设置窗口的 Owner
属性,可以建立这种所有权关系。
设置窗口所有权
使用 ShowDialog
方法
当使用 ShowDialog
方法打开子窗口时,父窗口和子窗口之间会自动建立所有权关系。此时,子窗口是模式窗口,用户必须关闭子窗口才能返回父窗口。
Window ownedWindow = new Window();
ownedWindow.Owner = this; // 设置所有者
ownedWindow.ShowDialog(); // 以模式窗口方式显示
使用 Show
方法
当使用 Show
方法打开子窗口时,需要手动设置 Owner
属性来建立所有权关系。
Window ownedWindow = new Window();
ownedWindow.Owner = this; // 设置所有者
ownedWindow.Show(); // 以非模式窗口方式显示
窗口所有权的行为
1 最小化行为
- 如果所有者窗口最小化,则所有拥有的窗口也会最小化。
- 如果拥有的窗口最小化,所有者窗口不会最小化。
2 关闭行为
- 如果关闭所有者窗口,则所有拥有的窗口也会关闭。
- 如果关闭拥有的窗口,所有者窗口不会关闭。
3 窗口层级
- 所有者窗口永远不会被拥有的窗口覆盖。拥有的窗口总是在所有者窗口之上。
示例
主窗口代码
using System.Windows;
namespace WpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OpenOwnedWindow_Click(object sender, RoutedEventArgs e)
{
Window ownedWindow = new Window();
ownedWindow.Owner = this; // 设置所有者
ownedWindow.Show(); // 显示子窗口
}
}
}
主窗口XAML
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="300">
<StackPanel>
<Button Content="打开子窗口" Click="OpenOwnedWindow_Click" Margin="10"/>
</StackPanel>
</Window>
注意事项
- 在浏览器中托管的窗口无法设置或获取
Owner
属性。 - 如果使用
ShowDialog
打开子窗口,建议同时设置Owner
属性,以确保正确的行为。
关闭/取消关闭窗口
关闭窗口
1. 使用 Close
方法
在 WPF 中,最常见的关闭窗口的方法是调用窗口的 Close
方法。这会触发窗口的 Closing
和 Closed
事件。
private void closeButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
2 使用 DialogResult
属性
对于模态窗口,可以通过设置 DialogResult
属性来关闭窗口。
private void okButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
取消关闭窗口
1 订阅 Closing
事件
要取消关闭窗口,可以订阅窗口的 Closing
事件,并在事件处理程序中设置 CancelEventArgs.Cancel
为 true
。
public MainWindow()
{
InitializeComponent();
this.Closing += MainWindow_Closing;
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (MessageBox.Show("确定要关闭窗口吗?", "提示", MessageBoxButton.YesNo) == MessageBoxResult.No)
{
e.Cancel = true;
}
}
2 隐藏窗口而非关闭
有时可能希望隐藏窗口而不是关闭它。可以在 Closing
事件中调用 Hide
方法。
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
其他关闭窗口的方法
1 使用 IsCancel
属性
可以将按钮的 IsCancel
属性设置为 true
,以便按下 ESC 键时关闭窗口。这仅在使用 ShowDialog
方法打开窗口时有效。
private void CreateCancelButtonWindow_Click(object sender, RoutedEventArgs e)
{
Window cancelButtonWindow = new Window
{
Title = "Cancel Button Window",
Width = 300,
Height = 200
};
Button cancelButton = new Button
{
Content = "Cancel",
Width = 100,
Height = 30,
IsCancel = true,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
// 将取消按钮添加到窗口的内容中
cancelButtonWindow.Content = cancelButton;
// 显示窗口
cancelButtonWindow.ShowDialog();
}
2 强制退出
即使有其他线程未结束,也可以使用 Application.Current.Shutdown
方法强制退出应用程序。
Application.Current.Shutdown();
自定义窗口
对窗口的样式进行自定义,主要是修改窗口的非工作区的样式——包括标题栏、图标、最小化/最大化/关闭按钮、系统菜单和边框。
通过设置WindowStyle=None,并重写标题栏、图标、最小化/最大化/关闭按钮、系统菜单和边框样式即可。
问题1:即便WindowStyle=None,还是存在一定的边框。
在之前的文章WPF 窗体增加置顶按钮Demo_wpf 弹出窗口 置顶-CSDN博客中,当时我误以为是因为设置了WindowStyle=None,导致不能调整窗口大小,我还重新实现了调整窗口大小的功能。
在后来才发现是因为我设置了AllowsTransparency =true
。而之所以会这样是因为:
AllowsTransparency 是为了方便创建非矩形窗口,因此,当AllowsTransparency 设置为true 时,非工作区将不起效果,所以需要自己实现。而且注意此时窗口的WindowStyle 属性必须设置为None。当启用透明度时,窗口的渲染需要处理透明像素,这与标准窗口的渲染机制不同,会增加渲染的复杂性和计算量。所以一般的窗口无需设置为True。
问题2:该如何去掉边框呢?那该如何实现高性能且自定义的Window样式呢?
使用WindowChrome
WindowChrome
允许开发者自定义这些非客户区的元素,从而实现更灵活和个性化的窗口设计。位于 System.Windows.Shell
命名空间中。
- CaptionHeight: 标题栏的高度。
- CornerRadius: 窗口角的圆角半径。
- GlassFrameThickness: 玻璃框架的厚度。
- ResizeBorderThickness: 可调整大小的边框厚度。
- UseAeroCaptionButtons: 是否使用 Aero 风格的系统按钮。
何为 Chrome
在计算机图形学和用户界面设计中,“chrome”指的是应用程序窗口的非客户区部分。
这些部分包括标题栏、边框和系统按钮(如最小化、最大化和关闭按钮)。
这些元素通常不包含应用程序的主要内容,但它们提供了窗口的控制和装饰功能。
“Chrome”这个术语的使用源于其在用户界面中的装饰性和功能性作用,就像汽车的镀铬装饰一样,既美观又实用。
//一个奢侈品品牌——克罗心Chrome Hearts
示例
这里就以WPF 窗体增加置顶按钮Demo为例,将其封装为一个自定义控件。
效果
- TopMostWindow.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:WindowDemo">
<Style TargetType="{x:Type local:TopMostWindow}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TopMostWindow}">
<Border
BorderBrush="White"
BorderThickness="0"
CornerRadius="5">
<Grid
x:Name="PART_RootGrid"
Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Background="DarkGray"
CornerRadius="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="10,0,0,0"
VerticalAlignment="Center"
Text="{TemplateBinding Title}" />
<ToggleButton
x:Name="PART_TopMostButton"
Grid.Column="1"
Width="30"
Height="30"
Content="📌">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding TopMostWindowViewModel.TopMostCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TopMostWindow}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ToggleButton>
<Button
Grid.Column="2"
Width="30"
Height="30"
Command="{Binding MinimizeCommand}"
Content="-">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding TopMostWindowViewModel.MinimizeCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TopMostWindow}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button
Grid.Column="3"
Width="30"
Height="30"
Command="{Binding MaximizeCommand}"
Content="[]">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding TopMostWindowViewModel.MaximizeCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TopMostWindow}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button
Grid.Column="4"
Width="30"
Height="30"
Command="{Binding CloseCommand}"
Content="X">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding TopMostWindowViewModel.CloseCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TopMostWindow}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Border>
<ContentPresenter Grid.Row="1" />
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding TopMostWindowViewModel.DragMoveCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TopMostWindow}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
- TopMostWindow.cs
using System.Windows;
using System.Windows.Shell;
namespace WindowDemo
{
public class TopMostWindow : Window
{
static TopMostWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TopMostWindow), new FrameworkPropertyMetadata(typeof(TopMostWindow)));
}
public TopMostWindow()
{
this.Loaded += (sender, e) =>
{
TopMostWindowViewModel = new TopMostWindowViewModel(this);
this.MaxHeight = SystemParameters.PrimaryScreenHeight;//防止最大化时系统任务栏被遮盖
};
WindowChrome.SetWindowChrome(this, new WindowChrome()
{
CaptionHeight = 0,
UseAeroCaptionButtons = false,
NonClientFrameEdges = NonClientFrameEdges.None
});
}
public TopMostWindowViewModel TopMostWindowViewModel
{
get { return (TopMostWindowViewModel)GetValue(TopMostWindowViewModelProperty); }
set { SetValue(TopMostWindowViewModelProperty, value); }
}
public static readonly DependencyProperty TopMostWindowViewModelProperty =
DependencyProperty.Register("TopMostWindowViewModel", typeof(TopMostWindowViewModel),
typeof(TopMostWindow), new PropertyMetadata(null));
}
}
- ViewModel
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows;
namespace WindowDemo
{
public partial class TopMostWindowViewModel: ObservableObject
{
public TopMostWindowViewModel(TopMostWindow window)
{
_window = window;
}
public TopMostWindow _window;
[RelayCommand]
private void Close()
{
if (_window == null) return;
_window.Close();
}
[RelayCommand]
private void Maximize()
{
if (_window == null) return;
if (_window.WindowState == WindowState.Maximized)
_window.WindowState = WindowState.Normal;
else
_window.WindowState = WindowState.Maximized;
}
[RelayCommand]
private void Minimize()
{
if (_window == null) return;
_window.WindowState = WindowState.Minimized;
}
[RelayCommand]
private void DragMove()
{
if (_window == null) return;
_window.DragMove();
}
[RelayCommand]
private void TopMost()
{
if (_window == null) return;
_window.Topmost = true;
}
}
}
参考资料
- WPF 中的窗口概述
- Windows in WPF overview
- Window.ShowActivated 属性 (System.Windows) | Microsoft Learn
- WPF 让窗口激活作为前台最上层窗口的方法 - lindexi - 博客园
- WPF中Window.Owner属性详解
- Window.Owner 属性 (System.Windows) | Microsoft Learn
- WPF中Window.Owner属性详解_wpf owner-CSDN博客.
- WPF 中如何取消关闭窗口 - CSDN博客
- 如何关闭窗口 - WPF .NET | Microsoft Learn
- WindowChrome Class (System.Windows.Shell) | Microsoft Learn