![](/images/cnblogs_com/youzai/BallClockIce.PNG)
动机(Motivation):
在软件的构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种
复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
在这种情况下,我们可以使用一个“中介对象”来管理对象间的关联关系,避免相互的对象之间
紧耦合引用关系,从而更好地抵御变化。
在开始本次之前,有必要对上次的一点儿遗漏的地方再废话一下。
关机的实现:关机也是调用Windows API 实现的,具体代码参考上次列出的清单
PInvokeService.DoExitWin(int) 函数。
值得一提的是 关机对话框调用了 Windows 中一个未公开的 API, 我们不知道这个函数
的名字,但是我们知道它的地址,我使用 ISO C++ 进行了封装,代码如下:
1
#include <windows.h>
2![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
namespace CPPCode
{
4![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
namespace Shutdown
{
5
typedef int (CALLBACK *SHUTDOWNDLG) (int);
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
extern "C"
{
8
__declspec(dllexport) void ShowShutdownDialog();
9
}
10
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
void ShowShutdownDialog()
{
12
HINSTANCE hInst = LoadLibrary(LPCWSTR(L"shell32.dll"));
13
SHUTDOWNDLG SHShutDownDialog;
14![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if(hInst != NULL)
{
16
// Shutdown dialog API is not publicized by Microsoft but we know
17
// It's ID is 60
18
SHShutDownDialog = (SHUTDOWNDLG)GetProcAddress(hInst, (LPSTR)60);
19
SHShutDownDialog(0);
20
FreeLibrary(hInst);
21
}
22
}
23
}
24
}
25![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
编译生成的 dll 是 CPPCode.Shutdown.dll
然后你可以在 PInvokeService.cs 里面找到这样的代码:
[DllImport("CPPCode.Shutdown.dll", ExactSpelling = true, SetLastError = true)]
public static extern void ShowShutdownDialog();
抛开具体细节讨论抽象,现在可以开始正式讲本次的内容了。
Mediator模式的应用
动机(Motivation):
在软件的构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种
复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
在这种情况下,我们可以使用一个“中介对象”来管理对象间的关联关系,避免相互的对象之间
紧耦合引用关系,从而更好地抵御变化。
意图(Intent):
用一个中介对象来封装一系列对象交互。中介者使各对象不需要显示的相互引用,从而使其
耦合松散,而且可以独立地改变它们之间的交互。
————《设计模式》GOF
我们抛开这些精典的理论,看看如何把我们的功能细节分离到一系列 class 中,以及让菜单工作。
先总体上看一下最后的类图:
![](https://www.cnblogs.com/images/cnblogs_com/youzai/CSharpCode.WinClock.png)
点击下载完整类图
Element 和 Mediator 都是抽象类,但他们之间存在很强的耦合关系, Element 依赖 Mediator 对象,
同时Mediator对象内部包含 Element 的集合。因此 Mediator 也依赖 Element,Element 了类化就
可以在这些类中实现我们的功能细节。那就先看看这两个抽象类吧:
1
// Element.cs
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
internal abstract class Element : IDisposable
{
3
private ICommand theCommand;
4
protected Mediator mediator;
5![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Element(Mediator mediator)
{
7
this.mediator = mediator;
8
this.mediator.AddElement(this);
9
}
10![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
~Element()
{
12
Dispose(false);
13
}
14![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected MainForm mainForm
{
16![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
17
return mediator.MainForm;
18
}
19
}
20![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected ICommand command
{
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
23
return theCommand;
24
}
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{
26
theCommand = value;
27![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (theCommand != null)
{
28
RegisterEvents(false);
29
RegisterEvents(true);
30
}
31
}
32
}
33![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
34![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public object Source
{
35![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
36
return this.command.Source;
37
}
38
}
39![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
40![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected void NotifyStatusChanged()
{
41![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (mediator != null)
{
42
mediator.Notify();
43
}
44
}
45![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected ClockOption clockOpt
{
47![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
48
return mainForm.ClockOption;
49
}
50
}
51![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
52![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected RemindOperate remindOperate
{
53![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
54
return mainForm.RemindOperate;
55
}
56
}
57![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
58
// Only dispose the item, do not dispose the mediator
59
// because the mediator is sigalton, it need dispose
60
// only once
61![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected virtual void Dispose(bool disposing)
{
62![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (disposing)
{
63
// Do not move this outside of the if block, because
64
// memory of the element may have been collected by GC
65![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
try
{
66
RegisterEvents(false);
67
this.command.Dispose();
68![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} finally
{
69
this.command = null;
70
}
71
}
72
}
73![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
74
protected abstract void OnExecute();
75![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected internal virtual void OnStatusChanged()
{
76
}
77![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
78![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void RegisterEvents(bool register)
{
79![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (register)
{
80
this.command.Execute += CommandOnExecuted;
81![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
82
this.command.Execute -= CommandOnExecuted;
83
}
84
}
85![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
86![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void CommandOnExecuted(object sender, EventArgs args)
{
87
OnExecute();
88
NotifyStatusChanged();
89
}
90![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
91![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
IDisposable Members#region IDisposable Members
92![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
93![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void Dispose()
{
94
Dispose(true);
95
GC.SuppressFinalize(this);
96
}
97![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
98
#endregion
99
}
100
101
// Mediator.cs
102![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
internal abstract class Mediator : IDisposable
{
103
private MainForm mainForm;
104
protected IList<Element> elementList;
105![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
106![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Mediator(MainForm mainForm)
{
107
this.elementList = new List<Element>();
108
this.mainForm = mainForm;
109
}
110![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
111![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
~Mediator()
{
112
Dispose(false);
113
}
114![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
115![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public MainForm MainForm
{
116![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
117
return this.mainForm;
118
}
119
}
120![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
121![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public IList<Element> ElementList
{
122![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
123
return this.elementList;
124
}
125
}
126![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
127
// Notify all other elements to change status
128
public abstract void Notify(Element sorceElement);
129![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
130
// Notify all elements to change status
131
public abstract void Notify();
132![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
133![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public virtual void AddElement(Element element)
{
134
// Mainmenu status maybe depended on submenu, So Mainmenu Element should
135
// be placed after submenu Element
136
elementList.Insert(0, element);
137
}
138![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
139![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected virtual void Dispose(bool disposing)
{
140![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (disposing)
{
141![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
try
{
142![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
foreach (Element element in this.elementList)
{
143
element.Dispose();
144
}
145![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} finally
{
146
elementList.Clear();
147
elementList = null;
148
}
149
}
150
}
151![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
152![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
IDisposable Members#region IDisposable Members
153![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
154![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void Dispose()
{
155
Dispose(true);
156
GC.SuppressFinalize(this);
157
}
158![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
159
#endregion
160
}
在这里,MainForm 是主窗口,它充当了中介,它可以被 Element 和 Mediator 引用。
你可能已经发现,Element还依赖于一个 ICommand 接口,IComand 接口引用了一个 object 的对象
在它的一个实现类 MenuItemCommand 中,这个对象指向的是一个 ToolStripMenuItem 的对象。
1
// ICommand.cs
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
public interface ICommand : IDisposable
{
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
object Source
{
4
get;
5
}
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
event EventHandler Execute;
8
}
9
10
// MenuItemCommand.cs
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
internal class MenuItemCommand : ICommand
{
12
private ToolStripMenuItem source;
13
protected EventHandlerList Events;
14
private static readonly object EventExecute = new object();
15![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public MenuItemCommand(ToolStripMenuItem source)
{
17
this.Events = new EventHandlerList();
18
this.source = source;
19
this.source.Click += SourceOnClick;
20
}
21![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
~MenuItemCommand()
{
23
Dispose(false);
24
}
25![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void SourceOnClick(object sender, EventArgs args)
{
27
OnExecute(EventArgs.Empty);
28
}
29![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
30![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected virtual void Dispose(bool disposing)
{
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (disposing)
{
32
this.source.Click -= SourceOnClick;
33
}
34
}
35![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected virtual void OnExecute(EventArgs args)
{
37
EventHandler handler = this.Events[EventExecute] as EventHandler;
38![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (handler != null)
{
39
handler(this, args);
40
}
41
}
42![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
43![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
ICommand Members#region ICommand Members
44![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
45![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public object Source
{
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
47
return this.source;
48
}
49
}
50![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
51![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public event EventHandler Execute
{
52![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
add
{
53
this.Events.AddHandler(EventExecute, value);
54
}
55![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
remove
{
56
this.Events.RemoveHandler(EventExecute, value);
57
}
58
}
59![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
60
#endregion
61![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
62![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
IDisposable Members#region IDisposable Members
63![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
64![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void Dispose()
{
65
Dispose(true);
66
GC.SuppressFinalize(this);
67
}
68![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
69
#endregion
70
}
那么你可能会问,Element 为什么不直接依赖 ToolStripMenuItem 呢?其实在这个项目里,我们完全
可以这么做,因为我们要使用的仅仅是ToolStripMenuItem, 我们之所以抽象出来一个 ICommand, 完全是
考虑到可能会用到其他的UI组件比如 Button 等,那么如果有这样的需求,很简单,我们可以实现一个
ButtonCommand,让他实现 ICommand 接口,这样保证了接口的统一,它就可以和 ToolStripMenuItem 一起
工作了。简单地说,抽象一个 ICommand 只是为了统一接口。Element 和 Mediator 还实现了 IDispose
接口,这是.Net中被经常提到的 Dispose 模式,如果感兴趣,你可以看看我的另一篇博客:
.Net Dispose 模式 与 C++/CLI 确定性资源清理
实际使用中,Dispose 方法都是通过 Mediator 对象调用的。
好的,抽象已经完成,看看具体怎么实现各个 Menu 的工作细节吧。举一个穿透桌面功能的例子吧:
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
internal class PenetrateElement : Element
{
2
public PenetrateElement(Mediator mediator, ToolStripMenuItem source)
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
: base(mediator)
{
4
base.command = new MenuItemCommand(source);
5
}
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnExecute()
{
8
clockOpt.Penetrate = !clockOpt.Penetrate;
9![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10
byte alpha = (byte)(mainForm.Opacity * 255);
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (clockOpt.Penetrate)
{
12
PenetrateService.MousePenetrate(mainForm, alpha);
13![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
14
PenetrateService.MouseNotPenetrate(mainForm, alpha);
15
}
16
}
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected internal override void OnStatusChanged()
{
19
ToolStripMenuItem menuItem = base.Source as ToolStripMenuItem;
20
menuItem.Checked = clockOpt.Penetrate;
21
}
22
}
OnExecute 是点击菜单是会执行的方法,OnStatusChanged 的调用可能是因为用户单击了任何一个菜单,
也可能是MainForm中的代码调用了 mediator.Notify(), 由于 Mediator 中引用着所有 Element 的集合,
所以调用 Notify() 将导致所有 Element 的 OnStatusChanged() 被调用,同样,直接点击任何一个菜单项,
也会导致其他(还有自身)菜单接到通知,即 OnStatusChanged() 被调用。所以这些都是在抽象基类里面完成
的,实体 Element 只需要处理这两个方法就可以了。实体 Mediator 本软件中只有一个,他的实现很简单。
1
// ContextMenuMediator.cs
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
internal sealed class ContextMenuMediator : Mediator
{
3
public ContextMenuMediator(MainForm mainForm)
4![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
: base(mainForm)
{
5
}
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public override void Notify(Element sorceElement)
{
8
// elementList may be null because it has been disposed
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (elementList != null)
{
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
foreach (Element element in elementList)
{
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (!object.ReferenceEquals(element, sorceElement))
{
12
element.OnStatusChanged();
13
}
14
}
15
}
16
}
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public override void Notify()
{
19
// elementList may be null because it has been disposed
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (elementList != null)
{
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
foreach (Element element in elementList)
{
22
element.OnStatusChanged();
23
}
24
}
25
}
26
}
27![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
为了更好地利用 Visual Studio 的 Designer 设计器,菜单还是在 MainForm 里面用 Designer 生成的,只是在
MainForm 的构造行函数里面,菜单项被 Element 引用。由于进行了设计,功能都被分散了,MainForm 里面的实体
代码并不多,看看吧。
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
public partial class MainForm : Form, IMementoCapable
{
2
private ClockOption clockOpt;
3
private RemindOperate remindOperate;
4
private Mediator mediator;
5
private Point mousePosition;
6
private Bitmap currentBitmap;
7
private WindowShapeMaker windowShapeMaker;
8![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
private static readonly int WM_KEYDOWN = 0x0100;
10
internal static readonly string AppPath = ClockOption.AppPath;
11![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public MainForm(ClockOption clockOpt)
{
13
InitializeComponent();
14
EnableDoubleBuffering();
15
this.clockOpt = clockOpt;
16
remindOperate = new RemindOperate();
17
Core.Properties properties = PropertyService.Get<Core.Properties>("WinClock.RemindOperate", new Core.Properties());
18
remindOperate.SetMemento(properties);
19![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
this.mousePosition = Point.Empty;
21
this.Cursor = Cursors.SizeAll;
22
windowShapeMaker = new WindowShapeMaker(this, clockOpt);
23
}
24![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
internal ClockOption ClockOption
{
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
27
return this.clockOpt;
28
}
29
}
30![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
internal RemindOperate RemindOperate
{
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
33
return this.remindOperate;
34
}
35
}
36![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
37![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
internal void RefreshSkin()
{
38
string path = Path.Combine(Path.GetDirectoryName(typeof(MainForm).Assembly.Location), "Images");
39
string filenaeme = Path.Combine(path, clockOpt.Filename);
40![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
41![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
try
{
42![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (!File.Exists(filenaeme))
{
43
clockOpt.Filename = "Default.png";
44
filenaeme = Path.Combine(path, clockOpt.Filename);
45
}
46![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
47![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (currentBitmap != null)
{
48
currentBitmap.Dispose();
49
}
50
currentBitmap = new Bitmap(filenaeme);
51
RefreshWindow();
52![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} catch (Exception e)
{
53![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
using (EventLog elog = new EventLog())
{
54
elog.Log = "Application";
55
elog.Source = "WinClock";
56
elog.WriteEntry("Error: " + e.Message, EventLogEntryType.Error);
57
}
58
Environment.FailFast(ResourceService.GetString("CSharpCode.WinClock.MainForm.LackComponent"));
59
}
60
}
61![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
62![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
internal void RefreshWindow()
{
63
windowShapeMaker.RefreshWindow(currentBitmap);
64
}
65![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
66![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
internal void CheckBounds(ref Point location)
{
67![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (clockOpt.CheckBounds)
{
68
Rectangle rectScreen = Screen.GetWorkingArea(this);
69![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (location.X < rectScreen.Left)
{
70
location.X = rectScreen.Left;
71![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else if (location.X + this.ClientSize.Width > rectScreen.Right)
{
72
location.X = rectScreen.Right - this.ClientSize.Width;
73
}
74![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
75![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (location.Y < rectScreen.Top)
{
76
location.Y = rectScreen.Top;
77![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else if (location.Y + this.ClientSize.Height > rectScreen.Bottom)
{
78
location.Y = rectScreen.Bottom - this.ClientSize.Height;
79
}
80
}
81
}
82![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
83![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnLoad(EventArgs e)
{
84
base.OnLoad(e);
85
SetupMenuItemElements();
86
mediator.Notify();
87
byte alpha = clockOpt.Opacity;
88![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (clockOpt.Penetrate)
{
89
PenetrateService.MousePenetrate(this, alpha);
90![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
91
PenetrateService.MouseNotPenetrate(this, alpha);
92
}
93
RefreshSkin();
94
}
95![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
96![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseMove(MouseEventArgs e)
{
97![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (e.Button == MouseButtons.Left)
{
98
// The clock is fixed up on the desktop
99
if (!clockOpt.CanMove)
100
return;
101![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
102
int left = this.Location.X + e.Location.X - this.mousePosition.X;
103
int top = this.Location.Y + e.Location.Y - this.mousePosition.Y;
104
Point location = new Point(left, top);
105
CheckBounds(ref location);
106
this.SetBounds(location.X, location.Y, this.ClientSize.Width, this.ClientSize.Height);
107
clockOpt.Location = this.Location;
108
}
109![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
110
base.OnMouseMove(e);
111
}
112![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
113![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseDown(MouseEventArgs e)
{
114![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (e.Button == MouseButtons.Left)
{
115
this.mousePosition = e.Location;
116
}
117![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
118
base.OnMouseDown(e);
119
}
120![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
121![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override CreateParams CreateParams
{
122![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{
123
CreateParams createParams = base.CreateParams;
124
createParams.ExStyle |= PInvokeService.WS_EX_LAYERED;
125
return createParams;
126
}
127
}
128![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
129![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseLeave(EventArgs e)
{
130
clockOpt.PreviewOpacity = clockOpt.Opacity;
131
windowShapeMaker.RefreshWindow(currentBitmap);
132![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
133
base.OnMouseLeave(e);
134
}
135![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
136![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnMouseEnter(EventArgs e)
{
137
clockOpt.PreviewOpacity = clockOpt.MouseEnterOpacity;
138
windowShapeMaker.RefreshWindow(currentBitmap);
139![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
140
base.OnMouseEnter(e);
141
}
142![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
143
// Press ESC to exit
144![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
145![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (msg.Msg == WM_KEYDOWN && keyData == Keys.Escape)
{
146
this.Close();
147
return true;
148
}
149![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
150
return base.ProcessCmdKey(ref msg, keyData);
151
}
152![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
153![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void OnClosed(EventArgs e)
{
154
base.OnClosed(e);
155
Core.Properties properties = this.CreateMemento();
156
PropertyService.Set("WinClock.MainForm", properties);
157
properties = clockOpt.CreateMemento();
158
PropertyService.Set("WinClock.ClockOption", properties);
159
}
160![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
161![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void SetupMenuItemElements()
{
162
mediator = new ContextMenuMediator(this);
163
new OptionElement(mediator, menuItemOption);
164
new RemindElement(mediator, menuItemRemind);
165
new CalendarElement(mediator, menuItemCalendar);
166
new AboutElement(mediator, menuItemAbout);
167
new PenetrateElement(mediator, menuItemPenetrate);
168
new UnmovableElement(mediator, menuItemUnmovable);
169
new TopMostElement(mediator, menuItemTopMost);
170
new MinimizeElement(mediator, menuItemMiniMize);
171
new ExitElement(mediator, menuItemExit);
172![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
173
// Additional function
174
new NotepadElement(mediator, menuItemNotePad);
175
new CalcElement(mediator, menuItemCalc);
176
new MediaPlayerElement(mediator, menuItemMediaPlayer);
177
new BackupElement(mediator, menuItemBackup);
178
new DirectXElement(mediator, menuItemDirectx);
179
new SystemDoctorElement(mediator, menuItemDoctor);
180![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
181
// Shutdown windows
182
new ShutdownElement(mediator, menuItemShutDown);
183![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
184
// Language
185
new LanguageElement(mediator, menuItemLanguage);
186
}
187![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
188![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void NotifyIconOnDoubleClick(object sender, EventArgs e)
{
189
this.Activate();
190
}
191![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
192![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void NotifyIconOnMouseDown(object sender, MouseEventArgs e)
{
193
// Right click to active the window
194![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (e.Button == MouseButtons.Right)
{
195
this.Activate();
196
}
197
}
198![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
199![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void NotifyIconOnMouseMove(object sender, MouseEventArgs e)
{
200
// Show the tooltip
201
string time = DateTime.Now.ToString(Core.RemindOperate.YearMonthDayTimeFmt);
202
string week = Core.RemindOperate.Days[DateTime.Now.DayOfWeek];
203![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (string.Equals("zh-CHS", clockOpt.Language))
{
204
string lunar = ChineaseDateService.GetLunarCalendar(DateTime.Now.Date);
205
notifyIcon.Text = string.Format("{1} {2}{0}农历{3}", Environment.NewLine, time, week, lunar);
206![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
} else
{
207
notifyIcon.Text = string.Format("{0} {1}", time, week);
208
}
209
}
210![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
211![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void TimerSecondOnTick(object sender, EventArgs e)
{
212
windowShapeMaker.RefreshWindow(currentBitmap);
213
}
214![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
215![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void TimerRemindOnTick(object sender, EventArgs e)
{
216
remindOperate.CheckRemindList();
217
}
218![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
219![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private void EnableDoubleBuffering()
{
220
this.SetStyle(ControlStyles.OptimizedDoubleBuffer &
221
ControlStyles.UserPaint &
222
ControlStyles.AllPaintingInWmPaint,
223
true);
224![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
225
this.UpdateStyles();
226
}
227
228![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
protected override void Dispose(bool disposing)
{
229![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (disposing)
{
230![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
IDisposable[] resourceList = new IDisposable[]
{
231
mediator,
232
currentBitmap,
233
windowShapeMaker,
234
components
235
};
236![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
foreach (IDisposable resource in resourceList)
{
237![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (resource != null)
{
238
resource.Dispose();
239
}
240
}
241
}
242
base.Dispose(disposing);
243
}
244![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
245![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
IMementoCapable Members#region IMementoCapable Members
246![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
247![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Core.Properties CreateMemento()
{
248
Core.Properties properties = new Properties();
249
properties.Set("Location", string.Format("{0}|{1}", this.Location.X, this.Location.Y));
250
properties.Set("TopMost", this.TopMost);
251![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
252
return properties;
253
}
254![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
255![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void SetMemento(Core.Properties properties)
{
256
string location = properties.Get("Location", "100,100");
257![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
string[] splitedLocation = location.Split(new char[]
{ '|' }, StringSplitOptions.RemoveEmptyEntries);
258![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (splitedLocation.Length == 2)
{
259
this.Location = new Point(int.Parse(splitedLocation[0]), int.Parse(splitedLocation[1]));
260
}
261
this.TopMost = properties.Get("TopMost", false);
262
}
263![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
264
#endregion
265
}
这里 Designer 生成的代码没有列出来,其中 SetupMenuItemElements 方法就是完成了实例化 Element 和 Mediator
具体类的作用, 这样他们都被 medator 引用,所以你不用担心它马上会被垃圾收集干掉。
参考资料:
关机对话框
李建忠-C#面向对象模式纵横谈第七讲-(行为型模式) Mediator 中介者模式