星期三, 九月 27, 2006

AOP, Signal/Slot, and Decoupling

解耦(Decoupling)是一个永恒的话题。本来没有打算这么早开始涉及“大型程序解耦”这一块内容,但是smithfox在winxcn论坛上提及相关的话题,所以决定还是在这里聊聊我对“解耦”的一些看法。

面向方面编程(AOP,Aspect Oriented Programming)思想的精粹,在于提倡人们尽量对功能进行切片,形成一个个独立的服务。而后,通过组合的方式,把这些服务组装成为所需要的组件。AOP的关注点在于复杂对象(或系统)的解耦(Decoupling)问题

信号-槽(Signal-Slot)机制,是希望提供一个统一的、可伸缩的方式,来规范组件之间的通讯机制。Signal-Slot的关注点在于组件间的解耦(Decoupling)问题。Delphi、C#、QT、SmartWin(Boost)均提供了Signal-Slot机制。另外,COM的ConnectPoint规范亦属于Signal-Slot范畴。

对于在SmartWin的中将消息(或称为“事件”)归类为一个个Aspect,并且采用Singal-Slot方式提供,你怎么看?WINX的消息机制为什么不采用类似SmartWin的Signal-Slot机制?这个问题可以从以下四个角度来回答。

其一:兼容。我已经说过,WINX的一个基调,是要让现有的MFC用户感到熟悉、感到Happy。所以,我不能够采取MFC用户比较陌生的Signal-Slot来进行消息处理。

其二:效率。SmartWin的消息机制无疑使得窗口对象的尺寸迅速膨胀,并且消息分派的效率大幅降低。

其三:Signal-Slot最主要的关注点是组件间的解耦(Decoupling)。一般情况下,我们主要将其用于两种对象(或多种对象)之间的消息通讯。Delphi、C#、QT在这一点上的度把握的相当好。而SmartWin将Signal-Slot机制应用于窗口自身内部的消息分派,让人有点“杀鸡用牛刀”之感。

其四:从AOP角度看。AOP的关注点是提供服务(功能切片),SmartWin只是将消息归为Aspect,并未提供服务,看起来这一个个Aspect主要是出于实现上重用的考虑,个人认为意义不大。真正AOP思想的贯彻者是ATL/WTL(当然,SmartWin既然支持了所有消息的Signal-Slot,自然也可以实现一个个的“功能切片”,尽管我还没有看到,但这可能是因为我不熟悉SmartWin的缘故)。ATL/WTL的消息分派中的MessageMap Chain机制,使得消息处理可以按功能切片进行分割,并最后可以完美的组装在一起。ATL/WTL中这样的“功能切片”太多了(有点吹牛了,其实不多:-),我们可以随意举几个例子:
  - WTL::CDialogResize (窗口布局,不只用于Dialog的Layout)
  - WTL::CDoubleBufferImpl (支持双缓冲Paint机制)
  - WTL::CThemeImpl (支持XP Theme)
  - ...

接下来我们谈谈WINX中大型程序的解耦(Decoupling)。我曾经在C++程序员的困惑一文中提到这个问题(不过这个问题不是C++程序员所特有的),并且把它作为WINX的一个要解决的目标。我在这方面做过尝试,并获得了一定的成果。但是很抱歉,它离我的期望还有一定的差距,关于这一部分的代码目前并未开放。

最后,我要附带对比一下各种的Signal-Slot实现。

Delphi、C#都是从语法角度来支持Signal-Slot机制,其性能、便利、友好程度,显然都到了最佳(Delphi为了效率,每个Signal只支持一个Slot)。QT虽然基于C++,但是其Signal-Slot机制也是从“半语法的角度”来提供(所以就有了moc预处理)。

根据我的猜想,SmartWin的作者正是觉得QT的做法不太纯洁,而试图提供一个标准C++的解决方案。但是,有两个原因让我觉得SmartWin(或者Boost)的Signal-Slot机制不好:

其一:Singal-Slot是一个通用的解耦机制。它将应用到各种层次的组件,而不会只是用于窗口消息处理。因此,Singal-Slot机制的简洁、易用是很重要的。这让我倾向于QT的Singal-Slot实现(只是相比SmartWin、Boost而言)。

其二:Signal-Slot既然关注于组件间的解耦(Decoupling),我个人倾向于它是一个二进制的规范,而不是C++ 模版定义的规范。原因很简单:我不想假设所有的组件实现者均喜欢C++。

.NET平台和Java平台最大区别在哪里?把你的焦点从C#与Java的比较上脱离开来吧。其实两者最大的区别在于,.NET平台推的是其二进制规范CLR(从COM二进制规范延伸),而Java平台推的是Java语言。微软是聪明的。呃,我把话题扯得远了。

星期一, 九月 25, 2006

新域名winxcn.com预告

http://winxcn.com

