MFC ActiveX コントロール : フォントの使用
更新 : 2007 年 11 月
ActiveX コントロールでテキストを表示する場合は、フォントのプロパティを変更できるようにすると、コントロールのユーザーがテキストの外観を変更できるようになります。フォントのプロパティは、フォント オブジェクトとして実装され、ストック プロパティとカスタム プロパティの 2 種類に分かれます。ストック フォント プロパティは、あらかじめ実装されているフォント プロパティであり、プロパティ追加 ウィザードを使って追加できます。カスタム フォント プロパティは、あらかじめ実装されていないフォント プロパティで、開発者が動作や使い方を決定します。
ここでは、次のトピックについて説明します。
ストック フォント プロパティの使用
コントロールでのカスタム フォント プロパティの使用
ストック フォント プロパティの使用
ストック フォント プロパティは、COleControl クラスによってあらかじめ実装されています。また、標準のフォント プロパティ ページも用意されています。ユーザーは、このプロパティ ページを使って、フォント オブジェクトのさまざまな属性 (名前、サイズ、スタイルなど) を変更できます。
フォント オブジェクトにアクセスするには、COleControl の関数 GetFont、SetFont、および InternalGetFont を使用します。コントロールのユーザーは、ほかの Get/Set プロパティにアクセスする場合と同様に、GetFont 関数と SetFont 関数を使ってフォント オブジェクトにアクセスします。コントロール内からフォント オブジェクトにアクセスするには、InternalGetFont 関数を使用します。
「MFC ActiveX コントロール : プロパティ」で説明したように、プロパティ追加 ウィザードを使うとストック プロパティを簡単に追加できます。フォント プロパティを選択すると、コントロールのディスパッチ マップにストック フォント プロパティのエントリが自動的に挿入されます。
プロパティ追加 ウィザードを使ってストック フォント プロパティを追加するには
コントロールのプロジェクトを読み込みます。
[クラス ビュー] ウィンドウで、コントロールのライブラリ ノードを展開します。
コントロールのインターフェイス ノード (ライブラリ ノードの 2 番目のノード) を右クリックし、ショートカット メニューを開きます。
ショートカット メニューの [追加] をクリックし、[プロパティの追加] をクリックします。
これにより、プロパティ追加 ウィザードが開きます。
[プロパティ名] ボックスの [Font] をクリックします。
[完了] をクリックします。
コントロール クラスの実装ファイル内にあるコントロールのディスパッチ マップに、次の行が追加されます。
DISP_STOCKPROP_FONT()
さらに、コントロールの .IDL ファイルに次の行が追加されます。
[id(DISPID_FONT)] IFontDisp*Font;
ストック フォント プロパティの情報を使って描画できるテキスト プロパティとしては、たとえば、ストック キャプション プロパティがあります。ストック キャプション プロパティをコントロールに追加するための手順は、ストック フォント プロパティを追加する際の手順に似ています。
プロパティ追加 ウィザードを使ってストック キャプション プロパティを追加するには
コントロールのプロジェクトを読み込みます。
[クラス ビュー] ウィンドウで、コントロールのライブラリ ノードを展開します。
コントロールのインターフェイス ノード (ライブラリ ノードの 2 番目のノード) を右クリックし、ショートカット メニューを開きます。
ショートカット メニューの [追加] をクリックし、[プロパティの追加] をクリックします。
これにより、プロパティ追加 ウィザードが開きます。
[プロパティ名] ボックスの [Caption] をクリックします。
[完了] をクリックします。
コントロール クラスの実装ファイル内にあるコントロールのディスパッチ マップに、次の行が追加されます。
DISP_STOCKPROP_CAPTION()
OnDraw 関数の変更
OnDraw の既定の実装では、コントロールに表示されるすべてのテキストに対して Windows のシステム フォントが使用されます。したがって、デバイス コンテキストにフォント オブジェクトを選択して 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 関数を変更すると、コントロール内のテキストがコントロールのストック フォント プロパティの特性で表示されるようになります。
コントロールでのカスタム フォント プロパティの使用
ActiveX コントロールでは、ストック フォント プロパティだけでなくカスタム フォント プロパティも使用できます。カスタム フォント プロパティを追加するときには、以下の作業が必要になります。
プロパティ追加 ウィザードを使ってカスタム フォント プロパティを実装する。
フォントの通知を処理する。
新しいフォント通知インターフェイスを実装する。
カスタム フォント プロパティの実装
カスタム フォント プロパティを実装するには、プロパティ追加 ウィザードを使ってプロパティを追加し、コードを一部変更します。ここでは、Sample コントロールにカスタム フォント プロパティ HeadingFont を追加する方法について説明します。
プロパティ追加 ウィザードを使ってカスタム フォント プロパティを追加するには
コントロールのプロジェクトを読み込みます。
[クラス ビュー] ウィンドウで、コントロールのライブラリ ノードを展開します。
コントロールのインターフェイス ノード (ライブラリ ノードの 2 番目のノード) を右クリックし、ショートカット メニューを開きます。
ショートカット メニューの [追加] をクリックし、[プロパティの追加] をクリックします。
これにより、プロパティ追加 ウィザードが開きます。
[プロパティ名] ボックスにプロパティ名を入力ます。この例では、「HeadingFont」と入力します。
実装の種類として、[Get/Set メソッド] をクリックします。
[プロパティの種類] ボックスで、プロパティの種類として [IDispatch*] を選択します。
[完了] をクリックします。
プロパティ追加 ウィザードによって、CSampleCtrl クラスと SAMPLE.IDL ファイルにカスタム プロパティ HeadingFont を追加するコードが作成されます。HeadingFont プロパティの実装の種類は Get/Set なので、プロパティの追加ウィザードは、CSampleCtrl クラスのディスパッチ マップを変更して DISP_PROPERTY_EX_IDDISP_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) ファイルに GetHeadingFont 関数と SetHeadingFont 関数の宣言を追加し、コントロールの実装 (.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 関数の呼び出しを追加します。これにより、カスタム フォント プロパティの初期化と永続化が行われます。
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);
カスタム フォント プロパティの実装が完了したら、コントロールのユーザーがコントロールの現在のフォントを変更できるように、標準のフォント プロパティ ページを実装する必要があります。標準のフォント プロパティ ページのプロパティ ページ ID を追加するには、BEGIN_PROPPAGEIDS マクロの後に次の行を挿入します。
PROPPAGEID(CLSID_CFontPropPage)
このほか、BEGIN_PROPPAGEIDS マクロのパラメータ count の値を 1 つ増やす必要があります。たとえば、次のようになります。
BEGIN_PROPPAGEIDS(CMyAxFontCtrl, 2)
変更作業が完了したら、プロジェクト全体をリビルドして追加機能を組み込みます。
フォントの通知の処理
通常は、フォント オブジェクトの特性が変更されたら、それをコントロールに通知する必要があります。各フォント オブジェクトは、COleControl によって実装されている IFontNotification インターフェイスのメンバ関数を呼び出すことにより、変更を通知できます。
コントロールがストック フォント プロパティを使っている場合は、COleControl の OnFontChanged メンバ関数が変更の通知を処理します。カスタム フォント プロパティを追加した場合も、同じ実装を使用できます。前のセクションで取り上げた例では、m_fontHeading メンバ変数を初期化するときに &m_xFontNotification を渡すことによって変更の通知を処理しました。
複数のフォント オブジェクト インターフェイスの実装
上の図の実線は、両方のフォント オブジェクトが同じ IFontNotification の実装を使用していることを表しています。これでは、どちらのフォントが変更されたのかを区別する必要がある場合に問題になります。
コントロールのフォント オブジェクトの通知を区別するには、コントロールの各フォント オブジェクトに個別に IFontNotification インターフェイスを実装する方法があります。この方法を使うと、最後に変更されたフォントを使用している文字列だけを更新することによって、描画コードを最適化できます。次に、2 番目のフォント プロパティに別の通知インターフェイスを実装するための手順を紹介します。前のセクションで追加した HeadingFont プロパティを 2 番目のフォント プロパティとして使用します。
新しいフォント通知インターフェイスの実装
複数のフォントの通知を区別するには、コントロールで使用する各フォントごとに新しい通知インターフェイスを実装する必要があります。この後のセクションでは、コントロールのヘッダー ファイルと実装ファイルを変更して新しいフォント通知インターフェイスを実装する方法について説明します。
ヘッダー ファイルへの追加
コントロールのヘッダー (.H) ファイルで、クラス宣言に次の行を追加します。
protected:
BEGIN_INTERFACE_PART(HeadingFontNotify, IPropertyNotifySink)
INIT_INTERFACE_PART(CMyAxFontCtrl, HeadingFontNotify)
STDMETHOD(OnRequestEdit)(DISPID);
STDMETHOD(OnChanged)(DISPID);
END_INTERFACE_PART(HeadingFontNotify)
これにより、HeadingFontNotify という名前の IPropertyNotifySink インターフェイスの実装が作成されます。この新しいインターフェイスには、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;
}
IPropertyNotifySink インターフェイスの AddRef メソッドと Release メソッドは、ActiveX コントロール オブジェクトの参照カウントを追跡します。コントロールは、インターフェイス ポインタにアクセスしたときに、AddRef を呼び出して参照カウントを増分します。また、グローバル メモリ ブロックを解放するために GlobalFree を呼び出すのと同様に、ポインタを使い終わると Release を呼び出します。インターフェイスの参照カウントが 0 になると、インターフェイスの実装を解放できます。この例では、QueryInterface 関数が、特定のオブジェクトの IPropertyNotifySink インターフェイスへのポインタを返します。ActiveX コントロールは、この関数を使って、オブジェクトがサポートしているインターフェイスを問い合わせることができます。
変更作業が完了したら、プロジェクトをリビルドし、テスト コンテナを使ってインターフェイスをテストします。テスト コンテナへのアクセス方法については、「テスト コンテナでのプロパティとイベントのテスト」を参照してください。