[Original] Silverlight pop-up window - Design

In the previous " [original] pop-up window for Silverlight - show ", only made the show is a functional Silverlight-based pop-ups I made and provide a very basic version of the source.

This version is indeed very foundation, of which there are many non-optimized code structure while there is a degree of confusion, so if you do not sort out their on the whole, just by reading the code is probably difficult to understand that the entire work process of.

The purpose of this post is, from the structural design of the pop-up window that functions as a general introduction, the design ideas throughout the entire work portrayed, and this idea is bound to be for the whole project in the most stable part, It will not have much change in the future.

Source code has been updated small part, if the last time a download of the Friends of the Park, please re-download

the whole frame

First, it should be on the whole concept of a bird's-eye view of the structure of this project, therefore attach Chart a course, this is not a regular UML diagram, but should be sufficient to explain many problems

Untitled

It can be seen in this structure, there are six very important components, they are:

  1. PopupService: The core component that provides features pop-up window, pop-up windows are all controlled by
  2. LayoutMask: is located in the lower part of the pop-up window of the mask layer, the lower layer will also provide shielding control function modal dialog box opens
  3. PopupBox: pop-up window, the base class, having LayoutMask between certain communication and interaction capabilities, while providing special effects and other functions
  4. BoxPage MessagePage /: PopupBox implementation, corresponding to different functions, but based on this design, there is too much associated with the realization of the code, it will not be told as a key
  5. DragService: mouse to drag the window management features, the introduction of dragging from the outside to show favor and decoupling functions, all while dragging with the mouse-related implementation focused on a class, convenient location and appearance after revision BUG

PopupService

In my design, we can not directly create new objects, such as BoxPage box = new BoxPage (); to generate a pop-up window.

当然最初的设计确实是可以使用new来创建的,但在日后的测试中发现,这样的创建会产生很多问题,在下面例举一二:

1. 每一个弹出窗口会拥有一个遮罩层,因此后开的弹出窗口一定比之前的在更上层,这就导致先打开的弹出窗口永远不可能被移到最上方,而在日常操作中,点击后面的窗口应当可以将这个窗口移到最前方

2. 过多的遮罩层导致了资源大量的占用,当打开5个以上弹出窗口时,应用程序的响应将出现明显变慢的现象

这种种缺陷,都将矛头指向了一处,即我们需要一个统一的环境来管理弹出窗口及其遮罩层,保证多个弹出窗口所拥有的遮罩层的“单例性”,因此就产生了通过PopupService这样一个“服务”来管理的策略,其管理方案如下:

1. 一个PopupService对应一个Control,此Control就是弹出窗口的拥有者

2. PopupService会产生一个遮罩层与此Control联系,并且在此后有弹出窗口时都会使用这唯一的一个遮罩层

3. 无论何时,对于同一个Control,将只会有一个PopupService进行服务

看到上面的方案后,我想大多数人都会发现,PopupService变成了“伪单例”或“局部单例”的类,因此就着“单例模式”的设计,我们肯定不能简单地通过new的构造来使用这个类,因此就引出了该类的一个静态方法GetServiceFor,此方法接收一个参数即PopupService的拥有者,当然为了能够将遮罩层铺在控件上,我们要求控件是一个布局控件,即Panel类型。

为了保证PopupService的伪单例的特性,必须将生成的对象保存起来,这里简单地用到了Dictionary进行保存,当调用GetServiceFor方法时,首先在缓存池中寻找,如果找不到则调用new进行实例化,随后放入缓存池中进行持久的保存,以保证日后不会再发生重新构造的问题,其具体代码如下:

public static PopupService GetServiceFor(Panel owner)
{
if (!Cache.ContainsKey(owner))
{
PopupService service = new PopupService(owner);
Cache[owner] = service;
}
return Cache[owner];
}

 

在这里并没有显式地对Cache对象(Dictionary<Panel, PopupService>类型)进行线程锁,这是因为Silverlight与Winform类似,所有对UI的操作只能在主线程进行,因此我认为没有必要在一个单线程执行的环境中进行加锁。

上面讲了PopupService的产生过程,下面例举一下PopupService提供的方法,由于方法十分简单,就不展开讨论,其方法主要有3个:

1. GetBoxPage:获取在此PopupService管理下的BoxPage的一个对象,有重载

2. GetMessagePage:获取在此PopupService管理下的MessagePage的一个对象,有重载

3. RegisterPopupBox:对已经创建好的但没有纳入PopupService管理的PopupBox对象进行注册,注册后的对象将由此PopupService进行管理,即分配一个LayoutMask

LayoutMask

LayoutMask听起来就叫“遮罩层”,但其实他不是一个控件,其地位类似于PopupService,是一个“管理者”的角色,他将管理多个PopupBox,从而将弹出窗口于PopupService分享开来,起到解耦的作用,尽可能地减少PopupService的负担,从而使程序结构更加清晰。