网站建设中,敬请关注。

星期日, 九月 24, 2006

WINX与ATL/WTL/MFC的关系,以及跨平台问题

说WINX基于ATL/WTL,其实不是准确的说法。实际上WINX的最核心组件(Windows、Dialog、Control)与WTL没有任何关系。只是WINX的句柄类(CWindow、Gdi句柄如CPen等)、资源类(CMenu等)是WTL的实现。

虽然说从重用角度来讲,WINX确实是:ATL -> WTL -> WINX。但对于WINX的性能是不需要担心的。WINX之所以基于WTL,完全是因为并不希望重复制造轮子。但是WINX的窗口机制是独特的,完全区别于现有各种界面库。对COM的连接点事件(ConnectPoint)更是比ATL好得多,使得C++可以如VB、C#一般方便地接收来自ActiveX控件的事件。

我在开发WINX的时候,参考了众多的界面库,如:MFC、ATL/WTL、QT、wxWidgets、SmartWin++,还有sourceforge上的win32gui、vgui等。

我所遇到的第一个问题,是兼容谁的问题。这里的兼容主要是指使用界面的兼容。这个问题不是谁说了算。看看google趋势:


可以看出,MFC、QT用户占了绝大多数。这就为WINX的开发建立了基调:尽量让MFC、QT用户感到熟悉、感到Happy。

另外,我发现一个值得注意的问题:几乎所有的库均有一个“不良倾向”,就是让自己包罗万象,给用户一个完整的解决方案 —— 网络、界面、自动化、XML、OLE等等。 特别是QT、wxWidgets,这种倾向及其明显。

为了明确我不是万能的,WINX不是万能的,在开发之初,我就给WINX一个规则:接纳现有的库,如果不能够提供得更好,就告诉(建议)用户用什么。如果某个问题有多个卓越的解决方案,那么,用户可以自由选择其中一个。

所以,在WINX中,库的关系是平行的:
 WindowSDK:Gdiplus (GDI+)、MSXML、等等
 Xerces-C
 DirectX
 ATL/WTL
 MFC
 STL
 Boost
 Loki
 ...
 WINX

而不是:
   /--- WindowSDK、Gdiplus、MSXML、DirectX
WINX ---- ATL/WTL
   \--- STL、Boost、Loki、Xerces-C

试图让自己成为用户唯一所见,这是一个危险的想法,我认为是这样。

举个例子,用户希望读取xml文件。那么用MSXML,还是用Xerces-C,还是expat?我不能确定用户想要什么。有一点可以肯定的是,WINX不会开发出另外一个XML Parser。如果WINX的代码需要一个XML Parser,我会尽量在一个比较抽象的层次,为以上三者提供一个共同的界面(当然,不是抽象所有的功能,只是WINX所需要的),就如我们抽象WINX_ASSERT一样。

我们回头看WINX_ASSERT这个例子。WINX中是这么实现的:

#if defined(ASSERT)
#define WINX_ASSERT(e) ASSERT(e)
#elif defined(_ASSERTE)
#define WINX_ASSERT(e) _ASSERTE(e)
#else
#ifdef _DEBUG
#define WINX_ASSERT(e) assert(e)
#else
#define WINX_ASSERT(e) 0
#endif
#endif

这意味着什么?ASSERT来自MFC,_ASSERTE来自MSCRT(参见crtdbg.h)、assert来自C标准库。这里没有检测ATLASSERT,是因为ATLASSERT就是_ASSERTE。

尽管实现ASSERT功能并不复杂(肯定远远简单于实现XML Parser),但是我决定还是不自己去做。原因很简单:我没有打算对它作出改进。

最后我们谈谈跨平台。其实我一直在尝试找到一个方案,可以把平台的差异屏蔽。然而,有一点让我感到不安,因为没有谁可以真的做到平台无关,无论我作出多少努力。我要在Linux平台(甚至更多的平台)提供提供Gdiplus(Mono在做这件事)?DirectX?COM?OLE?还是我去告诉用户,不要用Gdiplus,不要用OLE,不要用COM,喏,这里有一个跨平台的方案,你照着办吧。

我会尝试让WINX跨平台。当然我相信只是最核心部分值得这样做。问题的重心仍然在于,你无权阻止用户喜欢用Gdiplus来开发。所以,跨平台的方案,永远有很多种。

星期一, 九月 18, 2006

winx-1.1(stable version)发布

第一个winx-stable版本发布。

接下来一段时间内不会发布新的版本,主要以修改一些反馈的bug为主。winx发布虽然只有二十几天,但是在内部使用已经有较长时间,应该说功能、接口均相对比较稳定。事实上有更多功能是暂时屏蔽的。本着审慎发布一项功能的原则,那些接口需要进一步商榷,或者属于比较外围的功能,均暂时不对外发布。

下一阶段重点会补充一下文档。

