65.9K
CodeProject 正在变化。 阅读更多。
Home

CFontListBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (24投票s)

2006年1月20日

9分钟阅读

viewsIcon

96567

downloadIcon

3693

一个列表框,可以显示和选择字体,并具有特殊功能。

Content

引言

首先,对于我英语和本文内容中的任何错误,我深表歉意(这是我的第一次投稿)。

为了一个个人项目(一个PropertyList控件),我需要一个ListBox,它可以显示和选择字体。阅读了关于这个主题的几篇文章后,我决定编写自己的控件,具有以下主要行为:

  • 字体类型检测时的性能(当我开始这项工作时,我只有一台 333 MHz 的奔腾电脑!),
  • 可以显示字体名称为标准文本或使用字体本身,
  • 可以使用样本文本或字体名称显示工具提示(跟踪鼠标指针或选定项目),
  • 具有最近使用(MRU)子列表,可由应用程序中的多个CFontListBox实例共享,或不共享,
  • 项目不依赖于位图或ImageList资源文件,
  • 可以作为对话框子类化控件使用,也可以手动创建。

背景

要管理一个最近使用(MRU)子列表(共享或不共享),CFontListBox使用CFontListManager实例提供的服务。应用程序初始化期间只自动创建一个CFontListManager实例。该类负责加载字体列表,通过CFontDescriptorCFontMruList对象及其各种集合来管理最近使用列表(私有于CFontListBox实例,在两个或多个控件之间共享,或全局于应用程序)。

该控件从COMDLG32.DLL的位图资源中获取用于显示列表的位图,因此您的项目中不需要包含特定的资源。

为了允许加载字体列表的过程,该控件不使用其他控件使用的方式。我试图区分字体类型(TrueType、OpenType 等)。它使用::EnumFontFamiliesEx API 传递给回调函数的标志(参见FontListManager.cpp中的EnumFontProcCFontListManager::FillFontList()函数)。

需要重新创建基础列表框窗口,以便在创建后修改控件,以遵循CFontListBox样式标志的修改。

最近使用列表的持久化需要在CWinApp::InitInstanceCWinApp::ExitInstance方法中调用两个函数。这是因为当创建一个CFontListManager实例时,没有调用CWinApp::SetCurrentHandles()(MFC 库的这个函数负责初始化CWinApp对象的成员变量,如m_pszRegistryKeym_pszProfileName)。持久化仅对全局最近使用列表和自定义命名列表生效,前提是这些列表被标记为这样做。

CFontListManager有一个功能,可以响应系统广播的消息WM_FONTCHANGE。此功能需要一个顶级窗口来接收该消息,该窗口在首次创建CFontListBox时创建(这只能在CWinApp对象和其他框架或对话框窗口初始化后发生)。

使用代码

CFontListBox一起使用的所有类都位于一个静态库(FontLBLibxx.lib)中。库项目目录树有一个特殊的结构,因为我(为了其他项目)需要一个包含头文件和 lib 文件的唯一位置。

注意:如果您的列表框没有设置LBS_NOINTEGRALHEIGHT样式标志,并且多次更改项目的高度(例如,添加,然后删除最近使用列表),您会看到列表框缩小:这是设计使然!

列表样式标志

  • FNTLB_GRAPHIC:列表项使用相应的字体显示。
  • FNTLB_ITEMHEIGHTEXPANDED:间距与 ComboBox 列表框相同。
  • FNTLB_NOTYPEIMAGE:在字体名称之前不显示字体图像类型。
  • FNTLB_MANUAL:未初始化的字体列表框(下文讨论)。

它们通过SetFlagsModifyFlags函数,或者通过构造函数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:工具提示还显示选定字体列表。

它们通过SetFlagsModifyFlags函数,或者通过构造函数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 列表

您可以使用AttachToMruFontListSetMruFontListName(它们是同义词)函数将 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。不要忘记调用CFontListBoxInitialize()函数。

动态创建 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);

安装指南

源文件分为两个项目:库项目和演示项目。

库项目安装

  1. extlib.zip(尊重文件夹名称)解压到您的磁盘上。您应该得到类似这样的内容:

  2. 在 Visual Studio 6 中打开FontControl.dsw项目,然后重新编译所有需要的库(菜单 Build->Batch build->然后选择您的项目)。
  3. 通过将xxx/ExtLib/Includexxx/ExtLib/Lib添加到 VS6 搜索目录(菜单 Tools->Options->Directories)来准备您的开发环境。

现在,您可以在您的程序中使用CFontListBox类了。

演示项目安装

demo.zip(尊重文件夹名称)解压到您的磁盘上,打开项目文件,然后重新编译它。

致谢

许多 CodeProject 的贡献者会在这里找到他们代码的一部分,我感谢他们的(无意的)帮助(但我没有记录他们文章的引用:如果您觉得您对这段代码有贡献,请给我发邮件,我将修改本文并添加指向您代码的链接)。

我记得的有:

历史

  • 10 月 10 日,2006 年 - 版本 1.0
    • First version.
  • 1 月 25 日,2006 年 - 版本 1.1 
    • 修复了排序字体名称时的错误。
    • 修复了计算某些打印机字体图像索引时的错误。
    • 修复了字体枚举中的错误。
© . All rights reserved.