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类地文件列表
那么,俺们如何在程序软件代码中实现写出来此种功能呢?
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类地文件列表