星期四, 九月 14, 2006

感谢“无名”

WINX发布不到一个月,已经有一些热心的朋友在试用,并发现了一个重要bug。为了纪念,为此专门发布一个版本:

http://sourceforge.net/project/shownotes.php?group_id=174954&release_id=447445

感谢不相识的朋友。

星期三, 九月 13, 2006

005 - 窗体属性(Window Property)

摆脱丑陋的MessageMap,你不再需要进行消息映射。这是WINX带给你的第一份惊喜。

你的第二份惊喜,是一种全新(至少对MFC、WTL程序员如此)的编程体验:类Delphi的窗体属性编程。

先看几个例子:

// ---------------------------------------
// 设置对话框背景为灰色

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
WINX_BKGND_BRUSH(GRAY_BRUSH);
};

// ---------------------------------------
// 设置对话框背景为一幅位图

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
WINX_BKGND_PATTERN(IDB_BKGND);
// 这里IDB_BKGND是位图的资源ID
};

// ---------------------------------------
// 设置对话框快捷键

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
WINX_DLG_ACCEL();
WINX_ACCEL(IDR_ACCEL);

WINX_CMDS_BEGIN();
WINX_CMD(ID_HELP_ABOUT, OnCmdAbout);
WINX_CMDS_END();

public:
VOID OnCmdAbout(HWND hWnd)
{
winx::SimpleDialog dlg;
dlg.DoModal(hWnd, IDD_ABOUT);
}
};

我们看到,这种代码风格与MFC、WTL是十分不同的。我称之为基于窗体属性的编程风格。

首先需要指出的是,尽管这里以对话框作为例子,但是这些窗体属性是普适的,可以用于任何窗体。

只要一句WINX_ACCEL(IDR_ACCEL),搞定快捷键(WINX_DLG_ACCEL是打开快捷键功能。只需要顶层窗口调用,不是每个控件都需要。WINX_CMDXXX属于命令分派,与快捷键无关),这在WTL中非常难以办到。我们知道,WTL的快捷键机制是基于PreTranslateMessage的,而在WTL的模态对话框中根本没有PreTranslateMessage消息。所以,为了支持快捷键,你不得不改用非模态对话框。

关于窗体属性的概览性描述,请参考:

http://winxcn.blogspot.com/2006/09/002-winxproperty.html

星期一, 九月 11, 2006

004 - Hello, WINX! - 续

我们再来看看用MFC、WTL、WINX来实现的SDI窗口风格的最简单的Hello程序。

// -----------------------------------------
// MFC的Hello程序

class CMainFrame : public CFrameWnd
{
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;

cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(
CS_HREDRAWCS_VREDRAWCS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW),
HBRUSH(COLOR_WINDOW+1),
NULL);

return TRUE;
}

protected:
afx_msg void OnPaint()
{
CPaintDC dc(this);
dc.TextOut(1, 1, _T("Hello, MFC!"));
}
DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()

class CHelloMfc2App : public CWinApp
{
public:
virtual BOOL InitInstance()
{
CMainFrame* pFrame = new CMainFrame;
m_pMainWnd = pFrame;

pFrame->Create(NULL, _T("Hello"));
pFrame->ShowWindow(SW_SHOW);

return TRUE;
}
};

// -----------------------------------------
// WTL的Hello程序

class CHelloMainFrame : public CFrameWindowImpl<CHelloMainFrame>
{
public:
BEGIN_MSG_MAP(CHelloMainFrame)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
CHAIN_MSG_MAP(CFrameWindowImpl<CHelloMainFrame>)
END_MSG_MAP()

LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CPaintDC dc(m_hWnd);
dc.TextOut(1, 1, _T("Hello, WTL!"));
return 0;
}
};

WTL::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
_Module.Init(NULL, hInstance);
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);

CHelloMainFrame wndMain;
wndMain.Create(NULL, NULL, _T("Hello"));
wndMain.ShowWindow(nCmdShow);

theLoop.Run();
}
_Module.Term();
return 0;
}

// -----------------------------------------
// WINX的Hello程序

class CHelloMainFrame : public winx::MainFrame<CHelloMainFrame>
{
WINX_CLASS("CHelloMainFrame");
public:
void OnPaint(HWND hWnd)
{
winx::PaintDC dc(hWnd);
dc.TextOut(1, 1, _T("Hello, WINX!"));
}
};

winx::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
CAppModuleInit module;

CHelloMainFrame::RegisterClass();
CHelloMainFrame wndMain;
wndMain.Create(NULL, _T("Hello"));

return module.Run();
}
// -----------------------------------------

如果你了解Windows SDK编程,你肯定知道一个典型的Windows窗口程序(SDI),通常包含以下三个步骤:

1)注册窗口类。
2)创建窗口。
3)消息循环:分派并在窗口过程处理相应的消息。

