MFC ActiveX 控件:使用字体

如果 ActiveX 控件显示文本,则可以允许控件用户通过更改字体属性来更改文本外观。 字体属性作为字体对象实现,可以是两种类型之一:库存或自定义。 股票字体属性是预实现的字体属性,可以使用“添加属性向导”添加。 自定义字体属性未预先实现,控件开发人员确定属性的行为和用法。

本文涵盖以下主题:

使用 Stock Font 属性

Stock Font 属性由 类 COleControl 预先实现。 此外,还可以使用标准 Font 属性页,允许用户更改字体对象的各种属性,例如其名称、大小和样式。

通过 GetFont、SetFontInternalGetFont 函数COleControl访问字体对象。 控件用户将以与任何其他 Get/Set 属性相同的方式通过 GetFont 字体 SetFont 对象访问字体对象。 当需要从控件内部访问字体对象时,请使用该 InternalGetFont 函数。

MFC ActiveX 控件中所述:属性使用“添加属性向导”可以轻松添加库存属性。 选择 Font 属性,“添加属性向导”会自动将股票字体条目插入控件的调度映射中。

使用“添加属性向导”添加 stock Font 属性

  1. 加载控件的项目。

  2. 在类视图中,展开控件的库节点。

  3. 右键单击控件(库节点的第二个节点)的接口节点以打开快捷菜单。

  4. 在快捷菜单中,单击“ 添加 ”,然后单击“ 添加属性”。

    这将打开“添加属性向导”。

  5. “属性名称 ”框中,单击“ 字体”。

  6. 单击“完成”。

“添加属性向导”将以下行添加到控件的调度映射(位于控件类实现文件中):

DISP_STOCKPROP_FONT()

此外,“添加属性向导”将以下行添加到控件。IDL 文件:

[id(DISPID_FONT)] IFontDisp* Font;

stock Caption 属性是可以使用“字体”属性信息绘制的文本属性的示例。 将 stock Caption 属性添加到控件使用与用于 stock Font 属性的步骤类似。

使用“添加属性向导”添加 stock Caption 属性

  1. 加载控件的项目。

  2. 在类视图中,展开控件的库节点。

  3. 右键单击控件(库节点的第二个节点)的接口节点以打开快捷菜单。

  4. 在快捷菜单中,单击“ 添加 ”,然后单击“ 添加属性”。

    这将打开“添加属性向导”。

  5. “属性名称 ”框中,单击“ 标题”。

  6. 单击“完成”。

“添加属性向导”将以下行添加到控件的调度映射(位于控件类实现文件中):

DISP_STOCKPROP_CAPTION()

修改 OnDraw 函数

对控件中显示的所有文本使用 Windows 系统字体的默认实现 OnDraw 。 这意味着必须通过在设备上下文中选择字体对象来修改 OnDraw 代码。 为此,请调用 COleControl::SelectStockFont 并传递控件的设备上下文,如以下示例所示:

CFont* pOldFont;
TEXTMETRIC tm;
const CString& strCaption = InternalGetText();

pOldFont = SelectStockFont(pdc);
pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
pdc->GetTextMetrics(&tm);
pdc->SetTextAlign(TA_CENTER | TA_TOP);
pdc->ExtTextOut((rcBounds.left + rcBounds.right) / 2,
(rcBounds.top + rcBounds.bottom - tm.tmHeight) / 2,
ETO_CLIPPED, rcBounds, strCaption, strCaption.GetLength(), NULL);

pdc->SelectObject(pOldFont);

OnDraw修改函数以使用字体对象后,控件中的任何文本将显示控件的 stock Font 属性的特征。

在控件中使用自定义字体属性

除了 stock Font 属性,ActiveX 控件还可以具有自定义字体属性。 若要添加自定义字体属性,必须:

实现自定义字体属性

若要实现自定义 Font 属性,请使用“添加属性向导”添加属性,然后对代码进行一些修改。 以下部分介绍如何将自定义 HeadingFont 属性添加到示例控件。

使用“添加属性向导”添加自定义 Font 属性
  1. 加载控件的项目。

  2. 在类视图中,展开控件的库节点。

  3. 右键单击控件(库节点的第二个节点)的接口节点以打开快捷菜单。

  4. 在快捷菜单中,单击“ 添加 ”,然后单击“ 添加属性”。

    这将打开“添加属性向导”。

  5. “属性名称 ”框中,键入属性的名称。 对于此示例,请使用 HeadingFont

  6. 对于 实现类型,请单击 “获取/设置方法”。

  7. “属性类型” 框中,选择属性类型的 IDispatch*

  8. 单击“完成”。

