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 日:文章更新