我们看到,MFC把注册窗口类的过程隐含在PreCreateWindow,并尽量弱化窗口类概念。这表现在MFC的AfxRegisterWndClass甚至不允许用户主动指定窗口类的名字。WTL进一步弱化了窗口类概念,用户甚至根本就不需要知道RegisterClass(注册窗口类)这回事。

WINX采取了相反的策略,强调窗口类的概念,并认为用户理解窗口类是重要的。具体表现在:

1)用户需要用WINX_CLASS指定窗口类的名字。
2)用户需要在创建窗口类的第一份实例前,主动RegisterClass

这看似麻烦,但其实它是WINX强调可视化编程的关键,这一点我们在后续文章中将详细解释。

最让你惊异的,也许是WINX没有MessageMap。是的,WINX不需要你指定如何分派消息。并且,你将发现,几乎所有的MFC中的消息,WINX中均有对应,并且两者的消息处理函数原型极其相似,基本都是多了一个HWND hWnd参数。例如MFC的void OnPaint(),到WINX中为void OnPaint(HWND hWnd); MFC中的void OnLButtonDown(UINT uFlags, CPoint pt),到WINX为void OnLButtonDown(HWND hWnd, UINT uFlags, winx::CPoint pt); 等等。关于WINX中的消息分派机制,我们后续将进一步介绍。

最后需要提醒的是,WINX目前没有提供自己的消息循环,直接取自WTL。这个例子你看到WINX的WinMain代码比WTL简洁,但这只是假象。WINX只是对WTL的WinMain进行了一定的包装,没有实质性改进。WTL完全可以提供同样简洁的代码。

004 - Hello, WINX!

提到WINX,总是感觉有太多的背景需要交代,以致于到现在才迎来我们经典的第一课——Hello程序。

一个我们已经提到过的事实是:WTL的很多关键特性在模态对话框中不可用。例如PreTranslateMessage (包括快捷键的支持)、UpdateUI等等。而与WTL对模态对话框的“轻视”相反,WINX极大化的强化模态对话框的能力。为了突出这一点,我们的Hello程序首先从模态对话框开始:

// -----------------------------------------
// MFC的Hello程序

class CHelloMfcDlg : public CDialog
{
public:
enum { IDD = IDD_HELLOMFC_DIALOG };

CHelloMfcDlg(CWnd* pParent = NULL)
: CDialog(CHelloMfcDlg::IDD, pParent) {}
};

class CHelloMfcApp : public CWinApp
{
public:
BOOL InitInstance()
{
CHelloMfcDlg dlg;
dlg.DoModal();
return FALSE;
}
};

CHelloMfcApp theApp;

// -----------------------------------------
// WTL的Hello程序

class CHelloDlg : public ATL::CDialogImpl<CHelloDlg>
{
public:
enum { IDD = IDD_HELLO };

public:
BEGIN_MSG_MAP(CHelloDlg)
COMMAND_RANGE_HANDLER(IDOK, IDNO, OnCloseCmd)
END_MSG_MAP()

LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
::EndDialog(m_hWnd, wID);
return 0;
}
};

WTL::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
_Module.Init(NULL, hInstance);
{
CHelloDlg dlg;
dlg.DoModal();
}
_Module.Term();
return 0;
}

// -----------------------------------------
// WINX的Hello程序

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
};

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
CHelloDlg dlg;
dlg.DoModal();
return 0;
}
// -----------------------------------------

是的,对比三者模态对话框的样例,你至少可以看出两点:

1)MFC框架带给MFC模态对话框的,是累赘。WTL、WINX均不提供框架,你可以按自己的意愿写WinMain中的代码。

2)WTL的代码总是看起来显得笨拙(尽管高效)。而单看对话框代码,MFC、WINX看起来比较简洁,因为他们隐含已经处理了IDCANCEL、IDOK代码。

不过,由于只有WTL写了消息处理代码,其余两者均未处理消息,这个样例对WTL而言并不公平。让我们再来看看三者提供的最原始的SDI窗体。

关于这里的各个例子,你可以在WINX提供的tutorials中找到。它们分别是:

tutorials/winx/step001/hello,mfc
tutorials/winx/step001/hello,wtl
tutorials/winx/step001/hello,winx

003 - 对比WINX与MFC、WTL

尽管对MFC有这样那样的批评(这其中也包括我本人),但是MFC无疑是C++ GUI领域最成功的。WTL源于ATL,由于它的灵活、高效,而赢得了不少人的赞赏。然而要说WTL获得了成功,个人认为言之过早。看起来WTL似乎是“叫好不叫座”,并没有多少成功的产品是真正基于WTL进行GUI开发。