“添加属性向导”创建用于将 HeadingFont 自定义属性添加到 CSampleCtrl 类和 SAMPLE 的代码。IDL 文件。 由于 HeadingFont 是 Get/Set 属性类型,“添加属性向导”会 CSampleCtrl 修改类的调度映射以包含 DISP_PROPERTY_EX_ID DISP_PROPERTY_EX 宏条目:

DISP_PROPERTY_EX_ID(CMyAxFontCtrl, "HeadingFont", dispidHeadingFont,
   GetHeadingFont, SetHeadingFont, VT_DISPATCH)

DISP_PROPERTY_EX宏将 HeadingFont 属性名称与其相应的 CSampleCtrl 类 Get 和 Set 方法相关联, GetHeadingFont 以及 SetHeadingFont。 还指定了属性值的类型;在本例中,VT_FONT。

“添加属性向导”还会在控件头文件()中添加声明。H) 用于 GetHeadingFontSetHeadingFont 函数,并在控件实现文件 () 中添加其函数模板。CPP):

IDispatch* CWizardGenCtrl::GetHeadingFont(void)
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   // TODO: Add your dispatch handler code here

   return NULL;
}

void CWizardGenCtrl::SetHeadingFont(IDispatch* /*pVal*/)
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   // TODO: Add your property handler code here

   SetModifiedFlag();
}

最后,“添加属性向导”将修改控件。通过添加属性的条目 HeadingFont IDL 文件:

[id(1)] IDispatch* HeadingFont;

对控件代码的修改

将属性添加到 HeadingFont 控件后,必须对控件标头和实现文件进行一些更改,才能完全支持新属性。

在控件头文件中(。H),添加受保护成员变量的以下声明:

protected:
   CFontHolder m_fontHeading;

在控件实现文件中(。CPP),执行以下作:

  • 初始化控件构造函数中的 m_fontHeading

    CMyAxFontCtrl::CMyAxFontCtrl()
       : m_fontHeading(&m_xFontNotification)
    {
       InitializeIIDs(&IID_DNVC_MFC_AxFont, &IID_DNVC_MFC_AxFontEvents);
    }
    
  • 声明包含字体默认属性的静态 FONTDESC 结构。

    static const FONTDESC _fontdescHeading =
    { sizeof(FONTDESC), OLESTR("MS Sans Serif"), FONTSIZE(12), FW_BOLD,
      ANSI_CHARSET, FALSE, FALSE, FALSE };
    
  • 在控件 DoPropExchange 成员函数中,添加对函数的 PX_Font 调用。 这为自定义 Font 属性提供初始化和持久性。

    void CMyAxFontCtrl::DoPropExchange(CPropExchange* pPX)
    {
       ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
       COleControl::DoPropExchange(pPX);
    
       // [...other PX_ function calls...]
       PX_Font(pPX, _T("HeadingFont"), m_fontHeading, &_fontdescHeading);
    }
    
  • 完成实现控件 GetHeadingFont 成员函数。

    IDispatch* CMyAxFontCtrl::GetHeadingFont(void)
    {
       AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
       return m_fontHeading.GetFontDispatch();
    }
    
  • 完成实现控件 SetHeadingFont 成员函数。

    void CMyAxFontCtrl::SetHeadingFont(IDispatch* pVal)
    {
       AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
       m_fontHeading.InitializeFont(&_fontdescHeading, pVal);
       OnFontChanged();    //notify any changes
       SetModifiedFlag();
    }
    
  • 修改控件 OnDraw 成员函数以定义用于保存以前所选字体的变量。

    CFont* pOldHeadingFont;
    
  • 修改控件 OnDraw 成员函数,以在要使用的字体的位置添加以下行,以选择设备上下文中的自定义字体。

    pOldHeadingFont = SelectFontObject(pdc, m_fontHeading);
    
  • 通过添加以下行,修改控件 OnDraw 成员函数,以在使用字体后添加以下行,以选择上一个字体回到设备上下文中。

    pdc->SelectObject(pOldHeadingFont);
    

实现自定义 Font 属性后,应实现标准 Font 属性页,使控件用户能够更改控件的当前字体。 若要添加标准 Font 属性页的属性页 ID,请在BEGIN_PROPPAGEIDS宏后面插入以下行:

PROPPAGEID(CLSID_CFontPropPage)

