Silverlight 之模态对话框的通用模拟

Silverlight 给我们带来了如同 Winform 一样便捷的开发方式,然而在桌面应用程序中一个非常重要的元素——模态对话框,却不具备。
ScottGu 的 Silverlight 教程中提供了一种简单的方法,可以模拟出类似模态对话框的显示。
其原理跟我们在 HTML 中实现模拟的模态对话框的方法类似,无非是动态创建一个半透明的层盖住整个页面背景,让背后的元素无法操作,然后在其上显示对话框内容即可。不过,在这篇教程中创建的子窗体必须在调用者页面的 XAML 中手工声明,假如我们需要根据情况调出 n 个不同的对话框呢?这样做显然不够灵活。本文的目标是基于这种场景提供一个简单的封装,以便更方便的实现主/从调用的场景。

例子的运行效果如下:

modaldialog1

点击第一个按钮后,显示如下:

modaldialog2

 

下面看看如何来实现这个效果。


首先,我们来定义一个模态对话框的基类,它是一个用户控件(UserControl):

using System;
using System.Windows.Controls;

namespace ModalPopupDemo
{
   
public   class ModalDialog: UserControl
    {
       
public   event EventHandler Closed;

       
public   void Close()
        {
           
if (Closed !=   null )
            {
                Closed(
this , EventArgs.Empty);
            }
        }
    }

   
public   interface IModalDialogOpener
    {
       
void ShowModalDialog(ModalDialog dialog);

       
void CloseModalDialog();
    }
}


因为我们想在模态框关闭时,从父窗口中得到通知,所以给它定义了一个 Closed 事件。并且提供了该事件的触发函数 Close().

同时,我们还为模态框的父窗体(opener) 定义了一个接口 IModalDialogOpener.
其中顾名思义定义了打开和关闭模态框的两个方法。

好了,现在我们创建一个模态对话框试试。假设是一个登录窗体,就叫它 Login 吧——在项目中添加一个用户控件 Login.

在 Login.xaml 文件里添加了一些 Markup 后,我们修改后端代码文件 Login.xaml.cs 如下:

using System.Windows;