仔细分析一下,这样的结果并不奇怪。虽然MFC产生的代码笨拙,但是MFC的成功除了Microsoft大力推动之外,在于它生逢其时。它简陋的可视化开发界面,和自动代码生成,在当时基本上都是全手工界面开发的情况下,立即获得了青睐。而WTL恰恰是“生不逢时”,它出现于MFC大行其道之时,并且它的关注点在于“灵活,高效”,没有向导支持,所有代码手工完成,这无疑是一种倒退(MS可以强行开发基于WTL的可视化环境,但是WTL的特性并不太适合这样做,因为它“太灵活”了,这是它的优点,也是它的致命伤)。所以WTL仅仅获得少数有特殊需求的,对生成的代码尺寸非常注重的开发人员的推崇,但是Microsoft官方却弃之不顾(试为MS设想WTL可能的推广策略),其中道理不难理解。

WINX关注一个目标:简单而高效(MOST SIMPLE BUT EFFECTIVE)。WINX肯定了WTL技术上的先进性。故此WINX基于WTL,大量重用了WTL的代码。但是WINX除了高效外,更关注的是简单(SIMPLE)。

为了使得界面开发对程序员而言更为简单舒畅,WINX关注点不是在技术上,而是在对开发手段的革新上。WINX重用WTL只是一种手段。WINX重用的是它的实现,不是它的使用界面。

针对MFC的“长处”——半可视化开发,WINX引入了类似Delphi中的属性编程,使得多数常见的效果可以以窗体属性的方式进行表示。尽管目前尚且没没有专门针对WINX进行可视化界面开发的工具,但是它是WINX的发展方向。而且即使是在目前的简陋条件下,你仍然可以象MFC程序员一样,进行半可视的界面开发。

WTL脱离MFC程序员的习惯有点远了。这也是它让初学者畏惧的一个原因。WINX其实是更接近MFC的WTL:它提供了更接近于MFC的使用界面,并努力使得MFC代码可以更加容易地移植到WINX中。关于MFC代码移植,我们已经在WINX中提供了几个样例。有一点你需要理解的是,WINX并不十分关注将一个MFC应用程序移植到WINX下,因为那只有学术价值,但并不具备商业价值,我个人并不推荐你这样做。关于MFC移植,我的侧重点始终在于移植一些具备可重用性较高的MFC代码,例如MFC编写的控件等。

WINX可以取代MFC吗?对于这个问题我只能笑笑。我没有这个奢望。对于我而言,只是试图把我对C++程序员的几大困惑(内存管理、界面编程、自动化——Automation)的解决方案公诸于世,让更多人得益于它而已。从这个意义上来说,WINX只是侧重点在于提供界面开发的解决方案,但它不只是局限于界面库的范畴。

星期六, 九月 02, 2006

002 - WINX概览:高级特性

*) 进程间通讯(IPC)
 –拖放/复制粘贴
 –IDataObject
 –暂时屏蔽

*) 自动完成(AutoComplete)
 –Edit/ComboBox控件的自动完成
 –可保存历史到Windows注册表或者文件。

*) XP风格化
• Common Control
 –用InitCommControls()和manifest文件搞定。
• User Custom Control
 –#include "winx/Theme.h"
 –需要引入theme,自己绘制出XP风格的界面。
• Menu
 –用WINX_APP_MENU指定XP风格(建议选自动),或者用WINX_APP_LOOKNFEEL。
 –暂时屏蔽
• WebBrowser
 –ActiveX控件中,IE控件的XP风格化需要调用IE控件相关接口支持。

*) 让Win2000支持XP Style?
• 这个问题我这样看:既然用户选择了Windows 2000,说明他喜欢2000的精简风格,那么你又何必自作多情非要把你自己喜爱的XP风格强加给他?
• 风格一致性。

*) 自动化支持(Automation)
• #import
 –编译器的支持
• DispObject
 –Invoke 的包装
 –暂时屏蔽
• 接收连接点事件
 –DispEventBaseImpl
 –DispEventSimpleImpl
 –WINX_SINK_BEGIN
 –WINX_SINK_DEFI
 –WINX_SINK_END

002 - WINX概览:窗口行为(WindowBehavior)

*) 窗口行为
• 可以施加于任何窗口上的行为特征。
• WINX_BEHAVIOR
• 目前支持的行为列表
 –DefaultBehavior(无行为)
 –Moveless(不可移动)
 –WithinScreen(不可出屏幕)
 –LimitScreenTop/Left/Right
 –AutoHiddenTop/Left/Right(在上/左/右方自动隐藏)
 –AutoHiddenAnySide(自动隐藏)
 –FullScreenDockable(全屏停靠在窗口的左/右方)

*) 窗口行为的切换(Switch)
• BehaviorPermit
 –是否允许一个行为
 –即:在某种行为与DefaultBehavior之间切换
• BehaviorSwitch
 –在多种行为之间切换

002 - WINX概览:属性(Property)

*) 属性
• 基于属性的编程,容易向可视化发展。
• Delphi中优良的用户体验
• 区别于Delphi:如果某个特性没有使用,不会带来额外的代价。

