前言
显示等待框意义
在创建WPF应用的时候,如果我们要执行一个耗时的操作,那么给用户显示一个等待窗体是很常见的需求,通过显示一个等待窗体让用户明白运行的这个软件并没有崩溃,能有效消除用户的焦虑与不确定性,同时能极大提升用户体验,展示软件的专业性和品质,将无聊的等待转化为可预期的、安心的过程。
显示对话框意义
对话框能有效地捕获用户焦点。作为模态窗口,它会强制用户必须完成当前任务(如确认、输入信息或做出选择),之后才能继续操作主窗口。这保证了关键业务流程(如保存文件前的确认)的完整性,防止用户误操作。其次,对话框是信息收集与展示的专用容器。它将特定任务(如设置、登录、详情查看)的UI元素和逻辑封装起来,使主窗口界面保持简洁,同时提升了代码的模块化和可维护性。
显示信息框意义
信息框的核心价值在于即时性与强制性。对于操作成功、失败或出现警告等重要反馈,它能立刻捕获用户注意力,通过模态显示确保用户看到并处理该信息,避免了关键提示被忽略。其次,它为用户提供了快速决策的途径。除了纯信息展示,信息框可附带"是/否"、"确定/取消"等按钮,让用户在不离开当前上下文的情况下,就能对简单询问做出迅速回应,如确认删除操作。
Stylet介绍
Stylet是一个轻量级、高性能的MVVM框架,专为WPF设计。它摆脱了传统MVVM的样板代码,通过基于约定的特性,使得View和ViewModel的自动关联、依赖注入等变得极其简单。开发者几乎无需配置即可快速上手,其内置的功能如IConductor屏幕管理和事件聚合器,能优雅地处理复杂的应用程序导航与模块间通信。Stylet的目标是让开发者专注于业务逻辑本身,而非繁琐的框架配置,从而显著提升WPF应用的开发效率与代码质量。
本文通过使用Stylet内置的IWindowManager可以很容易实现这个需求。
显示等待框
要显示等待窗体那么必须先做一个等待窗体。
创建一个WaitingView.xaml:
csharp
<Window x:Class="Rouyan.Pages.View.WaitingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:Rouyan.Pages.View"
mc:Ignorable="d"
Title="WaitingWindow"
Height="200"
Width="350"
WindowStyle="ToolWindow"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
Topmost="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Background="{DynamicResource PrimaryHueMidBrush}"
Padding="16 8">
<TextBlock HorizontalAlignment="Center"
Text="{Binding Text}"
FontSize="16"/>
</Border>
<!-- 进度条区域 -->
<StackPanel Grid.Row="1"
Orientation="Vertical"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="20">
<ProgressBar Style="{StaticResource MaterialDesignCircularProgressBar}"
Foreground="{DynamicResource SecondaryHueMidBrush}"
Width="60"
Height="60"
IsIndeterminate="True"/>
<TextBlock Text="请稍候..."
FontSize="14"
HorizontalAlignment="Center"
Foreground="{DynamicResource MaterialDesignBody}"
Margin="0,10,0,0"/>
</StackPanel>
</Grid>
</Window>
效果:
分为两行,上面一行用于显示要显示的信息,下面一行用于显示ProgressBar与"请稍候..."的组合。
现在再来创建对应的ViewModel:
csharp
public class WaitingViewModel : Screen
{
public string Text { get; set; } = "处理中...";
}
显示这个等待窗体,比如当我们运行一个耗时任务的时候,可以先显示这个等待窗体给用户:
csharp
// 显示等待窗体
waitingVm = _container.Get<WaitingViewModel>();
waitingVm.Text = "正在分析请求,请稍候...";
_windowManager.ShowWindow(waitingVm);
显示等待窗体主要使用了Stylet中自带的一个组件IWindowManager,我们可以在构造函数中进行依赖注入:
csharp
private readonly IContainer _container;
private readonly IWindowManager _windowManager;
public TerminalAgentViewModel(IContainer container,IWindowManager windowManager)
{
_container = container;
_windowManager = windowManager;
}
由于等待窗体是一个Window,因此需要使用IWindowManager的ShowWindow方法。
Stylet会帮我们将所有View与ViewModel都以瞬态的形式注入依赖注入容器中了,因此可以不用自己在这里new一个viewmodel而是可以直接从容器中取一个出来:
csharp
waitingVm = _container.Get<WaitingViewModel>();
IWindowManager的ShowWindow方法的使用,反直觉的一点是传入的不是这个窗体,而是这个窗体对应的ViewModel,Stylet会根据这个ViewModel自动找到对应的View,这也体现了Stylet中ViewModel First的思想。
效果:
这时候可能会有一个疑问就是那么怎么关闭这个窗体呢?
如果是获取对应的Window,我们调用close方法就可以了,现在获取的是对应的ViewModel该如何关闭这个窗体呢?
Stylet已经考虑到了这一个,只要这样写就可以关闭这个窗体:
csharp
waitingVm.RequestClose();
显示对话框
使用Stylet中的IWindowManager显示对话框同样也很简单。
首先创建一个HumanApprovalDialogView:
csharp
<Window x:Class="Rouyan.Pages.View.HumanApprovalDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
Title="{Binding Title}"
Height="200"
Width="420"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Topmost="True">
<Window.InputBindings>
<KeyBinding Command="{s:Action Approve}" Key="Y"/>
<KeyBinding Command="{s:Action Reject}" Key="N"/>
</Window.InputBindings>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 顶部消息行 -->
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
<TextBlock Text="{Binding Message}"
TextWrapping="Wrap"
FontSize="14"/>
</ScrollViewer>
<!-- 底部按钮行:左侧同意,右侧拒绝 -->
<Grid Grid.Row="1" Margin="0,16,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="同意(Y)"
Width="100"
Height="30"
HorizontalAlignment="Left"
IsDefault="True"
Command="{s:Action Approve}"/>
<Button Grid.Column="1"
Content="拒绝(N)"
Width="100"
Height="30"
HorizontalAlignment="Right"
IsCancel="True"
Command="{s:Action Reject}"/>
</Grid>
</Grid>
</Window>
效果:
创建对应的ViewModel:
csharp
public class HumanApprovalDialogViewModel : Screen
{
public HumanApprovalDialogViewModel()
{
Title = "执行审批";
}
private string _title = string.Empty;
public string Title
{
get => _title;
set => SetAndNotify(ref _title, value);
}
private string _message = string.Empty;
public string Message
{
get => _message;
set => SetAndNotify(ref _message, value);
}
// Approve the action
public void Approve()
{
RequestClose(true);
}
// Reject the action
public void Reject()
{
RequestClose(false);
}
}
使用方式与ShowWindow也很相似:
csharp
var dialogVm = new HumanApprovalDialogViewModel
{
Title = "函数调用审批",
Message = $"是否同意这个操作?"
};
bool? result = _windowManager.ShowDialog(dialogVm);
最大的区别就是模态与非模态。
模态:
当一个模态窗口(如对话框)出现时,它会独占用户的操作焦点。在用户完成该窗口的任务(例如点击"确定"或"取消")并关闭它之前,无法与应用程序的其他任何窗口进行交互。程序流程也会在此处暂停,等待窗口关闭后返回结果。这就像一个强制性的"选择题",你必须回答才能继续。常见场景包括文件保存、确认删除等需要用户明确决策的流程。
非模态:
非模态窗口则像一个开放的助手。它显示后,用户可以随时在它和应用程序的其他窗口之间自由切换,无需关闭它。程序也会继续执行,不会被窗口的开启或关闭打断。这就像一个可以随时查阅的"便签"或"计算器"。它常用于工具箱、属性面板或辅助信息窗口,让用户在主任务进行时能方便地获取额外功能或信息。
简言之就是模态就是会阻塞程序运行,要你明确给一个反馈,非模态不会阻塞程序的运行,就单纯显示一个窗体。
效果:
同意就返回true,拒绝或者关闭窗体就返回false。
显示信息框
我们可以发现IWindowManager除了有ShowWindow与ShowDialog方法外还有一个ShowMessage方法,现在来看下这个方法的使用吧!!
1、基本用法 - 只显示消息
csharp
_windowManager.ShowMessageBox("你好");
效果:
2、带标题的消息框
csharp
_windowManager.ShowMessageBox("操作完成", "提示");
效果:
3、带确认和取消按钮的消息框
csharp
var result1 = _windowManager.ShowMessageBox("确定要删除这个文件吗?", "确认删除",
MessageBoxButton.OKCancel, MessageBoxImage.Question);
效果:
4、带是/否/取消按钮和警告图标的消息框
csharp
var result2 = _windowManager.ShowMessageBox("文件已修改,是否保存?", "保存确认",
MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
效果:
5、带自定义按钮标签的消息框 (使用YesNoCancel按钮以展示更多选项)
csharp
var customButtons = new Dictionary<MessageBoxResult, string>
{
{ MessageBoxResult.Yes, "继续" },
{ MessageBoxResult.No, "停止" },
{ MessageBoxResult.Cancel, "取消" }
};
var result3 = _windowManager.ShowMessageBox("检测到潜在风险,是否继续操作?", "安全警告",
MessageBoxButton.YesNoCancel, MessageBoxImage.Exclamation, MessageBoxResult.No, MessageBoxResult.Cancel, customButtons);
效果:
6、带文本对齐和流方向的消息框
csharp
_windowManager.ShowMessageBox("这是一个从右到左显示的消息框文本", "RTL示例",
MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.None, MessageBoxResult.None,
null, FlowDirection.RightToLeft, TextAlignment.Center);
效果:
7、完整参数示例
csharp
var fullResult = _windowManager.ShowMessageBox(
"这是一个完整的消息框示例,包含了所有参数的使用",
"完整示例",
MessageBoxButton.OKCancel,
MessageBoxImage.Information,
MessageBoxResult.OK,
MessageBoxResult.Cancel,
null,
FlowDirection.LeftToRight,
TextAlignment.Left);
效果:
最后
本文梳理了Stylet中IWindowManager的用法,分别是ShowWindow、ShowDialog与ShowMessageBox希望对你有所帮助。