如何在 ActiveX 控件中使用字体
作者:韩耀旭
字体的使用是ActiveX控件制作过程中不可缺少的部分。本文将深入浅出地介绍在ActiveX控件中制作过程中如何使用字体。本文所提及的ActiveX控件制作是指利用VC++6.0的MFC
ActiveX ControlWizard 工具,用其它方法制作 ActiveX 控件本文不作讨论。
本文专门设置了一些例程,以便您能够更好地掌握这些内容。这些例程都是完整的ActiveX控件制作源代码。为了突出重点,本文仅对字体使用相关部分的源代码作较详细的介绍,其余部分的代码本文不作过多讨论。字体作为一种属性在ActiveX控件中使用,与其它属性没有区别,同样分为库存属性和自定义属性,本文将分四个部分进行介绍。
第一部分 在控件中使用库存字体(Stock Font)属性
第二部分 在控件中使用自定义的字体(Custom Font)属性
第三部分 使用多种字体
第四部分 优化代码,合理地使用多种字体
第一部分 在控件中使用库存字体(Stock Font)属性
使用ClassWizard 添加 Font库存属性的步骤:
打开控件工程, 单击View菜单的ClassWizard打开ClassWizard;
单击Automation标签;
在Class Name框,选择你的控件类名;
单击Add Property;
在 External name框, 单击Font;
单击 OK;
单击 OK确认你的选择并关闭 ClassWizard;
ClassWizard将在控件类的执行文件的dispatch映射表中加入下列行:
DISP_STOCKPROP_FONT()
另外, ClassWizard 在.ODL 文件中加入下列行:
[id(DISPID_FONT), bindable] IFontDisp* Font;
例程1:Label.ocx
下面,我们举一个具体的实例详细演示如何在控件中添加Font库存属性。您可以下载该例程进行编译生成 .ocx 控件,并测试该控件。
我们将要制作的控件取名Label.ocx,模仿VB工具箱中的Label控件。该控件增强了VC++工具箱中的Static
Text控件的有限的功能,您可以方便地在您的工程中使用。例程的工程名也取名为Label。
创建Label工程
打开Visual C++ 6.0,单击File菜单的New,弹出New对话框。在对话框中选择MFC ActiveX
ControlWizard,并输入工程名Label,单击OK按钮,关闭该对话框并弹出MFC ActiveX ControlWizard-Step
1 of 2对话框。如图1-1。
图1-1
接受MFC ActiveX ControlWizard-Step 1 of 2对话框所有默认设置,单击Next按钮,关闭该窗口同时弹出MFC
ActiveX ControlWizard-Step 2 of 2对话框。继续接受MFC ActiveX ControlWizard-Step
2 of 2对话框所有默认设置,单击Finish按钮。
继续单击OK 按钮,Label工程创建完成,我们将继续对该工程进行修改。使用ClassWizard 给控件增加库存属性Caption,BackColor,ForeColor,Font。
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabelCtrl,单击 Add Property;
在External name框中,单击Caption;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
我们已经给控件增加了库存属性Caption,该属性用以设置Label控件的文本内容。按上述同样操作,依次添加库存属性BackColor,ForeColor,Font。这3个属性分别用以设置Label控件的背景色,前景色以及字体的属性。如图1-2。
图1-2
添加颜色属性页和字体属性页
打开工程的LabelCtl.cpp文件,将其中的如下代码进行修改:
BEGIN_PROPPAGEIDS(CLabelCtrl, 1)
PROPPAGEID(CLabelPropPage::guid)
END_PROPPAGEIDS(CLabelCtrl)
修改后的代码:
BEGIN_PROPPAGEIDS(CLabelCtrl, 3) // 具体个数由实际情况而定
PROPPAGEID(CLabelPropPage::guid)//系统自带的属性页
PROPPAGEID(CLSID_CColorPropPage) //加入Color属性页
PROPPAGEID(CLSID_CFontPropPage) //加入Font属性页
END_PROPPAGEIDS(CLabelCtrl)
修改系统自带的属性页
打开系统自带的属性页资源IDD_PROPPAGE_LABEL,将静态文本框IDC_STATIC的内容改为"Caption:",添加一个编辑框(Edit
Box)控件,控件ID为IDC_CAPTION。如图1-3
图1-3
按下"Ctrl+W"组合键打开ClassWizard,选中Member Variables标签,Class name:下拉列表框选择CLabelPropPage,Control
IDs:选中IDC_CAPTION,单击Add Variable…按钮,弹出Add Member Variable对话框。
在Add Member Variable对话框中,Member variable name:框输入m_caption,Category选择Value,Variable
type:框选择CString,Optional property name:框选择Caption,单击OK按钮。如图1-4。
图1-4
再次单击OK按钮关闭ClassWizard。上述操作,编辑框IDC_CAPTION与Caption属性之间建立了联系。您可以通过修改编辑框的内容来改变控件Caption的属性值。
修改OnDraw函数
修改后的OnDraw函数如下:
void CLabelCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid )
{
// TODO: Replace the following code with your own drawing code.
COLORREF colorBack=TranslateColor(GetBackColor());
COLORREF colorFore=TranslateColor(GetForeColor());
// GetBackColor(),GetForeColor()分别得到背景色和前景色
// GetBackColor(),GetForeColor()返回类型为OLE_COLOR
// TranslateColor函数将OLE_COLOR类型转换为COLORREF类型
CBrush brush(colorBack);
pdc-FillRect(rcBounds, &brush);
//背景色填充整个控件
pdc-SetBkMode(TRANSPARENT);
//设置透明背景色
pdc-SetTextColor(colorFore);
//前景色设置为字体颜色
const CString& strCaption = InternalGetText();
//获得库存属性Caption的值
CFont* pOldFont;
pOldFont = SelectStockFont(pdc);
//设备上下文中选择字体对象
RECT rect;
::CopyRect(&rect,rcBounds);
pdc-DrawText(strCaption,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
//单行,水平居中,垂直居中显示
pdc-SelectObject(pOldFont);
}
Label控件的制作已经完成了,您可以方便地改变字体的大小、类型、颜色以及控件的背景色。当然,您还可以进一步完善该控件,更加方便您的使用。
第二部分 在控件中使用自定义的字体(Custom Font)属性
除了库存字体属性Font外, ActiveX控件还允许使用自定义的字体属性。添加一个自定义字体属性,要完成一个自定义字体属性,你应该首先使用
ClassWizard 添加自定义字体属性,然后对代码作一些修改。下面将描述怎样给一个控件添加自定义属性(假设控件的名字为Sample):
使用 ClassWizard 添加自定义字体属性
打开 控件工程,单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
单击 Add Property;
在External name框中,输入属性名(在本例中,假设输入的自定义属性名为HeadingFont);
在Implementation框中,单击Get/Set Methods;
在Return Type框中,选择LPFONTDISP作为属性类型;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
ClassWizard 将产生相应的代码把自定义属性HeadingFont添加到CSampleCtrl类中和 SAMPLE.ODL文件中。既然
HeadingFont是一个 Get/Set 属性类型,ClassWizard 修改CSampleCtrl 类的dispatch
映射表来包括一个DISP_PROPERTY_EX 宏入口:
BEGIN_DISPATCH_MAP(CSampleCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CSampleCtrl)
DISP_PROPERTY_EX(CSampleCtrl, "HeadingFont", GetHeadingFont,
SetHeadingFont, VT_DISPATCH)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
DISP_PROPERTY_EX 宏把HeadingFont属性名和它的相应的CSampleCtrl类的Get / Set 方法(GetHeadingFont and SetHeadingFont)联系起来。
ClassWizard 也在控件的头文件(.H)为GetHeadingFont 和 SetHeadingFont函数添加声明 ,并在控件的执行文件(.CPP)添加模板函数:
LPFONTDISP CSampleCtrl::GetHeadingFont()
{
// TODO: Add your property handler here
return NULL;
}
void CSampleCtrl::SetHeadingFont(LPFONTDISP newValue)
{
// TODO: Add your property handler here
SetModifiedFlag();
}
最后, ClassWizard修改控件的 .ODL 文件,为HeadingFont 属性添加一个入口:[id(1)] IFontDisp* HeadingFont;
修改控件代码
现在你已经为控件添加了HeadingFont属性,你必须对控件的头文件和执行文件进行修改以完全支持新的属性。在控件的头文件(.H),添加一个protected成员变量的声明:
protected:
CFontHolder m_fontHeading;
在控件的执行文件(.CPP)中,如下操作:在控件的构造函数中初始化 m_fontHeading。
CSampleCtrl::CSampleCtrl( ) : m_fontHeading( &m_xFontNotification )
{
// [...body of constructor...]
}
声明一个静态FONTDESC结构,该结构包含缺省的字体属性。
static const FONTDESC _fontdescHeading =
{ sizeof(FONTDESC), OLESTR("MS Sans Serif"), FONTSIZE( 12 ), FW_BOLD,
ANSI_CHARSET, FALSE, FALSE, FALSE };
在控件的DoPropExchange 成员函数,添加一个PX_Font函数的调用,提供自定义字体属性的初始化和持久化。
void CSampleCtrl::DoPropExchange(CPropExchange* pPX)
{
COleControl::DoPropExchange(pPX);
// [...other PX_ function calls...]
PX_Font(pPX, _T("HeadingFont"), m_fontHeading, &_fontdescHeading);
}
完成控件的GetHeadingFont成员函数。
LPFONTDISP CSampleCtrl::GetHeadingFont( )
{
return m_fontHeading.GetFontDispatch( );
}
完成控件的SetHeadingFont成员函数
void CSampleControl::SetHeadingFont( LPFONTDISP newValue )
{
m_fontHeading.InitializeFont( &_fontdescHeading, newValue);
OnFontChanged(); //notify any changes
SetModifiedFlag( );
}
修改控件的OnDraw成员函数,定义一个变量控制以前选择的字体。
CFont* pOldHeadingFont;
修改控件的OnDraw成员函数,添加下列代码,在设备上下文中选择自定义的字体。
pOldHeadingFont = SelectFontObject(pdc, m_fontHeading);
修改控件的OnDraw成员函数,字体使用完后恢复原字体到设备上下文。
pdc-SelectObject(pOldHeadingFont);
添加标准字体属性页
自定义字体属性设置完成后,应添加标准字体属性页,允许控件使用者更改控件当前的字体。为标准字体属性页增加属性页的ID号, 在BEGIN_PROPPAGEIDS宏后添加如下行:
PROPPAGEID(CLSID_CFontPropPage)
完成这些工作后,重新编译工程。
例程2:Label1.ocx
例程2的Label1控件与例程1的Label控件在功能实现上没有任何区别,只是在改变字体特性时,不再使用库存字体属性Font,而是使用自定义字体属性CaptionFont。下面我们将结合Label1控件介绍如何在控件中使用自定义属性。
打开Visual C++ 6.0,按照例程1的操作步骤新建Label1工程,象例程1那样接受所有默认设置,
完成Label1工程创建。如图2-1。
图2-1
使用ClassWizard 给控件增加库存属性BackColor,ForeColor,Caption
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabel1Ctrl,单击 Add Property;
在External name框中,单击BackColor;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
按上述同样操作,添加库存属性ForeColor,Caption。BackColor,ForeColor,Caption这三个属性分别用来设置Label1控件的背景色,前景色和字符串内容。如图2-2。
图2-2
使用ClassWizard 给控件增加自定义属性CaptionFont
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabel1Ctrl,单击 Add Property;
在External name框中,输入自定义属性名CaptionFont;
在Implementation框中,单击Get/Set methods;
在Return Type框中,选择LPFONTDISP作为属性类型;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
如图2-3
图2-3
修改控件代码
在控件的头文件(Label1Ctl.h),添加一个protected成员变量的声明:
protected:
CFontHolder m_fontCaption;
如图2-4。
图2-4
在控件的执行文件(.CPP)中,如下操作:在控件的构造函数中初始化 m_fontCaption。
CLabel1Ctrl::CLabel1Ctrl(): m_fontCaption(&m_xFontNotification)
{
InitializeIIDs(&IID_DLabel1, &IID_DLabel1Events);
// TODO: Initialize your control''s instance data here.
}
声明一个静态FONTDESC结构,该结构包含缺省的字体属性。
static const FONTDESC _fontdescCaption =
{ sizeof(FONTDESC), OLESTR("MS Sans Serif"), FONTSIZE( 12 ), FW_BOLD,
ANSI_CHARSET, FALSE, FALSE, FALSE };
在控件的DoPropExchange 成员函数,添加一个PX_Font函数的调用,提供自定义字体属性的初始化和持久化。
void CLabel1Ctrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
PX_Font(pPX, _T("CaptionFont"), m_fontCaption, &_fontdescCaption);
}
完成控件的GetCaptionFont成员函数。
LPFONTDISP CLabel1Ctrl::GetCaptionFont()
{
// TODO: Add your property handler here
return m_fontCaption.GetFontDispatch();
}
完成控件的SetHeadingFont成员函数
void CLabel1Ctrl::SetCaptionFont(LPFONTDISP newValue)
{
// TODO: Add your property handler here
m_fontCaption.InitializeFont(&_fontdescCaption,newValue);
OnFontChanged();
SetModifiedFlag();
}
修改控件的OnDraw成员函数,
void CLabel1Ctrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// TODO: Replace the following code with your own drawing code.
COLORREF colorBack=TranslateColor(GetBackColor());
COLORREF colorFore=TranslateColor(GetForeColor());
CBrush brush(colorBack);
pdc-FillRect(rcBounds, &brush);
//背景色填充整个控件
pdc-SetBkMode(TRANSPARENT);
//设置透明背景色
pdc-SetTextColor(colorFore);
//前景色设置为字体颜色
const CString& strCaption = InternalGetText();
//获得库存属性Caption的值
CFont* pOldFont;
pOldFont = SelectFontObject(pdc,m_fontCaption);
//设备上下文中选择字体对象
RECT rect;
::CopyRect(&rect,rcBounds);
pdc-DrawText(strCaption,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
//单行,水平居中,垂直居中显示
pdc-SelectObject(pOldFont);
}
添加颜色属性页和字体属性页
打开工程的Label1Ctl.cpp文件,将其中的如下代码进行修改:
BEGIN_PROPPAGEIDS(CLabel1Ctrl, 1)
PROPPAGEID(CLabelPropPage::guid)
END_PROPPAGEIDS(CLabel1Ctrl)
修改后的代码:
BEGIN_PROPPAGEIDS(CLabel1Ctrl, 3) // 具体个数由实际情况而定
PROPPAGEID(CLabel1PropPage::guid)//系统自带的属性页
PROPPAGEID(CLSID_CColorPropPage) //加入Color属性页
PROPPAGEID(CLSID_CFontPropPage) //加入Font属性页
END_PROPPAGEIDS(CLabel1Ctrl)
修改系统自带的属性页
打开系统自带的属性页资源IDD_PROPPAGE_LABEL1,将静态文本框IDC_STATIC的内容改为"Caption:",添加一个编辑框(Edit
Box)控件,控件ID为IDC_CAPTION。如图2-5。
图2-5
按下"Ctrl+W"组合键打开ClassWizard,选中Member Variables标签,Class
name:下拉列表框选择CLabel1PropPage,Control IDs:选中IDC_CAPTION,单击Add
Variable…按钮,弹出Add Member Variable对话框。
在Add Member Variable对话框中,Member variable name:框输入m_caption,Category选择Value,Variable
type:框选择CString,Optional property name:框选择Caption,单击OK按钮。
再次单击OK按钮关闭ClassWizard。
上述操作,编辑框IDC_CAPTION与Caption属性之间建立了联系。您可以通过修改编辑框的内容来改变控件Caption的属性值。
编译并构建Label1.ocx控件。
第三部分 使用多种字体
前面分别详细说明了如何在控件中使用库存属性和自定义属性。但有时我们需要在一个控件中使用多种字体,下面我们将说明如何在一个控件中实现多种字体属性。
大多数情况下,每个字体对象都能够通过调用IFontNotification接口的成员函数(由COleControl实现)在自身更改时提供通知。如果控件使用库存Font属性,则其通知由COleControl的OnFontChanged成员函数处理。当添加自定义Font属性时,可以让它们使用同一实现。在第二部分的例程中,这一点通过在初始化m_fontCaption成员变量时传递&m_xFontNotification来实现。
例程3将演示多个字体对象使用IFontNotification的同一实现,实现在一个控件中使用多种字体。
例程3:LabelEx0.ocx
例程1的Label控件确实增强了VC++工具箱中的Static
Text控件的功能。但对于形如"m3/h","h2o"的文本内容,使用一个Label控件就有点无能为力了,下面我们将制作一个LabelEx0控件来再次扩展Label控件的功能,同时演示如何在控件中添加多种字体属性。
LabelEx0控件内部由上、中、下三个部分组成,三个部分的字体内容和字体属性可以分别设置,以方便地显示上下标部分。
打开Visual C++ 6.0,新建LabelEx0工程,接受所有默认设置,完成LabelEx0工程创建。使用ClassWizard
给控件增加库存属性BackColor,ForeColor:
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabelEx0Ctrl,单击 Add Property;
在External name框中,单击BackColor;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
我们已经给控件增加了库存属性BackColor,该属性用以设置LabelEx0控件的背景色。按上述同样操作,添加库存属性ForeColor。
使用ClassWizard 给控件增加自定义属性CaptionUp,CaptionMiddle,CaptionDown:
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabelEx0Ctrl,单击 Add Property;
在External name框中,输入自定义属性名CaptionUp;
在Implementation框中,单击Member variable;
在Return Type框中,选择CString作为属性类型;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
按上述同样操作,添加自定义属性CaptionMiddle,CaptionDown。CaptionUp,CaptionMiddle和CaptionDown分别用以设置LabelEx0控件上、中、下三个部分的文本内容。
使用ClassWizard 给控件增加自定义属性XUp,YUp,XMiddle, YMiddle, XDown, YDown
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabelEx0Ctrl,单击 Add Property;
在External name框中,输入自定义属性名XUp;
在Implementation框中,单击Member variable;
在Return Type框中,选择long作为属性类型;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
按上述同样操作,添加自定义属性YUp,XMiddle, YMiddle, XDown和YDown。其中XUp
和Yup属性用来决定上标文本的位置;XMiddle和YMiddle用以设置中间文本的位置;XDown和YDown
用以设置LabelEx0控件下标文本的位置。
使用ClassWizard 给控件增加自定义属性FontUp,FontMiddle,FontDown
单击View菜单的ClassWizard打开 ClassWizard;
单击Automation标签;
Class Name下拉列表框选择CLabelEx0Ctrl,单击 Add Property;
在External name框中,输入自定义属性名FontUp;
在Implementation框中,单击Get/Set methods;
在Return Type框中,选择LPFONTDISP作为属性类型;
单击OK;
单击OK确认你的选择并关闭ClassWizard;
按上述同样操作,添加自定义属性FontMiddle,FontDown。FontUp,FontMiddle和FontDown分别用以设置LabelEx0控件上、中、下三个部分的字体属性。
添加颜色属性页和字体属性页
打开工程的LabelEx0Ctl.cpp文件,将其中的如下代码进行修改:
BEGIN_PROPPAGEIDS(CLabelEx0Ctrl, 1)
PROPPAGEID(CLabelEx0PropPage::guid)
END_PROPPAGEIDS(CLabelEx0Ctrl)
修改后的代码:
BEGIN_PROPPAGEIDS(CLabelEx0Ctrl, 3) // 具体个数由实际情况而定
PROPPAGEID(CLabelEx0PropPage::guid)//系统自带的属性页
PROPPAGEID(CLSID_CColorPropPage) //加入Color属性页
PROPPAGEID(CLSID_CFontPropPage) //加入Font属性页
END_PROPPAGEIDS(CLabelEx0Ctrl)
修改系统自带的属性页
打开系统自带的属性页资源IDD_PROPPAGE_LABELEx0,把属性页的尺寸由250×62调整为250×110。删除原有的静态文本框,重新添加9个静态文本框,静态文本框的内容依次为"CaptionUp:",
"CaptionMiddle:","CaptionDown:","XUp:","Yup:","XMiddle:","YMiddle:","XDown:","YDown:"。添加9个对应的编辑框(Edit
Box)控件,控件ID依次为IDC_CAPTIONUP,IDC_CAPTIONMIDDLE,IDC_CAPTIONDOWN,IDC_XUP,IDC_YUP,IDC_XMIDDLE,IDC_YMIDDLE,IDC_XDOWN,IDC_YDOWN。其中,IDC_CAPTIONUP,IDC_CAPTIONMIDDLE,IDC_CAPTIONDOWN分别用于输入上标,中间,下标的文本内容;IDC_XUP,IDC_YUP,IDC_XMIDDLE,IDC_YMIDDLE,IDC_XDOWN,IDC_YDOWN分别用于输入上标,中间,下标的文本的坐标位置。如图3-1。
图3-1
按下"Ctrl+W"组合键打开ClassWizard,选中Member Variables标签,Class
name:下拉列表框选择CLabelEx0PropPage,Control IDs:选中IDC_CAPTIONUP,单击Add
Variable…按钮,弹出Add Member Variable对话框。
在Add Member Variable对话框中,Member variable name:框输入m_captionUp,Category选择Value,Variable
type:框选择CString,Optional property name:框选择CaptionUp,单击OK按钮。重复上述操作,为其余8个编辑控件建立添加相应的成员变量。对应关系如下表:
控件ID
Member variable name
Category
Variable type
Optional property name
IDC_CAPTIONUP
m_captionUp
Value
CString
CaptionUp
IDC_CAPTIONMIDDLE
m_captionMiddle
Value
CString
CaptionMiddle
IDC_CAPTIONDOWN
m_captionDown
Value
CString
CaptionDown
IDC_XUP
m_xUp
Value
long
XUp
IDC_YUP
m_yUp
Value
long
YUp
IDC_XMIDDLE
m_xMiddle
Value
long
XMiddle
IDC_YMIDDLE
m_yMiddle
Value
long
YMiddle
IDC_XDOWN
m_xDown
Value
long
XDown
IDC_YDOWN
m_yDown
Value
long
YDown
再次单击OK按钮关闭ClassWizard。
修改控件代码
在控件的头文件(LabelEx0Ctl.h),添加三个protected成员变量的声明:
protected:
CFontHolder m_fontUp;
CFontHolder m_fontMiddle;
CFontHolder m_fontDown;
在控件的执行文件(LabelEx0Ctl.CPP)中,如下操作:
在控件的构造函数中初始化 m_fontUp, m_fontMiddle, m_fontDown。
CLabelEx0Ctrl::CLabelEx0Ctrl():m_fontUp(&m_xFontNotification),
m_fontMiddle(&m_xFontNotification),m_fontDown(&m_xFontNotification)
{
InitializeIIDs(&IID_DLabelEx0, &IID_DLabelEx0Events);
// TODO: Initialize your control''s instance data here.
}
声明一个静态FONTDESC结构,该结构包含缺省的字体属性。
static const FONTDESC _fontdescCaption =
{ sizeof(FONTDESC), OLESTR("MS Sans Serif"), FONTSIZE( 12 ), FW_BOLD,
ANSI_CHARSET, FALSE, FALSE, FALSE };
在控件的DoPropExchange 成员函数,添加PX_函数的调用。
void CLabelEx0Ctrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: Call PX_ functions for each persistent custom property.
PX_Font(pPX, _T("FontUp"), m_fontUp, &_fontdescCaption);
PX_Font(pPX, _T("FontMiddle"), m_fontMiddle, &_fontdescCaption);
PX_Font(pPX, _T("FontDown"), m_fontDown, &_fontdescCaption);
PX_Long(pPX,_T("XUp"),m_xUp,0);
PX_Long(pPX,_T("YUp"),m_yUp,0);
PX_Long(pPX,_T("XMiddle"),m_xMiddle,0);
PX_Long(pPX,_T("YMiddle"),m_yMiddle,20);
PX_Long(pPX,_T("XDown"),m_xDown,0);
PX_Long(pPX,_T("YDown"),m_yDown,30);
PX_String(pPX, _T("CaptionUp"), m_captionUp, _T("上标"));
PX_String(pPX, _T("CaptionMiddle"), m_captionMiddle, _T("中间"));
PX_String(pPX, _T("CaptionDown"), m_captionDown, _T("下标"));
}
完成控件的成员函数
void CLabelEx0Ctrl::OnCaptionUpChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnCaptionMiddleChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnCaptionDownChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnXUpChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnYUpChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnXMiddleChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnYMiddleChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnXDownChanged()
{
InvalidateControl();
SetModifiedFlag();
}
void CLabelEx0Ctrl::OnYDownChanged()
{
InvalidateControl();
SetModifiedFlag();
}
LPFONTDISP CLabelEx0Ctrl::GetFontUp()
{
return m_fontUp.GetFontDispatch();
}
void CLabelEx0Ctrl::SetFontUp(LPFONTDISP newValue)
{
m_fontUp.InitializeFont(&_fontdescCaption,newValue);
OnFontChanged();
SetModifiedFlag();
}
LPFONTDISP CLabelEx0Ctrl::GetFontMiddle()
{
return m_fontMiddle.GetFontDispatch();
}
void CLabelEx0Ctrl::SetFontMiddle(LPFONTDISP newValue)
{
m_fontMiddle.InitializeFont(&_fontdescCaption,newValue);
OnFontChanged();
SetModifiedFlag();
}
LPFONTDISP CLabelEx0Ctrl::GetFontDown()
{
return m_fontDown.GetFontDispatch();
}
void CLabelEx0Ctrl::SetFontDown(LPFONTDISP newValue)
{
m_fontDown.InitializeFont(&_fontdescCaption,newValue);
OnFontChanged();
SetModifiedFlag();
}
修改OnDraw函数,修改后的OnDraw函数如下:
void CLabelExCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// TODO: Replace the following code with your own drawing code.
COLORREF colorBack=TranslateColor(GetBackColor());
COLORREF colorFore=TranslateColor(GetForeColor());
CBrush brush(colorBack);
pdc-FillRect(rcBounds, &brush);
pdc-SetBkMode(TRANSPARENT);
pdc-SetTextColor(colorFore);
CFont* pOldFont;
RECT rect;
::CopyRect(&rect,rcBounds);
pOldFont = SelectFontObject(pdc, m_fontUp);
pdc-TextOut(m_xUp,m_yUp,m_captionUp);
SelectFontObject(pdc, m_fontMiddle);
pdc-TextOut(m_xMiddle,m_yMiddle,m_captionMiddle);
SelectFontObject(pdc, m_fontDown);
pdc-TextOut(m_xDown,m_yDown,m_captionDown);
pdc-SelectObject(pOldFont);
}
LabelEx0控件的制作已经完成了。您可以对它进行测试,并使用它制作形如"m3/h"样式的标签。
第四部分 优化代码,合理地使用多种字体
上例中三个字体对象使用同一IFontNotification实现,不能具体区分控件字体对象的通知。如果想区分哪个字体已更改,可以使用以下方法:
为控件的每个字体对象各创建一个单独的IFontNotification接口实现。此技术使您只需更新使用最近修改的字体的一个或多个字符串,从而优化了绘制代码。以下将以修改例程3
LabelEx0的字体属性FontUp为例来说明如何为第二个字体属性实现单独的通知接口所必需的步骤。
实现新的字体通知接口
若要区分两个或多个字体的通知,必须为控件中使用的每个字体各实现一个新的通知接口。以下各节介绍如何通过修改控件头文件和实现文件来实现新的字体通知接口。
向头文件中添加的内容
在控件头文件(CLabelEx0Ctrl.H)中,添加下列代码:
DECLARE_INTERFACE_MAP() //声明接口映射
BEGIN_INTERFACE_PART(FontUpNotify,IPropertyNotifySink)//声明实现接口IPropertyNotifySink的嵌套类
INIT_INTERFACE_PART(CLabelEx0Ctrl,FontUpNotify)
STDMETHOD(OnRequestEdit)(DISPID);
STDMETHOD(OnChanged)(DISPID);
END_INTERFACE_PART(FontUpNotify)
这样,CLabelEx0Ctrl中多了一个嵌套类XFontUpNotify,以及它的一个变量m_xFontUpNotify。
在CLabelEx0Ctrl的实现文件中加入该嵌套类的实现:
//映射接口IPropertyNotifySink到相应的嵌套类
BEGIN_INTERFACE_MAP(CLabelEx0Ctrl,COleControl)
INTERFACE_PART(CLabelEx0Ctrl,IID_IPropertyNotifySink,FontUpNotify)
END_INTERFACE_MAP()
STDMETHODIMP_(ULONG) CLabelEx0Ctrl::XFontUpNotify::AddRef()
{
METHOD_PROLOGUE_EX(CLabelEx0Ctrl,FontUpNotify)
return (ULONG)pThis-ExternalAddRef();
}
STDMETHODIMP_(ULONG) CLabelEx0Ctrl::XFontUpNotify::Release()
{
METHOD_PROLOGUE_EX(CLabelEx0Ctrl,FontUpNotify)
return (ULONG)pThis-ExternalRelease();
}
STDMETHODIMP CLabelEx0Ctrl::XFontUpNotify::QueryInterface(REFIID iid,LPVOID FAR* ppvObj)
{
METHOD_PROLOGUE_EX(CLabelEx0Ctrl,FontUpNotify)
return (HRESULT)pThis-ExternalQueryInterface(&iid,ppvObj);
}
STDMETHODIMP CLabelEx0Ctrl::XFontUpNotify::OnChanged(DISPID)
{
METHOD_PROLOGUE_EX(CLabelEx0Ctrl,FontUpNotify)
pThis-InvalidateControl();
return NOERROR;
}
STDMETHODIMP CLabelEx0Ctrl::XFontUpNotify::OnRequestEdit(DISPID)
{
return NOERROR;
}
在CLabelEx0Ctrl类的构造函数代码中,将m_fontUp(&m_xFontNotification)更改为m_fontUp(&m_xFontUpNotify)。
对项目执行完这些更改后,重新生成项目,并使用测试容器测试接口。
例程4:LabelEx.ocx
例程4的控件名称为LabelEx.ocx,它是例程3代码的优化,实现与例程3完全相同的功能。打开Visual C++ 6.0,新建LabelEx工程,接受所有默认设置,完成LabelEx工程创建。完成所有与例程3完全相同的操作,完成后再作如下改动和补充:
向控件头文件(CLabelExCtrl.H)中添加下列代码
DECLARE_INTERFACE_MAP() //声明接口映射
BEGIN_INTERFACE_PART(FontUpNotify,IPropertyNotifySink)
INIT_INTERFACE_PART(CLabelExCtrl,FontUpNotify)
STDMETHOD(OnRequestEdit)(DISPID);
STDMETHOD(OnChanged)(DISPID);
END_INTERFACE_PART(FontUpNotify)
BEGIN_INTERFACE_PART(FontMiddleNotify,IPropertyNotifySink)
INIT_INTERFACE_PART(CLabelExCtrl,FontMiddleNotify)
STDMETHOD(OnRequestEdit)(DISPID);
STDMETHOD(OnChanged)(DISPID);
END_INTERFACE_PART(FontMiddleNotify)
BEGIN_INTERFACE_PART(FontDownNotify,IPropertyNotifySink)
INIT_INTERFACE_PART(CLabelExCtrl,FontDownNotify)
STDMETHOD(OnRequestEdit)(DISPID);
STDMETHOD(OnChanged)(DISPID);
END_INTERFACE_PART(FontDownNotify)
在CLabelExCtrl的实现文件中加入该嵌套类的实现
BEGIN_INTERFACE_MAP(CLabelExCtrl,COleControl)
INTERFACE_PART(CLabelExCtrl,IID_IPropertyNotifySink,FontUpNotify)
INTERFACE_PART(CLabelExCtrl,IID_IPropertyNotifySink,FontMiddleNotify)
INTERFACE_PART(CLabelExCtrl,IID_IPropertyNotifySink,FontDownNotify)
END_INTERFACE_MAP()
STDMETHODIMP_(ULONG) CLabelExCtrl::XFontUpNotify::AddRef()
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontUpNotify)
return (ULONG)pThis-ExternalAddRef();
}
STDMETHODIMP_(ULONG) CLabelExCtrl::XFontUpNotify::Release()
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontUpNotify)
return (ULONG)pThis-ExternalRelease();
}
STDMETHODIMP CLabelExCtrl::XFontUpNotify::QueryInterface(REFIID iid,LPVOID FAR* ppvObj)
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontUpNotify)
return (HRESULT)pThis-ExternalQueryInterface(&iid,ppvObj);
}
STDMETHODIMP CLabelExCtrl::XFontUpNotify::OnChanged(DISPID)
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontUpNotify)
pThis-InvalidateControl();
return NOERROR;
}
STDMETHODIMP CLabelExCtrl::XFontUpNotify::OnRequestEdit(DISPID)
{
return NOERROR;
}
STDMETHODIMP_(ULONG) CLabelExCtrl::XFontMiddleNotify::AddRef()
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontMiddleNotify)
return (ULONG)pThis-ExternalAddRef();
}
STDMETHODIMP_(ULONG) CLabelExCtrl::XFontMiddleNotify::Release()
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontMiddleNotify)
return (ULONG)pThis-ExternalRelease();
}
STDMETHODIMP CLabelExCtrl::XFontMiddleNotify::QueryInterface(REFIID iid,LPVOID FAR* ppvObj)
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontMiddleNotify)
return (HRESULT)pThis-ExternalQueryInterface(&iid,ppvObj);
}
STDMETHODIMP CLabelExCtrl::XFontMiddleNotify::OnChanged(DISPID)
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontMiddleNotify)
pThis-InvalidateControl();
return NOERROR;
}
STDMETHODIMP CLabelExCtrl::XFontMiddleNotify::OnRequestEdit(DISPID)
{
return NOERROR;
}
STDMETHODIMP_(ULONG) CLabelExCtrl::XFontDownNotify::AddRef()
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontDownNotify)
return (ULONG)pThis-ExternalAddRef();
}
STDMETHODIMP_(ULONG) CLabelExCtrl::XFontDownNotify::Release()
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontDownNotify)
return (ULONG)pThis-ExternalRelease();
}
STDMETHODIMP CLabelExCtrl::XFontDownNotify::QueryInterface(REFIID iid,LPVOID FAR* ppvObj)
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontDownNotify)
return (HRESULT)pThis-ExternalQueryInterface(&iid,ppvObj);
}
STDMETHODIMP CLabelExCtrl::XFontDownNotify::OnChanged(DISPID)
{
METHOD_PROLOGUE_EX(CLabelExCtrl,FontDownNotify)
pThis-InvalidateControl();
return NOERROR;
}
STDMETHODIMP CLabelExCtrl::XFontDownNotify::OnRequestEdit(DISPID)
{
return NOERROR;
}
CLabelExCtrl类的构造函数的代码作如下改动
CLabelExCtrl::CLabelExCtrl():m_fontUp(&m_xFontUpNotify),
m_fontMiddle(&m_xFontMiddleNotify),m_fontDown(&m_xFontDownNotify)
{
InitializeIIDs(&IID_DLabelEx, &IID_DLabelExEvents);
// TODO: Initialize your control''s instance data here.
}
对项目执行完这些更改后,重新生成项目,并使用测试容器测试接口。
本人初学VC++,所学粗浅,发表此文,以求共勉,还望方家指教。
e-mail:han_yao_xu@163.com 或 han_yao_xu@126.com。