*) 属性的命名规则
• 规则
 –WINX[_适用范围]_属性[_属性子类]
  • WINX_BKGND_PATTERN
   –其中,BKGND为属性,PATTERN为属性子类。
  • WINX_APP_ICON
   –其中,APP为适用范围,ICON为属性。
  • WINX_SYSICON
   –有时,为了减少宏名长度,在属性子类可以表意的情况下,可以省略属性。如:WINX_SYSICON,完整名应该称为WINX_ICON_SYSICON。
• 适用范围
 –WINX_XXX
  • 该属性所有窗口均适用
 –WINX_DLG_XXX
  • 该属性只在对话框类中使用。
 –WINX_APP_XXX
  • 该属性属于Application属性,应该在WinMain函数中使用。这类属性比较特殊,属于全局设置,作用于所有窗口。
 –......

*) WINX支持的属性列表
• 快捷键(Accel)
• 窗口图标(Icon)
• 窗口背景(Bkgnd)
• 窗口子控件统一背景(CtlBkgnd)
• 窗口Resize最小大小(MinInfo)
• 窗口布局(Layout)
• 窗口锚点(Anchor - todo)
• 应用程序图标(AppIcon)

*) WINX_ACCEL
• WINX_ACCEL
 –指定一个窗口的快捷键表
• WINX_DLG_ACCEL
 – 对话框及其子控件支持快捷键,默认不支持。
• TestTranslateAccel
 – 快捷键派发机制的修正。因为父窗口可能截获子窗口的快捷键。故此提供一个修正机会。

*) WINX_ICON
• 设置窗口的图标
 –WINX_ICON
 –WINX_ICONSM
 –WINX_SYSICON
 –WINX_SYSICONSM

*) WINX_BKGND
• 设置窗口背景
 –WINX_BKGND_COLOR
  例:WINX_BKGND_COLOR(COLOR_WINDOW);
 –WINX_BKGND_BRUSH
  例: WINX_BKGND_BRUSH(GRAY_BRUSH);
 –WINX_BKGND_PATTERN
  例:WINX_BKGND_PATTERN(IDB_BKGND);
  功能:以资源文件中的一幅位图作为窗口背景
 –WINX_BKGND_NULL
  功能:透明背景。

*) WINX_CTLBKGND
• 类似于WINX_BKGND,但是是统一设置某类型的子控件背景。
 –WINX_CTLBKGND_COLOR
 –WINX_CTLBKGND_BRUSH
 –WINX_CTLBKGND_PATTERN
 –WINX_CTLBKGND_NULL
 –WINX_CTLBKGND_NULL_ALL

*) WINX_MININFO
• 设置窗口的最小SIZE
 –WINX_MININFO
 –WINX_MININFO_PT
 –WINX_MININFO_DEFAULT

*) 设置窗口布局
• 提供以下方案:
 –WINX_DLGRESIZE_BEGIN
 –WINX_DLGRESIZE
 –WINX_DLGRESIZE_END
• 这是利用WTL 提供的DLGRESIZE_MAP 实现,使用上略有简化。

*) WINX_APP_ICON
• 设置应用程序图标。
• 这将使得所有有标题的弹出窗口自动设置了该图标(除非该窗口使用WINX_ICON自己设置了图标)。
• 包括MessageBox。

002 - WINX概览:消息(Message)

*) 消息分派(DispatchMessage)
• 消息分派机制是WINX的特色之一。
• 区别于WTL或者MFC,你不需要主动写一个消息分派的映射表。
• WINX易用性的第一体现。
• 高性能
 –WINX 为了实现消息分派使用了一个庞大的虚表?NO !
 –对比产生的汇编代码表明,WINX 的消息分派产生的代码比WTL 和MFC 更为短小精悍。

*) WINX和WTL消息分派的比较
• 各有特色
 –WTL:强灵活性,容易进行第三方的功能扩展。缺点是看起来比较丑陋。
 –WINX:强调易用性,并且实现了用户不需要某个特性时无额外开销。故此,系统可以不断的进行新特性的添加(但是缺点是不能以第三方的方式添加—— 注:当然你可以按WTL 的方式进行功能扩展,在WINX 中这是允许的,但与WINX 的设计理念有背)。

*) 消息分类
• 位置事件(MouseEvent)
• 焦点事件(Keyboard、Accel、Command)
 –目标的不确定性,是焦点事件(Focus Message)最显著特征。
• 绘制事件(Paint/NcPaint/EraseBkgnd等)
• 定时事件(Timer)
• 通知事件
 –子通知父
 –系统通知
 –广播

*) 命令分派(Command)
• 命令事件
 –WINX_CMDS_BEGIN[_EX]
 –WINX_CMD
 –WINX_CMD_EX
 –WINX_CMDS_END[_EX]
