Silverlight 中的模态窗口






4.96/5 (16投票s)
展示了一种创建模态窗口的通用机制。
注意:版本 2
本文是对 2008 年 9 月发布的初版文章的更新。初版文章发布后,有人指出在模态窗口中承载 ComboBox
和 DataGrid
等控件存在问题。问题在于 Popup
类未添加到视觉树中。
作为解决方案,我添加了一个 static
属性,该属性只需在应用程序中设置一次,最好将其初始化为**主页面的 VisualRoot**,并且它必须是**Grid 类**。
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
ModalControl.ParentHost = LayoutRoot; // Init the parent control
}
// Rest of the code removed
}
初始化 ModalControl.ParentHost
后,您就可以使用模态窗口来承载 ComboBoxes
和 DataGrids
。
演示项目展示了如何使用 ModalControl
和更新的 MessageBox
。
引言
本文介绍了如何实现一个类,该类能够模态地显示任何 UserControl
派生控件。
背景
我对此类的第一个实现要求您从该类派生您的控件,以便它们可以模态显示。
这种方法效果很好,并且是最完整、最易于使用的实现。但是,Expression Blend 设计器窗口不会显示那些不直接派生自 UserControl
的控件,因此我们会失去 Blend 的所有出色设计功能。
这让我思考,在不失去 Blend 设计功能的情况下,我还能如何实现相同的行为。经过一番思考,我意识到聚合必须是解决方案。
ModalControl 类的特性
- 模态显示任何
UserControl
派生控件 - 拖动托管控件
- 在浏览器窗口大小调整时居中托管控件
- 阻止托管控件拖出浏览器窗口
项目
此项目基于 Silverlight Wizard 项目,展示了如何模态地显示该向导,我还构建了一个 MessageBox
,演示了 ModalControl
类如何被封装以创建独立的模态窗口。
它看起来是什么样的
在这里,我们可以看到 Silverlight Wizard 项目的主页已更改,以便一个按钮可以模态地显示向导。我还添加了一个“显示对话框”按钮,它将弹出我通过聚合 ModalControl
实现的一个模态对话框窗口。

在这里,我们看到 Silverlight Wizard 以模态方式显示,而无需对其进行任何更改。我们从外部使用 ModalControl
类。

这里是模态对话框

Using the Code
您可以通过以下两种方式使用 ModalControl
类提供的服务
- 用作辅助类,并将要模态显示的
UserControl
传递给ShowModal
函数。 - 将
ModalClass
封装到您的控件中,MessageBox
展示了如何使用此技术。
ModalControl 作为辅助
请注意,在下面的代码中,我们只需要定义一个 ModalControl
实例;重要的是 ShowModal
接受要显示的控件作为参数,而 HideModal
返回 UserControl
。
// Declare a class instance member of the ModalControl
ModalControl _oModalCtrl = new ModalControl();
void OnShowWizardClick(object sender, RoutedEventArgs e)
{
// Instantiate the wizard
Wizard oWizard = new Wizard();
// Set data context and add pages to the wizard
oWizard.DataContext = LayoutRoot.DataContext;
oWizard.Pages.Add(new WizardPage1());
oWizard.Pages.Add(new WizardPage2());
oWizard.Pages.Add(new WizardPage3());
// Add a Pages event listener to the izard
oWizard.PageEvent += new WizardPageEvent(OnPageEvent);
// call ShowModal by passing the wizard refernce to the modal control
// this call will show the wizard modally, it could be any user control
_oModalCtrl.ShowModal(oWizard);
}
// Here we handle the wizard page events
void OnPageEvent(Wizard sender, WizardEventArgs e)
{
_txtMsg.Text = string.Format("Action: {0}, Current: {1}, New: {2}",
e.Action, e.CurrentPageIndex, e.NewPageIndex);
// When we hit the Finish button on the wizard we call close on the
// modal control and that returns to us the wizard reference so we could
// do some processing if we need to - sender prameter does the same
if(e.Action == WizardAction.Finish)
{
// Close the wizard
Wizard oWizard = (Wizard)_oModalCtrl.HideModal();
}
}
封装 ModalControl
将 ModalControl
封装到您自己的类中同样简单,这在 ModalWizard
项目中的 MessageBox
类中有充分演示。这里的代码是 MessageBox
实际实现的一个简化版本,但它显示了重要的部分。
public class MessageBox : UserControl
{
// Static demofunction
public static MessageBox Show(string Title, string Message)
{
// Here we create the MessageBox instance
MessageBox oBox = new MessageBox(Title, Message);
// Use its private member to show the dialog
oBox.ModalHost.ShowModal(oBox);
// Return the reference to the MessageBox
return oBox;
}
// Instance memebers
// --------------------------------------------------------------------
// Declare a private instance of the ModalControl
ModalControl ModalHost;
// Pass the parameters to the constructor
private MessageBox(string Title, string Message)
{
InitializeComponent();
txtTitle.Text = Title;
txtMessage.Text = Message;
ModalHost = new ModalControl(); // Instantiate the ModalControl
btClose.Click += OnCloseClick;
}
// Handle the close button click
private void OnCloseClick(object sender, RoutedEventArgs e)
{
// On modal control call HideModal to close the message box
ModalHost.HideModal();
}
}
使用 MessageBox 类/控件
以下代码段显示了如何使用 MessageBox
控件;您必须记住,ModalControl
并非真正地模态显示子/托管控件,它只是提供了模态的“错觉”。在此版本的 MessageBox
中,我们不需要维护对 MessageBox
的引用
由于我假设我们一次只显示一个 MessageBox
。这个假设允许我有一个单一的 static
引用 MessageBox
,因此使得 MessageBox
更像 WinForms MessageBox
。
void OnShowDialogClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Some modal title", "This is some message!",
MessageBox.Buttons.YesNo,
MessageBox.Icons.Information, OnDialogClosed);
}
bool OnDialogClosed(object sender, ExitCode e)
{
Debug.Print("Dialog Closed with code: " + e);
return true;
}
关注点
非常重要的是要记住,模态窗口并非真正模态,当前视图和模态控件之间放置了一个半透明的画布。如果关闭模态窗口时需要退出代码或其他任何详细信息,您必须实现
某些回调或事件处理程序。本项目中的 MessageBox
和 ModalWizard
类演示了所有这些要点。
特别感谢
读者 Predrag Tomasevic 识别出 Popup
类未添加到视觉树的问题。
历史
- 2008 年 9 月 28 日:初次发布
- 2009 年 3 月 11 日:文章更新