背景
年后来到公司实习,第一个星期没有项目,也就无所事是,此时有一位前辈和我说,他们有一个系统 是基于Silverlight研发的,而其中使用的弹出窗口存在着不少的问题,因此如果可以的话,要我做出一 个弹出窗口的功能,其子元素可以是自定义的任何控件,
Silverlight的弹出窗口展示
。我很高兴进入公司才几天就能有这样的任务,每一次我去不同的公司实习,总是能在很短的时间内被 “委以重任(当然更多的是我自己在自恋)”去进行可利用的基础设施的研发,非常感谢从大二开始都一 直如此信任和看得我的各家公司,我想只有在这样的环境中才可以全力以赴……
总之接下此任务的情况是这样的,那么至此我对Silverlight到底有多少的知识呢?答案是,零。认识 我的人都知道,我是非常非常没有美工水平的,因此对于几乎所有前台开发的技术都敬而远之,而其中如 此新颖的Silverlight更是令我望而生怯。
但是事已如此,刚进公司也不能说我不会做,于是就硬着头皮上了,最终的结果是这样的:
首先用半天的时间基本撑握了Silverlight的模型,包括了解了Xaml的语法如DependencyProperty, AttachedProeprty等,基本熟悉了相关的API,至于控件树之类的理念,Winform和Webform也是基本相同 的模型,因此熟悉起来也相对较快
随后用半天的时间,折腾出了一个弹出窗口的原型,看起来确实相当地舒服,弹出一个窗口再关闭, 弹出两个窗口再相继关闭,并没有出现特别大的问题
然后,5分钟的时间内,发现这东西并不完美,存在着不少的问题,于是从架构上完全推倒了前一次的 作品,很明显我的实现是基于一个错误的设计,也因此需要将整个模型进行重新的设计并进行再一次的实 现
经过再半天的努力,终于有了一个较为理想化的设计,并完成了这个版本的实现
我们的选择
对于弹出窗口这样的功能,我们在Silverlight中是有多种选择的,在介绍我的作品之前,我们先来看 一下各种选择的优点和缺点
选择1:MessageBox
确实Silverlight中是有一个MesasgeBox的,和Winform中的一样,调用MessageBox.Show即可,但其也 存在着不少问题,当然在这之前我们要先向好的一方面看
优点:MessageBox是同步的,即Show方法调用后可以直接取得返回值,同步编程在简单性上相比异步 编程有着非常大的优势,因此确实可以作为一个好的选择
缺点:知道为什么各JS框架都要出个MessageBox么,就是因为alert这东西实在太难看了,而现在我们 面对着更为绚丽的Silverlight应用,在赏心悦目地享受着的同时,难道要时不时看到一个和alert窗口一 模一样的家伙弹出又消失?
选择2:Popup
Popup是Silverlight基础类库提供的一个控件,可以直接完成弹出的功能
优点:操作简单,可以用Xaml表述,封装了弹出的功能,样式可自定义,作为官方的解决方案甚为强 大
缺点:据前辈说,Popup中包含有DataGrid之类的复杂控件的时候,会有Bug出现,这也是需要我去完 全做一个弹出窗口出来的主要原因……
我的作品—PopupBox
我没有办法称之为MessageBox,因为基础类库中有这东西
我也没办法称之为Popup,因为基础类库中也有这东西
我可不想自己的作品在出现之初就与基础类库有着冲突,要求调用者必须使用全局命名空间引用,更 不想借用官方的名字给人产生混淆的感觉
作为一个作品的展示,先来看看这东西是怎么使用的。事实上这并不是一个控件,而是一个类,因此 你不能使用Xaml声明他,作出这种决定的原因,一是我对Xaml只是掌握,并不熟悉,因此如果要做到使用 Xaml进行声明,可能至今都看不到这家伙的影子,其次是因为弹出窗口往往是通过编程来动态确定其内容 并控制是否弹出的,因此将之作为一个类来使用也不会产生太大的麻烦
为了使用我们的弹出窗口,首先需要获取一个称为PopupService的对象,其掌管着一切弹出之物,你 不能显式地通过new来构造一个PopupService,而必须通过工厂方式获取这个对象
PopupService service = PopupService.GetServiceFor(LayoutRoot);
随后,我们可以使用多种重载,从PopupService中通过调用GetBoxPage来获取弹出窗口的实例,其第 一个参数是显示在弹出窗口中的控件,类型为FrameworkElement,第二个参数为窗口的标题,后面的参数 控制着诸如是否可以拖动,是否显示关闭图标等功能,调用如下
BoxPage box = service.GetBoxPage(
new MyControl(), //显示的控件
Title.Text, //标题
true, //是否可以拖动
true //是否显示关闭图标
);
你可以给BoxPage加上出现和消失时的特效,现有支持的特效有淡入、缩放以及无特效,当然从设计上 可以非常轻松地对其进行扩展
box.Effect = Effect.Fade(box);
BoxPage拥有ShowComplete和CloseComplete事件,你可以通过注册这些事件来做一些操作
box.ShowComplete += new EventHandler(Box_ShowComplete);
box.CloseComplete += new EventHandler(Box_CloseComplete);
最后调用Show方法弹出窗口,当然也可以调用ShowAsModal方法来弹出一个模式窗口,所谓模式窗口就 是在弹出后会遮住下面所有的控件
box.Show();
box.ShowAsModal();
如果你觉得这样的使用还算简单的话,那么我想我已经成功了一半,当然我们还有另一个控件,你可 以通过调用PopupService的GetMessagePage方法来获取一个消息对话框,这是一个只用来显示消息的不可 以自定义控件作为内容的简单的对话框,在此放上两张图先展示一下效果
在展示以前,我还是想再三声明,我的美工不是一般地差,因此对话框的样式极为难看,请给我时间 去改进样式,谢谢……
左边的是弹出窗口,右边的是消息窗口
这个版本相比上一个版本的改进
当然上一个版本大家是没有见过的,也永远不会见到,但作为对自己的负责,我还是记录一下这个版 本的改进,并且在此之间应当也能略窥此弹出窗口的功能
1.解决多个弹出窗口时遮罩层重复问题,现在多个弹出窗口将共用一个遮罩层
2.解决多个弹出窗口中有一个窗口为模式对话框的情况下,关闭此模式对话框将不会取消模式遮罩层 的问题
3.解决多个弹出窗口的情况下,点击后台窗口将其置于最上层的问题
后续版本将要出现的改进
也许是下一个版本,也许是下n个版本,但是这些改进已经列入了计划,总有一天他们会得到实现
1.多国语言化,主要是按钮的文字
2.提供更多的样式自定义功能
3.优化PopupService的存储,将已经无用的PopupService(即不再管理任何弹出窗)及时从缓存中清 除
4.BoxPage加上Border
5.当前窗口弹出均在遮罩层的中间,改进为可以指定窗口弹出时相对遮罩层的位置
6.如果弹出窗口的位置已经有其他窗口的话,将窗口进行一定量的偏移以保证不会完全挡住原有窗口
7.弹出窗口在拖动的时候可以移出其遮罩层的范围,最好可以控制遮罩层的大小且不允许弹出窗口随 意移动,这样可以将弹出窗口的可移动范围控制在一个区域之内
已知的Bug
当然我只是一只菜鸟,出来的东西必定有着问题,在此是至今发现的问题,也希望大家多多提供 Bug
1.当在BoxPage中加载图片时,使用相对路径将造成读取类库的相对路径的图片,产生图片读取错误, 不知如何才能设置为去读取当前运行项目的相对路径
2.MessagePage和BoxPage中依旧存在部分相似、相同代码,无法完全抽象分离,需要考虑优化设计
3.MessagePage中的内容为一定数量并且没有空格的英文字符串时,会产生最后一个字符换行的问题
4.使用渐隐特效时,因为窗口消失有时间,所以可以多次点击MessagePage上的不同按钮,但结果以最 后一次点击的按钮为准,应当修正为后续按钮的点击都无效
5.无数未知的问题和BUG……
设计及实现的介绍
写到这里的时候我突然发现自己有点累了,虽然这是最重要的内容,但是老妈难得来上海看我,现在 正在旁边,实在没有什么心情继续,因此请允许我将这“精彩”的重头戏留到下一篇
源码分享
以下是本作品的源码地址,对于没有Silverlight开发环境的同学,就不要指望去打开项目了,但是 SilverlightApplication1\debug\bin\TestPage.htm依旧是可以运行的,相信你已经安装了Silverlight Runtime,当然没有安装你也会被微软要求安装的~~
这里是展示页面的一个截图
再次声明,我的美工非常差,因此界面非常简单,我想点一下按钮这事大家都会用,所以我就不对这 个界面多作解释了
本文配套源码