namespace ModalPopupDemo
{
   
public   partial   class Login : ModalDialog
    {
       
public Login()
        {
            InitializeComponent();
        }

       
private   void btnLogin_Click( object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}


这里注意基类改成了 ModalDialog,就像在 ASP.NET 页面基类里所做的那样。并且我们在按钮的点击事件处理函数里调用了刚才定义的 Close 方法来关闭窗体。

这时 Login.xaml 是这样的:

< local:ModalDialog x:Class ="ModalPopupDemo.Login"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local
="clr-namespace:ModalPopupDemo" >
   
< Grid x:Name ="LayoutRoot" >
       
< Border CornerRadius ="20" Background ="#eeeeee" Width ="300" Height ="150" >
           
< StackPanel Orientation ="Vertical" Margin ="10" >
               
< StackPanel Orientation ="Horizontal" Margin ="10" >
                   
< TextBlock Width ="100" > UserName: </ TextBlock >
                   
< TextBox x:Name ="txtUserName" Width ="150" HorizontalAlignment ="Right"   />
               
</ StackPanel >
               
< StackPanel Orientation ="Horizontal" Margin ="10" >
                   
< TextBlock Width ="100" > Password: </ TextBlock >
                   
< TextBox x:Name ="txtPassword" Width ="150"   />
               
</ StackPanel >
               
< StackPanel Margin ="10" >
                   
< Button x:Name ="btnLogin" Content ="Login" Width ="50"
                        HorizontalAlignment
="Right" Click ="btnLogin_Click"   />
               
</ StackPanel >
           
</ StackPanel >
       
</ Border >
   
</ Grid >
</ local:ModalDialog >


需要注意的是这个 XAML 文档的根元素从默认的 UserControl 改成了 local:ModalDialog. 而这正是后台 partial class 的基类,Silverlight 中规定这两者必须一致的。这里 local 是一个名称空间,可以看到稍后的 xmlns:local="clr-namespace:ModalPopupDemo" 这段代码将它声明为了当前程序集的 ModalPopupDemo 名称空间。

实现了子窗体,我们来看看如何实现父窗体。

我们的父窗体 Page.xaml 是这样的:

< UserControl x:Class ="ModalPopupDemo.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" >
   
< Grid x:Name ="LayoutRoot" Background ="White" >
       
< StackPanel Orientation ="Vertical" >
           
< TextBlock FontSize ="30" HorizontalAlignment ="Center" > This is the main page. </ TextBlock >
           
< Button x:Name ="btnOpen1" Content ="Open Login form" Click ="btnOpen_Click" Width ="150"   />
           
< Button x:Name ="btnOpen2" Content ="Open another form" Click ="btnOpen2_Click" Width ="150" Margin ="0,10"   />
       
</ StackPanel >
       
<!-- 占位元素,用于在其中加载模态窗口 -->
       
< Border x:Name ="placeHolder" VerticalAlignment ="Stretch" HorizontalAlignment ="Stretch"
                Visibility
="Collapsed"
                Background
="#ff8a8a8a" Opacity ="0.765"   />
   
</ Grid >
</ UserControl >


这里照搬了 ScottGu 文章中的那个技巧,不过我改成了用一个 Border 元素来占位,同时将它的大小设置为全屏、半透明、不可见状态。这样它就既可以作为子窗体的容器,又可以在弹出模态对话框时遮住背景,一举两得。

后端代码 Page.xaml.cs:

using System.Windows;
using System.Windows.Controls;

namespace ModalPopupDemo
{
   
public   partial   class Page : UserControl, IModalDialogOpener
    {
       
public Page()
        {
            InitializeComponent();
        }

       
private   void btnOpen_Click( object sender, RoutedEventArgs e)
        {
            var login
=   new Login();
            login.Closed
+= (sender2, args2) => CloseModalDialog();
            ShowModalDialog(login);
        }

       
private   void btnOpen2_Click( object sender, RoutedEventArgs e)
        {
            var form2
=   new Form2();
            form2.Closed
+= (sender2, args2) => CloseModalDialog();
            ShowModalDialog(form2);
        }

       
#region IModalDialogOpener implementations
       
public   void ShowModalDialog(ModalDialog dialog)
        {
            placeHolder.Child
= dialog;
            placeHolder.Visibility
= Visibility.Visible;
        }

       
public   void CloseModalDialog()
        {
            placeHolder.Child
=   null ;
            placeHolder.Visibility
= Visibility.Collapsed;
        }
       
#endregion
    }
}


这里 ShowModalDialog 和 CloseModalDialog 的实现相当简单,分别就是附加/移除相关的子窗体控件实例,并且显示/隐藏占位控件。

页面中除了 Login 窗体外,还调用了另一个简单的用户控件 Form2,调用方式是非常类似的。Form2 的代码如下:
Form2.xaml:

< local:ModalDialog x:Class ="ModalPopupDemo.Form2"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local
="clr-namespace:ModalPopupDemo" >
   
< Grid >
       
< Button x:Name ="btnClose" Width ="300" Height ="200" Content ="Close Me!"
                Click
="btnClose_Click" FontSize ="40"   />
   
</ Grid >
</ local:ModalDialog >


Form2.xaml.cs:

using System.Windows;

namespace ModalPopupDemo
{
   
public   partial   class Form2 : ModalDialog
    {
       
public Form2()
        {
            InitializeComponent();
        }

       
private   void btnClose_Click( object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}

弹出 Form2 的运行效果如下:


modaldialog3

 

至此,我们就实现了一个还算灵活的模态对话框弹出机制。这个好处在于,当我们要创建新的模态对话框时,在它的 Xaml 中只需关注其自身的显示逻辑即可,而不用关心如何显示到父窗体中,如何设置屏蔽层等细节问题,做到了一定程度的解耦。

 

注:本文提供了一个简单的实现方式,如果采用创建自定义控件等方法,也许能做到更好的封装,这个留待后续研究和探讨。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值