WPF/C#:使用Stylet中的IWindowManager用于顯示等待窗體、對話框與消息框
前言
顯示等待框意義
在創建WPF應用的時候,如果我們要執行一個耗時的操作,那么給用戶顯示一個等待窗體是很常見的需求,通過顯示一個等待窗體讓用戶明白運行的這個軟件并沒有崩潰,能有效消除用戶的焦慮與不確定性,同時能極大提升用戶體驗,展示軟件的專業性和品質,將無聊的等待轉化為可預期的、安心的過程。
顯示對話框意義
對話框能有效地捕獲用戶焦點。作為模態窗口,它會強制用戶必須完成當前任務(如確認、輸入信息或做出選擇),之后才能繼續操作主窗口。這保證了關鍵業務流程(如保存文件前的確認)的完整性,防止用戶誤操作。其次,對話框是信息收集與展示的專用容器。它將特定任務(如設置、登錄、詳情查看)的UI元素和邏輯封裝起來,使主窗口界面保持簡潔,同時提升了代碼的模塊化和可維護性。
顯示信息框意義
信息框的核心價值在于即時性與強制性。對于操作成功、失敗或出現警告等重要反饋,它能立刻捕獲用戶注意力,通過模態顯示確保用戶看到并處理該信息,避免了關鍵提示被忽略。其次,它為用戶提供了快速決策的途徑。除了純信息展示,信息框可附帶“是/否”、“確定/取消”等按鈕,讓用戶在不離開當前上下文的情況下,就能對簡單詢問做出迅速回應,如確認刪除操作。
Stylet介紹
Stylet是一個輕量級、高性能的MVVM框架,專為WPF設計。它擺脫了傳統MVVM的樣板代碼,通過基于約定的特性,使得View和ViewModel的自動關聯、依賴注入等變得極其簡單。開發者幾乎無需配置即可快速上手,其內置的功能如IConductor屏幕管理和事件聚合器,能優雅地處理復雜的應用程序導航與模塊間通信。Stylet的目標是讓開發者專注于業務邏輯本身,而非繁瑣的框架配置,從而顯著提升WPF應用的開發效率與代碼質量。
本文通過使用Stylet內置的IWindowManager可以很容易實現這個需求。
顯示等待框
要顯示等待窗體那么必須先做一個等待窗體。
創建一個WaitingView.xaml:
<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:
public class WaitingViewModel : Screen
{
public string Text { get; set; } = "處理中...";
}
顯示這個等待窗體,比如當我們運行一個耗時任務的時候,可以先顯示這個等待窗體給用戶:
// 顯示等待窗體
waitingVm = _container.Get<WaitingViewModel>();
waitingVm.Text = "正在分析請求,請稍候...";
_windowManager.ShowWindow(waitingVm);
顯示等待窗體主要使用了Stylet中自帶的一個組件IWindowManager,我們可以在構造函數中進行依賴注入:
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而是可以直接從容器中取一個出來:
waitingVm = _container.Get<WaitingViewModel>();
IWindowManager的ShowWindow方法的使用,反直覺的一點是傳入的不是這個窗體,而是這個窗體對應的ViewModel,Stylet會根據這個ViewModel自動找到對應的View,這也體現了Stylet中ViewModel First的思想。
效果:

這時候可能會有一個疑問就是那么怎么關閉這個窗體呢?
如果是獲取對應的Window,我們調用close方法就可以了,現在獲取的是對應的ViewModel該如何關閉這個窗體呢?
Stylet已經考慮到了這一個,只要這樣寫就可以關閉這個窗體:
waitingVm.RequestClose();
顯示對話框
使用Stylet中的IWindowManager顯示對話框同樣也很簡單。
首先創建一個HumanApprovalDialogView:
<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:
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也很相似:
var dialogVm = new HumanApprovalDialogViewModel
{
Title = "函數調用審批",
Message = $"是否同意這個操作?"
};
bool? result = _windowManager.ShowDialog(dialogVm);
最大的區別就是模態與非模態。
模態:
當一個模態窗口(如對話框)出現時,它會獨占用戶的操作焦點。在用戶完成該窗口的任務(例如點擊“確定”或“取消”)并關閉它之前,無法與應用程序的其他任何窗口進行交互。程序流程也會在此處暫停,等待窗口關閉后返回結果。這就像一個強制性的“選擇題”,你必須回答才能繼續。常見場景包括文件保存、確認刪除等需要用戶明確決策的流程。
非模態:
非模態窗口則像一個開放的助手。它顯示后,用戶可以隨時在它和應用程序的其他窗口之間自由切換,無需關閉它。程序也會繼續執行,不會被窗口的開啟或關閉打斷。這就像一個可以隨時查閱的“便簽”或“計算器”。它常用于工具箱、屬性面板或輔助信息窗口,讓用戶在主任務進行時能方便地獲取額外功能或信息。
簡言之就是模態就是會阻塞程序運行,要你明確給一個反饋,非模態不會阻塞程序的運行,就單純顯示一個窗體。
效果:


同意就返回true,拒絕或者關閉窗體就返回false。
顯示信息框
我們可以發現IWindowManager除了有ShowWindow與ShowDialog方法外還有一個ShowMessage方法,現在來看下這個方法的使用吧??!
1、基本用法 - 只顯示消息
_windowManager.ShowMessageBox("你好");
效果:

2、帶標題的消息框
_windowManager.ShowMessageBox("操作完成", "提示");
效果:

3、帶確認和取消按鈕的消息框
var result1 = _windowManager.ShowMessageBox("確定要刪除這個文件嗎?", "確認刪除",
MessageBoxButton.OKCancel, MessageBoxImage.Question);
效果:

4、帶是/否/取消按鈕和警告圖標的消息框
var result2 = _windowManager.ShowMessageBox("文件已修改,是否保存?", "保存確認",
MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
效果:

5、帶自定義按鈕標簽的消息框 (使用YesNoCancel按鈕以展示更多選項)
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、帶文本對齊和流方向的消息框
_windowManager.ShowMessageBox("這是一個從右到左顯示的消息框文本", "RTL示例",
MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.None, MessageBoxResult.None,
null, FlowDirection.RightToLeft, TextAlignment.Center);
效果:

7、完整參數示例
var fullResult = _windowManager.ShowMessageBox(
"這是一個完整的消息框示例,包含了所有參數的使用",
"完整示例",
MessageBoxButton.OKCancel,
MessageBoxImage.Information,
MessageBoxResult.OK,
MessageBoxResult.Cancel,
null,
FlowDirection.LeftToRight,
TextAlignment.Left);
效果:

最后
本文梳理了Stylet中IWindowManager的用法,分別是ShowWindow、ShowDialog與ShowMessageBox希望對你有所幫助。

浙公網安備 33010602011771號