在Windows的UI开发中,我们经常需要对窗口和控件进行自绘,以实现独特的UI风格,满足软件的个性化需求,提升用户的视觉体验。目前市面上流行的软件,大都有自己别具一格的界面,许多软件还提供多种界面供用户选择,我们称之为skin。应用程序的类型,按M$通俗粗略的划分,可以分为:对话框程序、单文档程序(SDI)、多文档程序(MDI)。在换肤功能的开发中,对话框程序是最简单的,单文档和多文档程序是比较头痛的。因为,我们会遇到诸如菜单栏、工具栏的换肤问题(对话框也可能有菜单栏、工具栏,只要有这些,就是比较麻烦的事情。)。 这里,我们先讨论一下关于菜单栏换肤时遇到的一个问题。
我们通常用自制的菜单栏控件替代系统的菜单栏来解决换肤的问题。大致的方案是,新建一个窗口用作菜单栏控件,在该窗口上绘制出菜单栏的外观,处理WM_PAINT、WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP和WM_MOUSELEAVE消息,鼠标移动时命中顶层菜单并使之高亮,鼠标按下时使用TrackPopupMenuEx函数弹出被命中顶层菜单的子菜单。
由于是自绘控件,换肤变得极其容易,想怎么绘制就怎么绘制,为换肤提供极大的灵活性。可是,现在却能遇到了一个难题。TrackPopupMenuEx函数弹出子菜单后不会立即返回,相当于弹出的子菜单是一个模态窗口。而事实上,子菜单的确是一个窗口。通过SetWindowsHookEx设置WH_CALLWNDPROC方式的hook可以hook到菜单是一个类名为”#32768″的窗口。菜单的这种特性导致了我们的自绘控件与系统的菜单栏行为不一致!在系统的菜单栏上,我们在一个顶层