而从界面的展现角度来讲,又可以认为LayoutMask确实是一个“控件”,因为他会生成一个Canvas平铺于其所有者(Panel类型)之上,此Canvas就是真正的“遮罩层”,对Canvas设定背景色就会产生模态对话框的效果,同时所有的弹出窗口都将作为这个Canvas的子元素,通过ZIndex的改变来确定哪一个对话框处在最前端,从而得以模仿我们日常使用中“点击在后面的窗口之后窗口会被置于最前端”的效果。

LayoutMask提供了与弹出窗口的管理相关的若干方法,其主要对外的方法如下:

1. AddBox:将一个弹出窗口添加进来,当然在调用窗口的Show方法之前,窗口是不会显示出来的,AddBox只是将窗口与本体进行联系。

2. RemoveBox:与AddBox相反,将一个窗口从本容器中除去,此后即使调用窗口的Show方法,窗口也不会再显示出来了,因此被移除的Box在XAML树中已经是一个孤岛,与XAML根没有联系的元素是不可能被渲染的。

3. PositionBox:在上一个版本中称为CenterBox,在此版本中加入了新的功能,即连续打开窗口时,窗口不会叠在一起,而会按一定的偏移量相互错开,因此方法也被改名为PositionBox,其作用就是找到一个合适的位置来放置弹出窗口。

除了公开的方法之外,其部分内部方法也有着举足轻重的作用:

1. CheckModal:每当AddBox或RemoveBox调用时,都会重新检查是否有弹出窗口是模态的,如果在这个LayoutMask管理的弹出对话框中有一个或多个是模态的,则需要将作为遮罩层使用的Canvas改为模态以屏蔽下面的其他控件,其方式是简单地给Canvas加上背景色。

2. MaxZIndex:返回管理的所有弹出对话框的ZIndex中的最大值,这方便了在后层的对话框移到前层,只需要设定其ZIndex为MaxZIndex + 1即可。

3. ReorderZIndex:当然ZIndex是有最大值的,一个很熟悉的数字65535,所以如果不断地给窗口增加ZIndex,必将导致ZIndex超出范围,这当然不是我们所希望的结果。因此就有了一个方法,当ZIndex已经过大的时候,将所有控件的ZIndex进行重新排列,按照现有的窗口叠放次序,从1开始重新排列ZIndex,保证ZIndex永远不会超过最大值(当然你硬要连续打开65536个窗口我也真没办法……)

4. RenderMask:当所有弹出窗口关闭时,遮罩层也应该相应消失,而当遮罩层未打开时,也不能打开新的窗口,所以这里就有一个流程,在第一个窗口打开时需要先将遮罩层打开,因此有了RenderMask方法,负责将遮罩层加入到所有者的子元素中并显示出来。

DragService:

DragService是一个辅助类,他将提供窗口的鼠标拖动功能,这个类的结构也非常简单,在构造的时候将需要窗口的对象传递给他,随后便可通过设定IsDraggable属性来打开/关闭鼠标拖动的功能,对于鼠标拖动的实现,在网上无论是FLEX,JS,WINFORM还是Silverlight都有很多的实现了,我使用的也与这些大同小异,主要就是通过监听MouseDown,MouseUp,MouseMove这3个事件来实现,可以看一下IsDraggable的实现:

public bool IsDraggable
{
get
{
return m_IsDraggable;
}
set
{
if (m_IsDraggable == value)
{
return;
}
if (value)
{
EnableDrag();
}
else
{
DisableDrag();
}
}
}

当IsDraggable被设定为true时,会调用EnableDrag方法,此方法如下:

private void EnableDrag()
{
PopupBox.DragMouseCaptureArea.MouseLeftButtonDown +=
new MouseButtonEventHandler(Drag_MouseLeftButtonDown);
PopupBox.DragMouseCaptureArea.MouseMove +=
new MouseEventHandler(Drag_MouseMove);
PopupBox.DragMouseCaptureArea.MouseLeftButtonUp +=
new MouseButtonEventHandler(Drag_MouseLeftButtonUp);
}

 

此方法监听了3个事件,对于拖动的逻辑,是在MouseDown的时候设定“开始拖动”,在MouseMove的时候,如果已经“开始拖动”,则计算出鼠标移动的距离,使窗口移动同样的距离,最后在MouseUp的时候“停止拖动”,具体代码可以在源文件中找到,不再浪费此处的空间了~~

而当IsDraggable设为false的时候,则调用DisableDrag方法,方法如下:

private void DisableDrag()
{
PopupBox.DragMouseCaptureArea.MouseLeftButtonDown -= Drag_MouseLeftButtonDown;
PopupBox.DragMouseCaptureArea.MouseMove -= Drag_MouseMove;
PopupBox.DragMouseCaptureArea.MouseLeftButtonUp -= Drag_MouseLeftButtonUp;
}

 

By cancellation of three events, naturally stopped dragging function.

after

Next will be more detail to talk about some of the ways to achieve the LayoutMask in, about the same time to realize complex content such as animation, etc., and finally how to extend this framework to achieve PopupBox custom.

Download Source

Reproduced in: https: //www.cnblogs.com/GrayZhang/archive/2009/02/24/silverlight-popupbox-design.html

Guess you like

Origin blog.csdn.net/weixin_34249678/article/details/93272184