还必须将BEGIN_PROPPAGEIDS宏的 count 参数递增一个。 以下行说明了这一点:

BEGIN_PROPPAGEIDS(CMyAxFontCtrl, 2)

完成这些更改后,请重新生成整个项目以合并其他功能。

处理字体通知

在大多数情况下,控件需要知道何时修改字体对象的特征。 每个字体对象都可以通过调用接口的成员函数 IFontNotification (由该 COleControl接口实现)来提供通知。

如果控件使用 stock Font 属性,则其通知由 OnFontChanged 成员函数 COleControl处理。 添加自定义字体属性时,可以让它们使用相同的实现。 在上一部分的示例中,这是通过在初始化m_fontHeading成员变量时传递 &m_xFontNotification 来实现的。

实现多个字体对象接口。
实现多个字体对象接口

上图中的实线显示,这两个字体对象都使用相同的实现 IFontNotification。 如果想要区分哪些字体已更改,这可能会导致问题。

区分控件的字体对象通知的一种方法是为控件中的每个字体对象创建接口的 IFontNotification 单独实现。 通过此方法,可以仅更新使用最近修改的字体的字符串或字符串来优化绘图代码。 以下部分演示了为第二个 Font 属性实现单独的通知接口所需的步骤。 假定第二个字体属性是 HeadingFont 上一节中添加的属性。

实现新的字体通知界面

若要区分两种或更多字体的通知,必须为控件中使用的每个字体实现新的通知界面。 以下部分介绍如何通过修改控件标头和实现文件来实现新的字体通知界面。

头文件新增内容

在控件头文件中(。H),将以下行添加到类声明:

protected:
   BEGIN_INTERFACE_PART(HeadingFontNotify, IPropertyNotifySink)
      INIT_INTERFACE_PART(CMyAxFontCtrl, HeadingFontNotify)
      STDMETHOD(OnRequestEdit)(DISPID);
   STDMETHOD(OnChanged)(DISPID);
   END_INTERFACE_PART(HeadingFontNotify)

这会创建名为 <a0/> 的接口的实现。 此新接口包含一个名为 OnChanged..

对实现文件进行添加

在初始化标题字体的代码(在控件构造函数中),将m_xFontNotification更改为 &m_xHeadingFontNotify。 然后添加以下代码:

STDMETHODIMP_(ULONG) CMyAxFontCtrl::XHeadingFontNotify::AddRef()
{
   METHOD_MANAGE_STATE(CMyAxFontCtrl, HeadingFontNotify)
      return 1;
}
STDMETHODIMP_(ULONG) CMyAxFontCtrl::XHeadingFontNotify::Release()
{
   METHOD_MANAGE_STATE(CMyAxFontCtrl, HeadingFontNotify)
      return 0;
}

STDMETHODIMP CMyAxFontCtrl::XHeadingFontNotify::QueryInterface(REFIID iid, LPVOID FAR* ppvObj)
{
   METHOD_MANAGE_STATE(CMyAxFontCtrl, HeadingFontNotify)
      if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IPropertyNotifySink))
      {
         *ppvObj = this;
         AddRef();
         return NOERROR;
      }
   return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP CMyAxFontCtrl::XHeadingFontNotify::OnChanged(DISPID)
{
   METHOD_MANAGE_STATE(CMyAxFontCtrl, HeadingFontNotify)
      pThis->InvalidateControl();
   return NOERROR;
}

STDMETHODIMP CMyAxFontCtrl::XHeadingFontNotify::OnRequestEdit(DISPID)
{
   return NOERROR;
}

AddRef接口中的IPropertyNotifySinkRelease方法跟踪 ActiveX 控件对象的引用计数。 当控件获取对接口指针的访问权限时,控件将调用 AddRef 递增引用计数。 当控件使用指针完成时,它调用 Release的方式与调用该控件以释放全局内存块的方式大致相同 GlobalFree 。 当此接口的引用计数为零时,可以释放接口实现。 在此示例中,该 QueryInterface 函数返回指向 IPropertyNotifySink 特定对象上的接口的指针。 此函数允许 ActiveX 控件查询对象以确定它支持哪些接口。

对项目进行这些更改后,请重新生成项目并使用测试容器测试接口。 请参阅 使用测试容器测试属性和事件 了解有关如何访问测试容器的信息。

另请参阅

MFC ActiveX 控件
MFC ActiveX 控件:在 ActiveX 控件中使用图片
MFC ActiveX 控件:使用 Stock 属性页