• ForwardCommand
 –WINX_CMDS_BEGIN_EX/END_EX 配对,区别与WINX_CMDS_BEGIN/END 的是,如果消息自身没有处理,会发给活动的子视图处理。

*) 命令状态(UpdateUI)
• WTL 的命令状态维护机制不错,但是ModalDialog 不完全支持该机制。主要的问题在于没有OnIdle 消息。
• WINX的UpdateUI基于WTL的命令状态维护机制实现,并作出改进。

*) 键盘与快捷键(Keyboard&Accel)
• 和命令一样,属于焦点事件,有事件的目标窗口不确定性。
 –考虑用况:整个应用程序支持Undo/Redo,但是程序某个时刻处于一个Edit控件中,此时希望Undo/Redo是Edit控件的文本,而不是应用程序一级的Undo/Redo。
• 解决方案:
 – 使用ForwardCommand
 – 使用TestTranslateAccel (见后文)

*) 通知消息分派(Notify)
• WINX_NOTIFY_BEGIN
• WINX_NOTIFY
• WINX_NOTIFY_END

*) 反射(Reflect)
• WINX_REFLECT
 –反射,即将子窗口发给父窗口的消息返回给子窗口自己处理。反射机制通常用于解耦。
• WINX_REFLECT_CMD
• WINX_REFLECT_CMDS_BEGIN
• WINX_REFLECT_CMD
• WINX_REFLECT_CMDS_END
• WINX_REFLECT_NOTIFY
• WINX_REFLECT_NOTIFY_BEGIN
• WINX_REFLECT_NOTIFY
• WINX_REFLECT_NOTIFY_END

*) 鼠标滚轮消息
• WM_MOUSEWHEEL
 –Note: SetFocus();

002 - WINX概览:窗口类(WindowClass)

*) 窗口类(WindowClass)的注册
• MFC、WTL弱化了窗口类的注册,与此相反,WINX强调窗口类概念,并要求用户主动注册窗口类。
 –表面上看,WINX的用法比MFC、WTL繁琐了(多了主动RegisterClass过程),但是深究下去,你将发现这恰恰是WINX推崇可视化界面开发的关键点。
• 窗口类注册相关
 –WINX_CLASS
 –WindowClass::RegisterClass
• 只有普通窗口、窗口超类(Superclass)需要注册。子类化窗口、普通对话框不需要注册。
 –RegisterClass 相关的例子:
  • 普通窗口
  • 超类(Superclass )
  • 对话框超类

*) 控件列表
• Static/Button/Edit/ComboBox
• ScrollBar/FlatScrollBar
• ListBox/DragListBox
• ListCtrl/HeaderCtrl/TreeCtrl
• ToolBarCtrl/ReBarCtrl
• SliderCtrl(即:TrackBarCtrl)
• SpinButtonCtrl(即:UpDownCtrl)
• TabCtrl/ToolTipCtrl/StatusBarCtrl/ProgressCtrl
• HotKeyCtrl/AnimateCtrl/IPAddressCtrl
• DateTimeCtrl/MonthCalCtrl
• ComboBoxEx/RichEdit
• LinkCtrl

*) 使用高版本特有控件
• SafeCtrl
 –这是一个特殊的窗口类。在以下用况使用:
  • 高版本Windows提供了一个控件(例如LinkCtrl)。我们希望在该版本的Windows版本使用它,而在低版本的Windows下,我们提供一个替换控件(AltCtrl)。
  • 我们使用了一个第三方控件。我们希望在用户安装了该控件时使用它,否则使用替换控件(AltCtrl)。
• 概念
 –OrgCtrl/OrgClassName
  • 高版本Windows提供(或第三方提供)的控件
 –AltCtrl/AltClassName
  • 替换控件。你需要实现的。
 –SafeCtrl/SafeClassName
  • 安全控件。WINX提供的。例如SafeLinkCtrl。

002 - WINX概览:句柄类(HandleClass)

*) 窗口句柄类
• WindowHandle - ATL::CWindow
• AxCtrlHandle - ATL::CAxWindow
 –ActiveX控件句柄类
