星辰.Net技术社区论坛

首页 » .NET » Silverlight » Silverlight 之模态对话框的通用模拟
admin - 2008-7-5 2:33:00
其原理跟我们在 HTML 中实现模拟的模态对话框的方法类似,无非是动态创建一个半透明的层盖住整个页面背景,让背后的元素无法操作,然后在其上显示对话框内容即可。不过,在这篇教程中创建的子窗体必须在调用者页面的 XAML 中手工声明,假如我们需要根据情况调出 n 个不同的对话框呢?这样做显然不够灵活。本文的目标是基于这种场景提供一个简单的封装,以便更方便的实现主/从调用的场景。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
例子的运行效果如下:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
点击第一个按钮后,显示如下:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
下面看看如何来实现这个效果。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
首先,我们来定义一个模态对话框的基类,它是一个用户控件(UserControl):qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
using System;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
using System.Windows.Controls;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
namespace ModalPopupDemoqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
{qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
public class ModalDialog: UserControlqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public event EventHandler Closed;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public void Close()qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
           
if (Closed != null)qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                Closed(
this, EventArgs.Empty);qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
public interface IModalDialogOpenerqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
void ShowModalDialog(ModalDialog dialog);qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
void CloseModalDialog();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
}qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
因为我们想在模态框关闭时,从父窗口中得到通知,所以给它定义了一个 Closed 事件。并且提供了该事件的触发函数 Close().qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
同时,我们还为模态框的父窗体(opener) 定义了一个接口 IModalDialogOpener.qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
其中顾名思义定义了打开和关闭模态框的两个方法。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
好了,现在我们创建一个模态对话框试试。假设是一个登录窗体,就叫它 Login 吧——在项目中添加一个用户控件 Login.qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
在 Login.xaml 文件里添加了一些 Markup 后,我们修改后端代码文件 Login.xaml.cs 如下:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
using System.Windows;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
namespace ModalPopupDemoqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
{qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
public partial class Login : ModalDialogqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public Login()qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            InitializeComponent();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
private void btnLogin_Click(object sender, RoutedEventArgs e)qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            Close();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
}qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
这里注意基类改成了 ModalDialog,就像在 ASP.NET 页面基类里所做的那样。并且我们在按钮的点击事件处理函数里调用了刚才定义的 Close 方法来关闭窗体。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
这时 Login.xaml 是这样的:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
<local:ModalDialog x:Class="ModalPopupDemo.Login"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns:local
="clr-namespace:ModalPopupDemo">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
<Grid x:Name="LayoutRoot">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
<Border CornerRadius="20" Background="#eeeeee" Width="300" Height="150">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
           
<StackPanel Orientation="Vertical" Margin="10">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
               
<StackPanel Orientation="Horizontal" Margin="10">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                   
<TextBlock Width="100">UserName:</TextBlock>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                   
<TextBox x:Name="txtUserName" Width="150" HorizontalAlignment="Right"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
               
</StackPanel>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
               
<StackPanel Orientation="Horizontal" Margin="10">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                   
<TextBlock Width="100">Password:</TextBlock>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                   
<TextBox x:Name="txtPassword" Width="150"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
               
</StackPanel>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
               
<StackPanel Margin="10">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                   
<Button x:Name="btnLogin" Content="Login" Width="50"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                        HorizontalAlignment
="Right" Click="btnLogin_Click"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
               
</StackPanel>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
           
</StackPanel>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
</Border>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
</Grid>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
</local:ModalDialog>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
需要注意的是这个 XAML 文档的根元素从默认的 UserControl 改成了 local:ModalDialog. 而这正是后台 partial class 的基类,Silverlight 中规定这两者必须一致的。这里 local 是一个名称空间,可以看到稍后的 xmlns:local="clr-namespace:ModalPopupDemo" 这段代码将它声明为了当前程序集的 ModalPopupDemo 名称空间。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
实现了子窗体,我们来看看如何实现父窗体。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
我们的父窗体 Page.xaml 是这样的:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
<UserControl x:Class="ModalPopupDemo.Page"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
<Grid x:Name="LayoutRoot" Background="White">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
<StackPanel Orientation="Vertical">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
           
