CFontListBox






4.92/5 (24投票s)
2006年1月20日
9分钟阅读

96567

3693
一个列表框,可以显示和选择字体,并具有特殊功能。
Content
引言
首先,对于我英语和本文内容中的任何错误,我深表歉意(这是我的第一次投稿)。
为了一个个人项目(一个PropertyList
控件),我需要一个ListBox
,它可以显示和选择字体。阅读了关于这个主题的几篇文章后,我决定编写自己的控件,具有以下主要行为:
- 字体类型检测时的性能(当我开始这项工作时,我只有一台 333 MHz 的奔腾电脑!),
- 可以显示字体名称为标准文本或使用字体本身,
- 可以使用样本文本或字体名称显示工具提示(跟踪鼠标指针或选定项目),
- 具有最近使用(MRU)子列表,可由应用程序中的多个
CFontListBox
实例共享,或不共享, - 项目不依赖于位图或
ImageList
资源文件, - 可以作为对话框子类化控件使用,也可以手动创建。
背景
要管理一个最近使用(MRU)子列表(共享或不共享),CFontListBox
使用CFontListManager
实例提供的服务。应用程序初始化期间只自动创建一个CFontListManager
实例。该类负责加载字体列表,通过CFontDescriptor
和CFontMruList
对象及其各种集合来管理最近使用列表(私有于CFontListBox
实例,在两个或多个控件之间共享,或全局于应用程序)。
该控件从COMDLG32.DLL的位图资源中获取用于显示列表的位图,因此您的项目中不需要包含特定的资源。
为了允许加载字体列表的过程,该控件不使用其他控件使用的方式。我试图区分字体类型(TrueType、OpenType 等)。它使用::EnumFontFamiliesEx
API 传递给回调函数的标志(参见FontListManager.cpp中的EnumFontProc
和CFontListManager::FillFontList()
函数)。
需要重新创建基础列表框窗口,以便在创建后修改控件,以遵循CFontListBox
样式标志的修改。
最近使用列表的持久化需要在CWinApp::InitInstance
和CWinApp::ExitInstance
方法中调用两个函数。这是因为当创建一个CFontListManager
实例时,没有调用CWinApp::SetCurrentHandles()
(MFC 库的这个函数负责初始化CWinApp
对象的成员变量,如m_pszRegistryKey
和m_pszProfileName
)。持久化仅对全局最近使用列表和自定义命名列表生效,前提是这些列表被标记为这样做。
CFontListManager
有一个功能,可以响应系统广播的消息WM_FONTCHANGE
。此功能需要一个顶级窗口来接收该消息,该窗口在首次创建CFontListBox
时创建(这只能在CWinApp
对象和其他框架或对话框窗口初始化后发生)。
使用代码
与CFontListBox
一起使用的所有类都位于一个静态库(FontLBLibxx.lib)中。库项目目录树有一个特殊的结构,因为我(为了其他项目)需要一个包含头文件和 lib 文件的唯一位置。
注意:如果您的列表框没有设置LBS_NOINTEGRALHEIGHT
样式标志,并且多次更改项目的高度(例如,添加,然后删除最近使用列表),您会看到列表框缩小:这是设计使然!
列表样式标志
FNTLB_GRAPHIC
:列表项使用相应的字体显示。FNTLB_ITEMHEIGHTEXPANDED
:间距与 ComboBox 列表框相同。FNTLB_NOTYPEIMAGE
:在字体名称之前不显示字体图像类型。FNTLB_MANUAL
:未初始化的字体列表框(下文讨论)。
它们通过SetFlags
或ModifyFlags
函数,或者通过构造函数CFontListBox(DWORD dwStyleFlags, LPCTSTR lpszMruListName=NULL)
设置或修改。
当设置FNTLB_GRAPHIC
标志并且字体是 SYMBOL 时,项文本也包含使用 GUI 系统字体显示的字体名称(就像在 Office 2003 字体 ComboBox 中一样)。
请注意,并非所有符号字体都标记为 SYMBOL,您可以在 Office 2003 字体 ComboBox 或演示程序中看到这一点。
FNTLB_ITEMHEIGHTEXPANDED
标志提供了与 ComboBox 中相同的项间距。如果未设置此标志且未附加 MRU 列表,则间距将与 Win32 FontDialog
相同。
如果设置了列表样式标志FNTLB_MANUAL
,则列表框不会使用系统字体资源列表进行初始化。这种类型的CFontListBox
旨在接收和显示通过其他方式选择的字体列表(请参阅演示)。所有 MRU 标志都自动禁用此类型的窗口。
工具提示样式标志
影响工具提示窗口的CFontListBox
样式标志如下:
FNTLB_HAS_TOOLTIPS
:控件有工具提示。FNTLB_TOOLTIPTEXT_FONT
:工具提示文本是字体名称。FNTLB_TOOLTIPTEXT_SAMPLE
:工具提示文本是样本文本(默认)。FNTLB_TRACKING_TOOLTIP
:工具提示跟随鼠标指针,并显示鼠标指针下方字体项的显示文本。FNTLB_TOOLTIPSELECTED
:工具提示还显示选定字体列表。
它们通过SetFlags
或ModifyFlags
函数,或者通过构造函数CFontListBox(DWORD dwStyleFlags, LPCTSTR lpszMruListName=NULL)
设置或修改。
请注意,如果设置了FNTLB_GRAPHIC
,工具提示会使用 GUI 系统字体显示字体名称(或样本文本);如果未设置,则工具提示会使用相应字体显示文本。
如果未设置FNTLB_TRACKING_TOOLTIP
,则仅当有一个或多个选定项目时才会显示工具提示。
如果设置了FNTLB_TRACKING_TOOLTIP
,工具提示会显示鼠标指针下方的字体名称;如果还设置了FNTLB_TOOLTIPSELECTED
标志,它还会显示选定字体的列表。
最近使用列表类型标志
您可以使用样式标志将 MRU 列表分配给您的CFontListBox
实例:
FNTLB_MRULIST
:私有的 MRU 列表。FNTLB_MRUGLOBALLIST
:应用程序级别的 MRU 列表。FNTLB_MRUCUSTOMLIST
:由两个或多个控件共享的命名 MRU 列表。
私有 MRU 列表
私有 MRU 列表是局部于控件的:对其 MRU 列表所做的修改不会反映在其他控件上。
自定义 MRU 列表
自定义 MRU 列表旨在供应用程序中的多个控件共享。它有一个名称(一个字符串)来标识它,并可用于引用它。它在第一个控件引用它时创建,并且在没有控件附加到它时不会被销毁(此功能将在即将推出的CFontComboBox
中使用...)。通过一个控件所做的所有修改都会反映在附加到 MRU 列表的其他控件上。
全局 MRU 列表
全局 MRU 列表是一个特殊的自定义 MRU 列表,在应用程序运行时有效。
将控件附加到 MRU 列表
您可以使用AttachToMruFontList
或SetMruFontListName
(它们是同义词)函数将 MRU 列表附加到您的控件,如下所示:
// Attach the control to a private MRU list m_listFont1.AttachToMruFontList(NULL); // Attach the control to a custom MRU list m_listFont2.AttachToMruFontList(_T("MruTest")); // Attach the control to the global MRU list m_listFont3.AttachToMruFontList(GLOBAL_MRULIST);
DetachFromMruFontList()
函数允许您将控件从先前附加的任何类型的 MRU 列表中分离出来。您无需使用此函数将先前已附加的控件附加到另一个 MRU 列表。
您也可以这样做:
// Attach the control to a private MRU list m_listFont1.ModifyFlags(FNTLB_MRUGLOBALLIST | FNTLB_MRUCUSTOMLIST, FNTLB_MRULIST); // or this : m_listFont1.SetMruFontListName(NULL); // Attach the control to a custom MRU list m_listFont2.SetMruFontListName(_T("MruTest2)); m_listFont2.ModifyFlags(FNTLB_MRULIST | FNTLB_MRUGLOBALLIST, FNTLB_MRUCUSTOMLIST); // or, more simple, this : m_listFont2.SetMruFontListName(_T("MruTest2)); // Attach the control to the global MRU list m_listFont3.ModifyFlags(FNTLB_MRULIST | FNTLB_MRUCUSTOMLIST, FNTLB_MRUGLOBALLIST); // or this : m_listFont3.SetMruFontListName(GLOBAL_MRULIST);
在 CDialog 中将 CFontListBox 用作自动子类化控件
- 步骤 1:使用资源编辑器在您的对话框资源中添加一个
ListBox
控件。指定您想要的样式(??选择模式)。 - 步骤 2:如果尚未创建,请创建与您的对话框资源相对应的
CDialog
派生类。 - 步骤 3:将FontListBox.h添加到您的对话框类头文件中。
- 步骤 4:通过 ClassWizard,添加一个类型为
CFontListBox
的成员控件变量。 - 步骤 5:在(您的对话框实现文件中)修改:
//////////////////////////////////////////////////// // CPpManual property page IMPLEMENT_DYNCREATE(CPpManual, CPropertyPage) CPpManual::CPpManual() : CPropertyPage(CPpManual::IDD) { ...
如下,添加或修改
CFontListBox
样式标志(其中m_listFont1
是之前添加的成员变量,"MruTest
"是一个共享 MRU 字体列表的名称)://////////////////////////////////////////////////// // CPpManual property page IMPLEMENT_DYNCREATE(CPpManual, CPropertyPage) CPpManual::CPpManual() : CPropertyPage(CPpManual::IDD), m_listFont1(FNTLB_HAS_TOOLTIPS | FNTLB_TOOLTIPTEXT_FONT | FNTLB_TRACKING_TOOLTIP | FNTLB_MRUCUSTOMLIST, _T("MruTest")) { ...
- 步骤 6:添加:
m_listFont1.Initialize();
在您的对话框类的
OnInitDialog
函数中。
在 CDialog 中将 CFontListBox 用作手动子类化控件
遵循上述步骤 1 到 3。然后,将一个CFontListBox
变量(或一个CFontListBox*
变量,根据需要)添加到您的CDialog
派生类中。
如果需要,创建对象并使用SubClassDlgItem
。不要忘记调用CFontListBox
的Initialize()
函数。
动态创建 CFontListBox 控件
请参阅示例中的CPpManual::OnInitDialog()
函数,了解如何动态添加和创建CFontListBox
控件。
CFontListBox 类
成员函数
构造函数
CFontListBox(); CFontListBox(DWORD dwStyleFlags, LPCTSTR lpszMruListName=NULL); // (see above, in "Using the code"). void Initialize(); // Always needed after the window // creation to configurate // and fill in the listbox.
样式标志和样式
DWORD SetFlags(DWORD dwFlags); DWORD GetFlags(); BOOL ModifyFlags(DWORD dwRemove, DWORD dwAdd); BOOL ModifyStyle( DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0 ); // Same as the CWnd similar functions, // but needed here to recreate the listbox.
图像
BOOL SetImages(CBitmap* pBitmap); BOOL LoadImages(LPCTSTR lpszResourceName); BOOL LoadImages(UINT nIDResource); // Allows the control to use // a different bitmap image set // for the font types. void SetStdImages(); // Reset the default bitmap // (extract from Comdlg32.dll).
可重写的过滤器函数
virtual BOOL OnAddItem(CFontDescriptor* lpFont); // return TRUE (default) // if the font can be added to the list virtual BOOL OnAddMruItem(CFontDescriptor* lpFont); // by default, return the result of OnAddItem // If these functions returns FALSE, // the item is not added to the listbox.
MRU 相关函数
BOOL AttachToMruFontList(LPCTSTR lpszMruListName); BOOL SetMruFontListName(LPCTSTR lpszMruListName); BOOL DetachFromMruFontList(); int AddFontsToMruList(LPCTSTR lpszFontName); int AddFontsToMruList(CFontDescriptor* lpFont); int AddFontsToMruList(CStringArray* lpStrFaceNames); int AddFontsToMruList(CFontDescriptorArray* lpFontArray); int AddSelectedToMruList(); int RemoveFontsFromMruList(LPCTSTR lpStrFaceName); int RemoveFontsFromMruList(CFontDescriptor* lpFont); int RemoveFontsFromMruList(CStringArray* lpStrFaceNames); int RemoveFontsFromMruList(CFontDescriptorArray* lpFontArray); int RemoveSelectedFontsFromMruList(); BOOL HasMruList(); void RefreshMruList(); void ClearMruList(); const CString& GetCustomMruListName(); int GetMruFontCount(); BOOL EnableMruListPersistence (BOOL bPersistence = TRUE); // If you call this function (and the current MRU // list is not a private one and is valid), // then the content of the MRU list will // not be saved and the key and values are removed // immediately from the registry // (or the .INI file) of the application. BOOL IsMruListPersistenceEnabled (); BOOL ReloadMruListSettings(); BOOL SaveMruListSettings();
杂项函数
int GetFontCount(); int GetSelectedCount(); int GetSelectedFontNames(CStringArray* pStrArray); int GetSelectedFontDescriptors(CFontDescriptorArray* lpArray); int ClearSelection(); BOOL SubclassDlgItem(UINT nID, CWnd* pParent); BOOL SubclassWindow(HWND hWnd); void SetSampleText(LPCTSTR lpszSampleText); CString& GetSampleText(); // For the tooltip window (see above in the article).
数据成员
没有公共数据成员。
CFontDescriptor 类
成员函数
CFont* GetFontObject(int nHeight = 0); // returns a CFont pointer corresponding // to the facename and the height // (or default height). Due to the // fact the CFontListManager create CFont // objects only when needed, // the protected data member m_pFont can be not // initialized. If you want a valid // CFont object, use this function.
数据成员
// Facename of the font CString m_strFaceName; // Flags describing the font type // (see possible values in FontListManager.h) int m_nFontType; // The tooltip text is a sample text (default) DWORD m_dwCharSets; // The index of the image // corresponding to the font type int m_nImage;
全局函数
最近使用列表的持久化函数
void LoadFontListSettings(); void SaveFontListSettings(); BOOL ReloadMruListSettings(LPCTSTR lpszMruListName); BOOL SaveMruListSettings(LPCTSTR lpszMruListName); BOOL EnableMruListPersistence (LPCTSTR lpszMruListName, BOOL bPersistence = TRUE); // See also the // CFontListBox::EnableMruListPersistence function. // The lpszMruListName parameter // can be the predefined value GLOBAL_MRULIST. BOOL IsMruListPersistenceEnabled (LPCTSTR lpszMruListName);
安装指南
源文件分为两个项目:库项目和演示项目。
库项目安装
- 将extlib.zip(尊重文件夹名称)解压到您的磁盘上。您应该得到类似这样的内容:
- 在 Visual Studio 6 中打开FontControl.dsw项目,然后重新编译所有需要的库(菜单 Build->Batch build->然后选择您的项目)。
- 通过将xxx/ExtLib/Include和xxx/ExtLib/Lib添加到 VS6 搜索目录(菜单 Tools->Options->Directories)来准备您的开发环境。
现在,您可以在您的程序中使用CFontListBox
类了。
演示项目安装
将demo.zip(尊重文件夹名称)解压到您的磁盘上,打开项目文件,然后重新编译它。
致谢
许多 CodeProject 的贡献者会在这里找到他们代码的一部分,我感谢他们的(无意的)帮助(但我没有记录他们文章的引用:如果您觉得您对这段代码有贡献,请给我发邮件,我将修改本文并添加指向您代码的链接)。
我记得的有:
- Paul S. Vickery - Dynamically re-creating a list box。
- Jean-Michel LE FOL - FontComboBox ActiveX control。
- Chris Losinger, Dave Schumann - Font Combo。
- AliRafiee - Transparent ListBox。
历史
- 10 月 10 日,2006 年 - 版本 1.0
- First version.
- 1 月 25 日,2006 年 - 版本 1.1
- 修复了排序字体名称时的错误。
- 修复了计算某些打印机字体图像索引时的错误。
- 修复了字体枚举中的错误。