背景
年后来到公司实习,第一个星期没有项目,也就无所事是,此时有一位前辈和我说,他们有一个系统是基于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,当然没有安装你也会被微软要求安装的~~
这里是展示页面的一个截图
再次声明,我的美工非常差,因此界面非常简单,我想点一下按钮这事大家都会用,所以我就不对这个界面多作解释了
下载请点这里
后话
再次非常感谢对我报以信任的公司,但同时希望不要因此而成了一个前台工程师……
切记这个版本只是一个“玩具”,千万别将他用于任何较为正式的环境