[オリジナル]シルバーポップアップウインドウ - デザイン

以前「ではSilverlightの[元]ポップアップウインドウ-ショー」、唯一のショーは私が作ったソースの非常に基本的なバージョンを提供し、機能Silverlightベースのポップアップで作られました。

このバージョンは、確かに非常に基礎、そのうちあなたは彼らの全体的に、単にコードを読み取ることによって、整理していないので、もし理解することは、おそらく困難な混乱の度合いがされている間、多くの非最適化されたコードの構造があるである全体の作業プロセスA。

この記事の目的は、一般的な紹介と、作品全体を通して、デザインのアイデアが描か機能ポップアップウィンドウの構造設計から、であり、このアイデアは、最も安定した部分にはプロジェクト全体のためになるにバインドされ、これは、将来的に大きな変化はありません。

前回パークの友達のダウンロードは、してください場合は、ソースコードは、小さな部分を更新されている再ダウンロードを

全体の構造

まず、それが進路を添付するので、このプロジェクトの構造の鳥瞰図の全体のコンセプトにする必要があり、これは通常のUMLダイアグラムではなく、多くの問題を説明するのに十分なものでなければなりません

無題

それは、このような構造で見ることができ、6つの非常に重要な要素があり、それらは:

  1. PopupService:機能ポップアップウィンドウを提供してコアコンポーネント、ポップアップウィンドウがすべてで制御されています
  2. LayoutMaskは:マスク層のポップアップウィンドウの下部に位置しており、下位層は、制御機能モーダルダイアログボックスが開きシールドを提供します
  3. PopupBox:ポップアップウィンドウを、基本クラスを、特定の通信及び相互作用能力との間LayoutMaskを有する、特殊効果、および他の機能を提供しながら
  4. BoxPage MessagePage /:PopupBox実装は、異なる機能に対応するが、この設計に基づいて、あまりにも多くのコードの実現にそこに関連付けられている、それがキーとして語られることはありません
  5. DragService:ウィンドウ管理機能をマウスでドラッグし、マウス関連の実装でドラッグ全てながら、賛成とデカップリング機能を表示するには、外部からのドラッグの導入は、クラスに改訂BUG後の便利な場所と外観を重視しました

PopupService

私の設計では、我々は直接、このようなBoxPageボックス=新しいBoxPage()などの新しいオブジェクトを作成することはできません。ポップアップウィンドウを生成します。

当然最初的设计确实是可以使用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方法,方法如下:

プライベートボイド DisableDrag()
{
PopupBox.DragMouseCaptureArea.MouseLeftButtonDown - = Drag_MouseLeftButtonDown。
PopupBox.DragMouseCaptureArea.MouseMove - = Drag_MouseMove。
PopupBox.DragMouseCaptureArea.MouseLeftButtonUp - = Drag_MouseLeftButtonUp。
}

 

3つのイベントをキャンセルすることで、自然に機能をドラッグ停止。

次はPopupBoxカスタムを達成するために、このフレームワークを拡張する方法をようやくなどのアニメーション、などの複雑なコンテンツを実現するためにほぼ同じ時間にLayoutMaskを達成するための方法のいくつかについて話をするより詳しくなる、となります。

ダウンロードソース

ます。https://www.cnblogs.com/GrayZhang/archive/2009/02/24/silverlight-popupbox-design.htmlで再現

おすすめ

転載: blog.csdn.net/weixin_34249678/article/details/93272184