• StaticHandle - WTL::CStatic
• ButtonHandle - WTL::CButton
• EditHandle - WTL::CEdit
• ComboBoxHandle - WTL::CComboBox
• ScrollBarHandle - WTL::CScrollBar
• FlatScrollBarHandle - WTL::CFlatScrollBar
• ListBoxHandle - WTL::CListBox
• DragListBoxHandle - WTL::CDragListBox
• ListCtrlHandle - WTL::CListViewCtrl
• HeaderCtrlHandle - WTL::CHeaderCtrl
• TreeCtrlHandle - WTL::CTreeViewCtrl
• ToolBarCtrlHandle - WTL::CToolBarCtrl
• TabCtrlHandle - WTL::CTabCtrl
• ToolTipCtrlHandle - WTL::CToolTipCtrl
• StatusBarCtrlHandle - WTL::CStatusBarCtrl
• SliderCtrlHandle/TrackBarCtrlHandle - WTL::CTrackBarCtrl
• SpinButtonCtrlHandle/UpDownCtrlHandle - WTL::CUpDownCtrl
• ProgressCtrlHandle - WTL::CProgressBarCtrl
• HotKeyCtrlHandle - WTL::CHotKeyCtrl
• AnimateCtrlHandle - WTL::CAnimateCtrl
• ReBarCtrlHandle - WTL::CReBarCtrl
• ComboBoxExHandle - WTL::CComboBoxEx
• DateTimeCtrlHandle - WTL::CDateTimePickerCtrl
• MonthCalCtrlHandle - WTL::CMonthCalendarCtrl
• IPAddressCtrlHandle - WTL::CIPAddressCtrl
• PagerCtrlHandle - WTL::CPagerCtrl
• RichEditHandle - WTL::CRichEditCtrl

*) 资源句柄类
• BitmapHandle - WTL::CBitmapHandle
• Bitmap - WTL::CBitmap
• IconHandle - WTL::CIconHandle
• Icon - WTL::CIcon
• CursorHandle - WTL::CCursorHandle
• Cursor - WTL::CCursor
• MenuHandle - WTL::CMenuHandle
• Menu - WTL::CMenu
• AcceleratorHandle - WTL::CAcceleratorHandle
• Accelerator - WTL::CAccelerator

*) GDI句柄类
• RgnHandle - WTL::CRgnHandle
• Rgn - WTL::CRgn
• PenHandle - WTL::CPenHandle
• Pen - WTL::CPen
• BrushHandle - WTL::CBrushHandle
• Brush - WTL::CBrush
• FontHandle - WTL::CFontHandle
• Font - WTL::CFont
• PaletteHandle - WTL::CPaletteHandle
• Palette - WTL::CPalette
• EnhMetaFileHandle - WTL::CEnhMetaFileHandle
• EnhMetaFile - WTL::CEnhMetaFile

*) GDI句柄类 – DC
• DCHandle - WTL::CDCHandle
• ClientDC - WTL::CClientDC
• WindowDC - WTL::CWindowDC
• PaintDC - WTL::CPaintDC
• MemoryDC - WTL::CMemoryDC
• EnhMetaFileDC - WTL::CEnhMetaFileDC

*) 其他句柄类
• ImageListHandle - WTL::CImageList

002 - WINX概览:基础支撑

*) 基础支撑的实现原则
• 不假设自己是用户独立使用的库。
• 所以尽量检测用户是否使用了某种流行的库,选择该库已有的。
• 举例:WINX_ASSERT
  #if defined(ASSERT)
  #define WINX_ASSERT(e)ASSERT(e)
  #elif defined(_ASSERTE)
  #define WINX_ASSERT(e)_ASSERTE(e)
  #else
  #ifdef _DEBUG
  #define WINX_ASSERT(e)assert(e)
  #else
  #define WINX_ASSERT(e)0
  #endif
  #endif

*) 不完全列表
• 基础类,多数来自WTL
 –CPoint
 –CRect
 –CSize
 –CString
 –CFindFile
 –CRecentDocumentList
 –WindowRect
 –ClientRect
• 通用
 –_offsetof、parent_class_ptr、countof
 –MsgBox
 –WINX_DEFINE_IID、WINX_UUID
• 调试/诊断
 –WINX_ASSERT
 –WINX_ASSERT_OK
 –WINX_ASSERT_ONCE
 –WINX_ASSERT_DERIVE
 –WINX_REPORT
 –WINX_VERIFY
 –WINX_VERIFY_OK
 –WINX_TRACE

002 - WINX概览:WINX中的基本概念

• 基础支撑
 –基础类(BasicTypes),多数来自WTL
 –辅助:调试诊断等
• 句柄类(HandleClass)
 –Win32 SDK中的句柄的简单包装
 –WINX的所有句柄类直接取自ATL/WTL
• 窗口类(WindowClass)
 –消息(Message)的接收者
 –窗口类不一定要保存有窗口句柄(!!!)
• 消息(WindowMessage)
 –消息分派(DispatchMessage)
• 属性(Property)
 –属性编程(类Delphi),易往可视化发展

002 - WINX概览:WINX的目标

• 轻巧、易用、“傻瓜式”
• 属性编程(类Delphi)
• 可视化
 – 事实:WTL 的很多关键特性在模态对话框中不可用。例如PreTranslateMessage (包括快捷键的支持)、UpdateUI 等等。
 – 与WTL 对模态对话框的“ 轻视” 相反,WINX 极大化的强化模态对话框的能力。从而使得多数的界面设计可以以可视化方式完成。