使用一个CWnd空闲池创建一个动态用户界面
2007-10-04 20:11:32 来源:WEB开发网介绍
本文提出了一组可以用来动态创建UI的类。该代码是集中于一个空闲池使用管理器的CWnd继承控件,该管理器可以帮助我们减少在特定UI场景中GDI资源的使用。为了在运行中演示这些类,我已经在此提供了一个MDI应用示例,它只是让你来打开XML文件。每个XML文件为单个MDI子窗体定义了布局和UI控件属性。尽管代码是用VC6写的,示例项目也可以被转换为VS 2003 和VS 2005项目。
UI场景
这里有两个普通的UI场景可能从空闲池的概念中受益。第一个例子是一个允许操作员来控制一些不同类型的远程设备的网络管理应用程序。每个设备有一组可以被几乎实时地读取或设置的参数。对于这种类型的应用程序有一个可能的UI模式是你的基本MDI框架允许你打开一个MDI子窗体以控制单个设备实例。因为每个设备可能有众多的(数以十计或甚至数以百计)参数,每个MDI子(或设备)窗体中的UI控件用如下图所示标签被组织为逻辑上的分组。
为每个设备类型实现UI的典型方法是为每个标签创建控件的一个不同的对话框或属性页。这个方法实现起来简单但是它不能很好地工作。考虑这样一个状况:你需要支持一个有着200个参数的设备类型。假定在一个设备窗体中每个标签可以为最多20个参数的控件提供的一个布局。因此,需要创建10个标签或对话框。现在,如果你认为每个参数也许需要配以它自我描述的文本标签,那么表示完整设备所必需的UI控件的数量可能会超过400个。另外,对于特定参数,UI控件可能并不是像你的基本CButton或CEdit那样简单。它也许可能是一个第三方测量的ActiveX控件(你必需在你的项目用到的),或一个类似于Windows Forms用户控件的聚集。因此,必需实现单个设备窗体的GDI资源耗费可能会很高并在操作员需要在同一时间打开很多这些设备窗体时变成一个限制的因素。
第二个例子是选项对话框(比如在VS2005中的“选项(Options)”对话框)。这一类型对话框的代表是在左手边包含一个树视图,右边是一组UI控件。每当树视图中的选择项改变,右手的那组控件就会动态改变。这个UI场景实际上与有着标签设备常窗体的第一个例子很相似。主要的不同之处是在选择和分组机制上(例如,树视图选择对应标签选择)。
CWnd空闲池
去除对不同的对话框或属性页的需要是减少标签设备窗体的资源需求的一个方法。可以通过只用一个对话框并实现一个机制,并由此依靠当前选择了哪一个标签决定UI控件被隐藏或显示。相同数量的UI控件需要被创建,但是我们以对话框所需要的数量保存这些控件。
如果我们认识到相同类型的UI控件常常在多个标签中被显示,那就可以在资源使用中获得更大节约。换句话说,不是只在标签选择改变时隐藏控件,我们可以在空闲池或cache中存储隐藏控件以便它们可以在转换到一个不同标签时被复用。这允许我们通过标签选择复用UI控件实例。举个例子,如果一个标签使用了一个CButton和另一个标签也使用一个CButton,为这两个标签它应该只需要创建一个CButton的实例并使用相同的UI实例。用此方法,每个设备窗体所需UI控件节约的数量会是相当大的。正如最佳案例场景的一个例子,考虑一个有者10个参数组(标签)和200个参数的设备,每个参数用一个trackbar(滑块)控件表示。如果我们也要用一个相应的文本标签控件为每个trackbar配对,这时就需要总数400个UI控件使用一个典型的多对话框实现。然而,如果我们从一个标签到另一个标签复用trackbar和label控件,设备窗体将需要至少20个trackbar和20个label控件,由此可以10倍减少资源的使用。
为了实现复用机制,我们首先通过定义一个只是保存空闲和可利用的CWnd实例track的CWndFreePool类。池中引用的每个CWnd配以一个字符串标识与UI控件的类型相应的CWnd。比如,"Button"类型字符串标识配对CWnd实际上是一个CButton实例,(以BS_PUSHBUTTON样式创建的)。除了MFC内建控件比如CButton,空闲池也可以引用ActiveX控件,因为Visual Studio可以为继承于CWnd的ActiveX控件生成MFC包装类。CWndFreePool类的public接口如下所示。
// CWndFreePool保持引用到已被创建但没有使用的(隐藏)的CWnds。
//该池包括仍在池中的CWnds的所有者并在其析构体删除它们中。
class CWndFreePool
{
public:
//构造器/析构器
CWndFreePool();
~CWndFreePool();
// Public 方法。
CWnd* GetWnd(const CString& strType);
voidAddWnd(const CString& strType, CWnd* pWnd);
};
控件类
为了复用一个UI控件实例,我们需要另一个在控件被返回到空闲池前保存控件状态,并且也要保存这个控件从池中被再次获得时状态的机制。为获得这样的机制,我们可以定义与一组MFC控件类支持的如CButton和CSliderCtrl相似的一层类。这层类的基类是CWndControl并且它的public接口如下所示以供参考。你可以认为这些CWndControl类是为它们的MFC副本而做的简单封装。
// CWndControl基类(抽象)
class CWndControl : public IWndEventHandler
{
public:
//构造器/析构器
CWndControl();
virtual ~CWndControl();
//类型字符串
const CString& GetTypeName() const;
//生成目标名称标识
const CString& GetName() const;
voidSetName(const CString& name);
//可见性
bool IsVisible() const;
void SetVisible(bool visible);
// Enabled状态
bool IsEnabled() const;
void SetEnabled(bool enabled);
// Read-only状态
bool IsReadOnly() const;
void SetReadOnly(bool readOnly);
//位置
const CPoint& GetLocation() const;
voidSetLocation(const CPoint& location);
// 尺寸大小
const CSize& GetSize() const;
voidSetSize(const CSize& size);
CRect GetRect() const;
// CWnd资源ID
UINT GetResourceId() const;
// CWnd装置
voidAttachWnd(CWnd* pWnd);
voidDetachWnd();
CWnd* GetAttachWnd();
// CFont装置
void AttachFont(CFont* pFont);
//事件
void EnableEvents(bool enable);
void SuspendEvents();
void RestoreEvents();
void AddEventHandler(IWndEventHandler* pEventHandler);
void RemoveEventHandler(IWndEventHandler* pEventHandler);
void RemoveAllEventHandlers();
//连接到其它CWndControl
void AddLinkedControl(CWndControl* pControl);
void RemoveLinkedControl(CWndControl* pControl);
void RemoveAllLinkedControls();
//纯虚方法
virtualbool CreateWnd(CWnd* pParentWnd, UINT resourceId) = 0;
virtualvoid UpdateWnd() = 0;
virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo) = 0;
// IWndEventHandler覆写
virtualvoid HandleWndEvent(const CWndEvent& ev);
};
可以只使用new操作符通过应用程序代码创建继承于CWndControl类的实例。然而,一个CWndFactory类已被提供来允许为CWndControl实例的创建而给定一个类型字符串。该工厂类主要被设计用来允许从XML清单中动态创建控件。 更多精彩
赞助商链接