关键字:自定义控件(Custom Control),C++,WIN32 SDK
本文发布的是我在工作中开发的自定义控件。第一个是多选控件,该控件主要启发来自于 ExplorerBar,即资源管理器左侧的 DirectDraw 部分,例如打开文件夹,位于左侧的那个可以扩展收缩的多面板组成的“文件夹任务”等。本控件的开发需求主要是用于在很多个Items中进行快速方便的选择和定位,因此我称它为多选控件。从外观上来看,它是由一些列面板从上到下的方式排列而成(我们把一个Group称为控件的一个Child,即第一层子结点的LineSize = 1),它和 Outlook 侧边栏的主要区别是,outlook侧栏是多个面板共享一个公用的较大客户区,即同一时刻通常仅可见一个面板(这一点类似TabControl),显然这个特征有时是我们需要的,但有时是我们不希望的。即 ExploerBar 和本控件则是多个面板同时可见的。比如说,TabPage上通常是按照功能分组归类到多个页面,这些页面彼此之间独立性较强,互相不依赖,因此在同一时刻只见到一个页面是比较符合用户意愿的。而我们要做的多选空间,他们都是本质上相同的,因此我希望随意组合式的多页面展示,这样可以给我们一个灵活又广阔的全局和局部视角组合,这种特点就是ExploreBar的特点。
因此总结下这个控件的诞生需求:是我们需要多选,多个面板保证灵活和快速定位的需求,即我们需要对Items进行一些分类并放入面板,可以任何折叠。使得提供灵活的视图。面板内部,由于Items的特征小而多,因此采用类似ListView的图标视图的做法,将他们从左到右进行布局。这样就可以比 TreeView 节省垂直方向的空间(因为Treeview每个节点都要占据一行),在这个控件里主要是各个元素之间都有边框,元素之间的间隔,边距等元素,这些元素显然会浪费一些空间。
本控件的组织架构是有顶层的代表窗口的一个类来管理的,下设Group集合。每个Group下面含有Items。每个Item为最基本单位,类似一个CheckBox。每个 Group 由Header和“客户区”组成。点击Header可以对其展开和折叠,同时左侧的按钮采用的是七帧图片得到旋转动画。截图如下所示:
另一个控件是时钟控件,该控件模仿的是 windows mobile 系统上的闹钟设置控件。主要用于设置时刻(例如闹钟),里面内置两个属性,Hour 和 minute,没有日期。因此这是它和 DateTimePicker 的主要区别。用户可以用鼠标拖动分针和时针,进行时刻的设置。时钟控件由于主要时为用户设置时间而提供,因此绘制上追求的原则是高效和对时间清楚的示意性,而不在于界面的美观和华丽,因此它在外观上显得“朴素”。控件内默认的分针拖动单位是5分钟(也可以设置为最小的 1 分钟分辨率,则拖动时的运动会更加平稳),指针捕捉误差缓冲为 3 分钟左右。
此两个控件都采用了内存绘制技术用于防止闪烁。
由于开发的比较紧急,因此尤其多选控件还缺乏足够的测试,可能有些接口方法还需要进一步改进,一些接口也有待于增加。具体用法由于精力所限,我就不在这里用代码做示范了,可以参考 Demo 项目的代码。在发行文件夹里有一个MulSelCtl.h 头文件,模仿windows sdk的做法我已经定义了一些对应的宏。因为时间原因,我还没有写出具体的说明文档。
最后增加一些技术总结:
(1)读写文本文件时,应该尽量避免使用unicode版本的函数(fgetws等),因为读写中文时可能乱码。因此本控件中先把 unicode 转换到ansi 再和文件进行交互。
(2)写文本文件时,应该把“\r\n"转换成单个”\n“,否则写入文本文件时,由于底层函数自作主张的翻译,会把\r\n写成\r\r\n。在文本框文本和文件之间交换数据时,这一点尤其需要注意。
(3)自定义控件要写成DLL被其他进程加载和使用,则ClassStyle中需要添加CS_GLOBAL样式,否则其他进程无法创建其实例。
(4)自定义控件由于可能被创建出多个实例,因此其窗口过程不能使用 Static 变量持有和窗口实例相关的内部数据(状态)。这一点和我们写普通窗口(主窗口,模态对话框)过程时不同,因为它们通常时进程范围内唯一的,因此可以使用 Static 变量持有窗口数据,这一点尤其要注意。正确做法时在注册窗口类时,告知系统为每个窗口保留其窗口数据额外空间的大小,在 WM_NCCREATE 时分配和填写数据。
Demo项目和控件发行包(*.h, *.lib, *.dll)的下载连接如下:
https://files.cnblogs.com/hoodlum1980/MulSelCtl_BIN.zip
【更新】
[1]. 修复多选控件中的一个BUG,点击 GroupHeader 上的 CheckBox 时,由于没有更新内部状态。会造成后续点击时,CheckBox 状态错误。2013-5-5;
[2]. DEMO项目对话框上,新增时钟控件的拖动单位设置(1或5分钟)。2013-5-5。