WPF 应用程序中的对话框管理





5.00/5 (5投票s)
如何在 WPF 应用程序中管理对话框

引言
本文旨在展示一种在 WPF 应用程序中管理对话框的思路。正如大多数开发者所知,对话框在桌面应用程序中是必需且不可避免的,您经常会看到对话框用于配置设置、向用户传达信息或错误消息等。对话框的不当使用和显示不仅会使应用程序客户端区域和 Windows 任务栏显得混乱,有些对话框甚至可能由于重叠和 Z 轴顺序问题而显示在应用程序主窗口后面。
背景
这个问题并不新鲜,自 Windows 操作系统诞生以来就一直存在,Windows 本身也面临着在 Windows Explorer 中管理打开的应用程序窗口的类似问题。Windows Aero 的一个不错的解决方案是尝试通过将同一应用程序的实例合并为一个任务栏图标来解决这个问题,这样可以在任务栏中容纳更多应用程序图标。您可以从 WIKIPEDIA 阅读更多关于 Windows Aero 的信息。
本文展示了 WPF 应用程序使用单例无模式窗口作为所有对话框的容器,这意味着无论屏幕上显示多少对话框,用户看到的只有一个对话框窗口。但是,用户可以通过应用程序任务栏中的导航控件在对话框之间导航或定位特定对话框。导航控件是
DialogTaskbar

DialogTaskbar
中的每个图标都代表一个打开的对话框。用户可以通过后退 (<) 和前进 (>) 导航按钮或使用快捷键(Page Up 和 Page Down、Left 键和 Right 键)在对话框之间导航。
DialogThumbnail

一个数字指示器叠加图标,用于指示打开的对话框数量。用户可以通过将鼠标悬停在任务栏图标上来查看一个弹出列表,从而在对话框之间导航。
理解代码
本文提供的库由以下基本构建块组成
DialogService
DialogServiceWindow
DialogServiceViewModel
ViewModelBase
DialogService
这是 IDialogService
接口的具体实现。应用程序使用它向用户显示对话框。它具有以下方法
ShowDialog
- 立即在屏幕上显示对话框。AddDialog
- 将对话框添加到集合中,但不替换已打开的对话框。RemoveDialog
- 从屏幕上移除对话框。这对于异步工作很有用,例如在启动后台作业时显示进度对话框,并在完成后将其从屏幕上移除。ContainsDialog
- 确定对话框是否在集合中。
DialogServiceViewModel
这是 DialogService
的模型。它使用 ObservableCollection
来保存 DialogItem
(对话框)的集合。此外,它还是 DialogServiceWindow
、DialogTaskbar
和 DialogThumbnail
导航控件的数据绑定模型。它具有以下属性和方法
Items
-DialogItem
的ObservableCollection
。SelectedItem
- 当前选定的DialogItem
。选定的对话框显示在屏幕上。MoveNext
- 调用时选择集合中下一个DialogItem
的DelegateCommand
。MovePrevious
- 调用时选择集合中上一个DialogItem
的DelegateCommand
。
MoveToIfEmpty
- 如果没有显示其他对话框,则显示指定的对话框。MoveTo
- 显示指定的对话框。RemoveItem
- 从集合中移除指定的对话框。
DialogServiceWindow
这是一个非模式窗口,是所有显示对话框的容器。其非客户端区域没有系统菜单、最小化、最大化、还原和关闭按钮,只有标题栏。

ViewModelBase
对话框的视图模型必须派生自 ViewModelBase
类。它提供了一些辅助函数和一个 RequestClose
事件,当调用其 OnRequestClose
方法时,DialogService
将使用该事件来关闭对话框。所有对话框在其完成预期目的时都必须调用 OnRequestClose
方法来关闭自身。
Using the Code
使用本文提供的库显示自定义对话框只需 3 个步骤。
步骤 1. 设计对话框
- 在
UserControl
中设计一个对话框。 - 将
Dialogbox
的 {xxx}ViewModel 派生自ViewModelBase
。 - 将 {xxx}ViewModel 绑定到
Dialogbox
的DataContext
。
注意:请记住在完成预期目的时调用 OnRequestClose
方法来关闭对话框。
private ICommand _closeCommand;
public ICommand CloseCommand
{
get { return _closeCommand ?? (_closeCommand = new DelegateCommand(OnRequestClose)); }
}
步骤 2. 设置运行时环境
在下面的代码清单中,我使用 Ninject 依赖注入容器来设置运行时环境。
[STAThread]
public static void Main(string[] args)
{
var app = new App();
var container = app.Container; // Ninject's DI Container (http://ninject.org/)
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
// Register the DialogService
container.Bind<IDialogService>().To<DefaultDialogService>().InSingletonScope();
// Register the DialogWindow
var dialogService = container.Get<IDialogService>();
container.Bind<IDialogWindow>().ToMethod(context =>
{
return new DialogServiceWindow(dialogService.Model)
{
Owner = mainWindow,
ShowInTaskbar = false
};
}).InSingletonScope();
// Resolve and bind the DialogWindow to DialogService
dialogService.DialogWindow = container.Get<IDialogWindow>();
// Bind the DialogServiceViewModel to the DialogTaskbar and
// DialogThumbnail navigation controls
mainWindow.DialogTaskbar.DataContext = dialogService.Model;
mainWindow.DialogThumbnail.DataContext =
new DialogThumbnailViewModel(dialogService.Model);
mainWindow.DataContext = new MainWindowViewModel(dialogService);
// Run the application
app.Run(mainWindow);
}
步骤 3. 显示对话框
最后一步是调用 ShowDialog
方法在屏幕上显示对话框。
public MainWindowViewModel(IDialogService dialogService)
{
dialogService.ShowDialog(
new DialogItem(
"About Jellyfish",
"The photo you're seeing is a Jellyfish.
\nThis photo is taken from the Windows 7 Pictures Library.",
new AboutDialog(new AboutDialogVM())));
}
作为一个库,它有一个内置对话框,可用于向用户显示简单的信息、警告或错误消息。
private ICommand _openInfoDialogCommand;
public ICommand OpenInfoDialogCommand
{
get
{
return (_openInfoDialogCommand ?? (_openInfoDialogCommand = new DelegateCommand(
() => _dialogService.ShowMessage
("Information Dialog", "This is a Information Dialog.", MessageType.Info))));
}
}
关注点
最后但同样重要的是,我包含了一个示例,可从本文顶部链接获取,该示例演示了我在这篇文章中描述的内容。
历史
- 2011 年 12 月 9 日:初始版本