<TextBlock FontSize="30" HorizontalAlignment="Center">This is the main page.</TextBlock>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
           
<Button x:Name="btnOpen1" Content="Open Login form" Click="btnOpen_Click" Width="150"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
           
<Button x:Name="btnOpen2" Content="Open another form" Click="btnOpen2_Click" Width="150" Margin="0,10"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
</StackPanel>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
<!-- 占位元素,用于在其中加载模态窗口 -->qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
<Border x:Name="placeHolder" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                Visibility
="Collapsed"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                Background
="#ff8a8a8a" Opacity="0.765"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
</Grid>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
</UserControl>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
这里照搬了 ScottGu 文章中的那个技巧,不过我改成了用一个 Border 元素来占位,同时将它的大小设置为全屏、半透明、不可见状态。这样它就既可以作为子窗体的容器,又可以在弹出模态对话框时遮住背景,一举两得。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
后端代码 Page.xaml.cs:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
using System.Windows;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
using System.Windows.Controls;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
namespace ModalPopupDemoqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
{qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
public partial class Page : UserControl, IModalDialogOpenerqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public Page()qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            InitializeComponent();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
private void btnOpen_Click(object sender, RoutedEventArgs e)qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            var login
= new Login();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            login.Closed
+= (sender2, args2) => CloseModalDialog();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            ShowModalDialog(login);qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
private void btnOpen2_Click(object sender, RoutedEventArgs e)qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            var form2
= new Form2();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            form2.Closed
+= (sender2, args2) => CloseModalDialog();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            ShowModalDialog(form2);qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
#region IModalDialogOpener implementationsqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public void ShowModalDialog(ModalDialog dialog)qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            placeHolder.Child
= dialog;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            placeHolder.Visibility
= Visibility.Visible;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public void CloseModalDialog()qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            placeHolder.Child
= null;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            placeHolder.Visibility
= Visibility.Collapsed;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
#endregionqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
}qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
这里 ShowModalDialog 和 CloseModalDialog 的实现相当简单,分别就是附加/移除相关的子窗体控件实例,并且显示/隐藏占位控件。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
页面中除了 Login 窗体外,还调用了另一个简单的用户控件 Form2,调用方式是非常类似的。Form2 的代码如下:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
Form2.xaml:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
<local:ModalDialog x:Class="ModalPopupDemo.Form2"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    xmlns:local
="clr-namespace:ModalPopupDemo">qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
<Grid>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
<Button x:Name="btnClose" Width="300" Height="200" Content="Close Me!"qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
                Click
="btnClose_Click" FontSize="40"/>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
</Grid>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
</local:ModalDialog>qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
Form2.xaml.cs:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
using System.Windows;qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
namespace ModalPopupDemoqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
{qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
   
public partial class Form2 : ModalDialogqÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
public Form2()qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            InitializeComponent();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
       
private void btnClose_Click(object sender, RoutedEventArgs e)qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        {qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
            Close();qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
        }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
    }qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
}qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
弹出 Form2 的运行效果如下:qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
至此,我们就实现了一个还算灵活的模态对话框弹出机制。这个好处在于,当我们要创建新的模态对话框时,在它的 Xaml 中只需关注其自身的显示逻辑即可,而不用关心如何显示到父窗体中,如何设置屏蔽层等细节问题,做到了一定程度的解耦。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
注:本文提供了一个简单的实现方式,如果采用创建自定义控件等方法,也许能做到更好的封装,这个留待后续研究和探讨。qÞT–fwww.netcsharp.cnñáˆs}µÚGÞÝ
1
查看完整版本: Silverlight 之模态对话框的通用模拟