自画 CComboBox 控件注意事项

来源:互联网 发布:广州淘宝摄影基地 编辑:程序博客网 时间:2024/06/05 16:24
 Office2000系列软件(Software)地工具栏顶都有1个字体选择组合框,它有三大特色,1就为具有扁平及鼠标热点地效果,二就为字体地样式在下拉列表框中直接可见,即用相应地字体来绘制字体名称,三就为将最近使用过地字体列在下拉列表框地顶端,方便下次使用。   
  那么,俺们如何在程序软件代码中实现写出来此种功能呢?   
  Windows地基本程序软件控件,如列表框、组合框及按钮(Button)等,可以由系统项目来绘制,也可以由用户程序软件代码自己负责绘制,要自己绘制时,必须为程序软件控件指定OWNERDRAW风格,通过此个手段,俺们可以绘制出组合框地下拉列表中地字体效果;然后,通过接管WM_PAINT消息,可以为组合框绘绘制出扁平及鼠标热点效果来。   
  要自画组合框,首先要为组合框指定CBS_OWNERDRAWVARIABLE风格或CBS_OWNERDRAWFIXED风格。区别就为前者可以为非同地列表项指定非同地高度,而后者则用于所有地列表项目都具有相同地高度地情况。使用MFC实现写出来自画组合框要从CComboBox类派生出1个新类,再为它重载三个虚函数function:CompareItem、DrawItem及MeasureItem。其中,CompareItem用于在排序时比较两个列表项目地先后顺序,DrawItem负责每1个列表项地绘制工作,MeasureItem则为每个列表项目指定非同地高度。只要就为自画地组合框,就1定要重载此三个函数function(用非到地函数function,函数function体可以为空),否则会出现ASSERT对话框,原因是在基类CComboxBox地虚函数function实现写出来编程代码(Code)中包含呢1行ASSERT(FALSE);。此三个函数function要用到三个结构,分别介绍如下。   
  COMPAREITEMSTRUCT结构地定义设置在winuser.h地2317行   
  typedef   struct   tagCOMPAREITEMSTRUCT   {   //cis   
          UINT                 CtlType;   //程序软件控件地类型   
          UINT                 CtlID;   //程序软件控件地ID   
          HWND                 hwndItem;   //窗口句柄   
          UINT                 itemID1;   //要比较地第1个项目地ID   
          DWORD               itemData1;   //要比较地第1个项目地32位用户指定地关联值   
          UINT                 itemID2;   //要比较地第二个项目地ID   
          DWORD               itemData2;   //要比较地第二个项目地32位用户指定地关联值   
          DWORD               dwLocaleId;   //   
  }   COMPAREITEMSTRUCT,   NEAR   *PCOMPAREITEMSTRUCT,   FAR   *LPCOMPAREITEMSTRUCT;   
  在CompareItem函数function中,1般就为通过所给出地两个要比较项目地ID或就为关联值,查找自己地数据结构,再返回非同地值来表明两个项目地先后顺序。返回-1表明第1个项目排列在第二个项目之前,返回0表明两个项目无法分出先后顺序,返回1表明第1个项目排在第二个项目之后。   
  DRAWITEMSTRUCT结构地定义设置如下(winuser.h地第2291行)   
  typedef   struct   tagDRAWITEMSTRUCT   {   //dis   
          UINT                 CtlType;   //程序软件控件地类型   
          UINT                 CtlID;   //程序软件控件地ID   
          UINT                 itemID;   //项目地ID,在组合框中就为序号   
          UINT                 itemAction;   //要地动作,可为全体地、有焦点地或就为已选择地   
          UINT                 itemState;   //项目地状态,可为选择地、禁止地、有焦点地等   
          HWND                 hwndItem;   //窗口句柄(菜单句柄)   
          HDC                   hDC;   //设备顶下文   
          RECT                 rcItem;   //项目地矩形区   
          DWORD               itemData;   //项目地1个32位地用户指定地关联值   
  }   DRAWITEMSTRUCT,   NEAR   *PDRAWITEMSTRUCT,   FAR   *LPDRAWITEMSTRUCT;   
  1般在程序软件代码中要根据itemState判断项目所处地状态,然后再使用hDC来绘图。itemState地可用值有ODS_SELECTED、ODS_GRAYED、ODS_DISABLED、ODS_CHECKED、ODS_FOCUS、ODS_DEFAULT、ODS_COMBOBOXEDIT、ODS_HOTLIGHT、ODS_INACTIVE,后两个值仅用于Windows98及Windows2000。   
  MEASUREITEMSTRUCT结构则就为在winuser.h地2278行定义设置地。   
  typedef   struct   tagMEASUREITEMSTRUCT   {   //mis   
          UINT               CtlType;   //程序软件控件地类型   
          UINT               CtlID;   //程序软件控件地ID   
          UINT               itemID;   //项目地ID,在组合框中就为序号   
          UINT               itemWidth;   //项目地宽度   
          UINT               itemHeight;   //项目地高度   
          DWORD             itemData;   //项目地1个32位地用户指定地关联值   
  }   MEASUREITEMSTRUCT,   NEAR   *PMEASUREITEMSTRUCT,   FAR   *LPMEASUREITEMSTRUCT;   
  在程序软件代码中1般要通过itemID或就为itemData来在自定义设置地数据结构中查找数据,然后计算并返回宽度与高度值,在函数function中,可以为非同地项目指定相同或非同地高度。   
  为呢实现写出来使用相应地字体来绘制字体名称地效果,俺们可以在DrawItem函数function中创建所要地字体对象,将它选入设备顶下文,并且用它来绘图,此样,每种字体地名称就可以用本身地字体来绘制呢。   
  为组合框实现写出来扁平及鼠标热点效果并非难,有多种办法可以实现写出来。最通用地办法就为编制1个类,通过安装钩子函数function来跟踪每1个程序软件控件地创建,然后替换掉并保存下它地窗口过程,此样,就可以在新地窗口过程中对非同地消息分别进行处理(当然也就可以处理WM_PAINT等消息并绘图),而且原因是原先地窗口过程已经保存下来,所以并非会损失任何原先程序软件控件地功能,只非过此样做要编制大量地编程代码(Code)。在本例中,为呢简单起见,直接在CComboBox地派生类中处理WM_PAINT等消息,根据非同地情况绘制出扁平效果,而鼠标热点则依赖于窗口定时器地使用。对于如何绘制扁平效果地程序软件控件已经讨论得很多呢,非再详述。下面将如何取得系统项目中所有已安装地字体地信息地方法function作1介绍。   
  要枚举系统项目中所有可用地字体,可以通过EnumFontFamiliesEx函数function来实现写出来。另1个可以实现写出来相同功能地EnumFontFamilies函数function就为为呢与16位地程序软件代码兼容才保留地,非再推荐使用。EnumFontFamiliesEx函数function地原型如下:   
  int   EnumFontFamiliesEx(   
      HDC   hdc,                             //   设备顶下文,由它来决定就为屏幕字体或就为打印机字体   
      LPLOGFONT   lpLogfont,     //   1个用于提供信息地LOGFONT结构   
      FONTENUMPROC   lpEnumFontFamExProc,   //   回调函数function   
      LPARAM   lParam,                 //   送往回调函数function地用户指定地指针   
      DWORD   dwFlags                   //   保留参数,必须为0   
  );   
  其实,与其它地Windows   API1样,假如去查它们地原始定义设置,就为有-A及-W地区别地,-A用于ANSI及MBCS字符集,-W用于Unicode字符集。但就为1般情况下可以非严格区分二者,UNICODE标志已经很好地处理呢此个问题和疑问。   
  lpLogFont参数只有三个成员对于EnumFontFamiliesEx函数function就为有效地,它们就为   
  lfCharset:假如设为DEFAULT_CHARSET会枚举所有地字体,假如指定为GB2312_CHARSET、ANSI_CHARSET等值,则只会枚举指定地字符集下地字体。   
  LfFaceName:枚举非同字符集下地同名字体   
  LfPitchAndFamily:除去希伯来语及阿拉伯语地操作系统项目都就当设置为0   
  当EnumFontFamiliesEx函数function被用之后,它将会对每1种字体调用回调函数function1次,在回调函数function中要保持此个动作要返回1,否则返回0值退出回调过程。回调函数function地原型就为:   
  int   CALLBACK   EnumFontFamExProc(   
      ENUMLOGFONTEX   *lpelfe,//   包含呢1个LOGFONT结构及1些可读性地信息   
      NEWTEXTMETRICEX   *lpntme,//     
      int   FontType,//   字体地类型   
      LPARAM   lParam//   由EnumFontFamilies函数function传来地参数   
  );   
  lpntme成员对于true   type字体,就为1个NEWTEXTMETRICEX结构,对于其它字体,则就为1个NEWTEXTMETRICEX结构。   
  字体类型地可用值为DEVICE_FONTTYPE、RASTER_FONTTYPE或者TRUETYPE_FONTTYPE,分别对应设备字体、光栅字体及true   type字体。   
  在回调函数function中使用lpelfe->elfLogFont.lfFaceName就可以得到字体地名称。   
  下面让俺们来实现写出来此种组合框。   
  创建1个对话框工程,添加1个从CComboBox类派生出地新类,名为CFontComboBox,重载CompareItem、DrawItem及MeasureItem等函数function,最后得到如下地文件。   
  对话框图资源中地组合框要具有CBS_OWNERDRAWVARIABLE、CBS_SORT及CBS_HASSTRINGS风格。   
  为呢速度顶地考虑,使用映射(Hash表)来保存所有地字体名称信息。共有两个映射,1个用于所有地字体名称,1个用于最近使用过地字体名称,最近使用过地字体名称位于组合框地下拉列表地最顶端。   
  所有地字体在下拉列表中均有1幅位图位于其左侧。在最近使用过地字体与普通地字体列表之间有1道双线间隔开。对于符号字体,原因是使用字体本身来绘制字体名称会导致无法辩认出字体名,所以使用默认地字体(父对话框地字体)在先绘制1遍该字体地名字,再用该字体绘制字体名字。   
  下拉列表地宽度与列表项目地最大宽度相同。   
  下面就为CFontComboBox类地文件列表