MFC ActiveX コントロール : Windows コントロールのサブクラス化
更新 : 2007 年 11 月
ここでは、Windows コモン コントロールをサブクラス化して ActiveX コントロールを作成する手順について説明します。既存の Windows コントロールをサブクラス化すると、ActiveX コントロールを簡単に開発できます。作成した新しいコントロールでは、サブクラス化した Windows コントロールの機能 (描画やマウス クリックへの応答など) を使用できます。Windows コントロールのサブクラス化の例については、MFC ActiveX コントロールのサンプル BUTTON を参照してください。
Windows コントロールをサブクラス化するには、以下の作業が必要になります。
IsSubclassedControl と PreCreateWindow のオーバーライド
OnDraw メンバ関数の変更
コントロールに返送された ActiveX コントロール メッセージ (OCM) を処理する
メモ : ActiveX コントロール ウィザードの [コントロールの設定] ページの [Select Parent Window Class] ボックスでコントロールをサブクラス化するように指定すると、上記の作業のほとんどが自動的に処理されます。
コントロールのサブクラス化の詳細については、サポート技術情報の「PRB: MFC ActiveX Control with Subclassed ComboBox Does Not Drop Down Correctly in a VB Container (Q243454)」を参照してください。
IsSubclassedControl と PreCreateWindow のオーバーライド
PreCreateWindow と IsSubclassedControl をオーバーライドするには、コントロール クラス宣言の protected セクションに次のコードを追加します。
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
BOOL IsSubclassedControl();
コントロールの実装 (.CPP) ファイルに次のコードを追加して、オーバーライドした 2 つの関数を実装します。
// CMyAxSubCtrl::PreCreateWindow - Modify parameters for CreateWindowEx
BOOL CMyAxSubCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = _T("BUTTON");
return COleControl::PreCreateWindow(cs);
}
// CMyAxSubCtrl::IsSubclassedControl - This is a subclassed control
BOOL CMyAxSubCtrl::IsSubclassedControl()
{
return TRUE;
}
この例では、PreCreateWindow で Windows のボタン コントロールを指定していますが、標準の Windows コントロールならばどれでもサブクラス化できます。標準の Windows コントロールの詳細については、「コントロール」を参照してください。
Windows コントロールをサブクラス化するときには、コントロールのウィンドウの作成に使用するウィンドウ スタイル (WS_) や拡張ウィンドウ スタイル (WS_EX_) のフラグを指定できます。PreCreateWindow メンバ関数でこれらのパラメータの値を設定するには、構造体のフィールド cs.style と cs.dwExStyle を変更します。これらのフィールドを変更するときには、COleControl クラスによって設定される既定のフラグを維持する必要があるため、OR 演算子を使います。たとえば、BUTTON コントロールをサブクラス化したコントロールをチェック ボックスとして表示するには、CSampleCtrl::PreCreateWindow の実装の return ステートメントの前に次のコードを挿入します。
cs.style |= BS_CHECKBOX;
これにより、COleControl クラスの既定のスタイル フラグ (WS_CHILD) を維持したまま、BS_CHECKBOX スタイル フラグが追加されます。
OnDraw メンバ関数の変更
サブクラス化したコントロールを対応する Windows コントロールと同じ外観にするには、次のように、コントロールの OnDraw メンバ関数に DoSuperclassPaint メンバ関数の呼び出しだけを含めるようにします。
void CMyAxSubCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
if (!pdc)
return;
DoSuperclassPaint(pdc, rcBounds);
}
COleControl によって実装される DoSuperclassPaint メンバ関数は、Windows コントロールのウィンドウ プロシージャを使って、指定されたデバイス コンテキストで、外接する四角形の内側にコントロールを描画します。これにより、コントロールは、アクティブでないときにも表示されるようになります。
メモ : |
---|
DoSuperclassPaint メンバ関数を使用できるのは、WM_PAINT メッセージの wParam としてデバイス コンテキストを渡すことができるコントロールだけです。SCROLLBAR や BUTTON などの標準の Windows コントロールやすべてのコモン コントロールで、この関数を使用できます。この動作がサポートされていないコントロールについては、アクティブでないコントロールを正しく表示するためのコードを自分で用意する必要があります。 |
返送されたウィンドウ メッセージの処理
Windows コントロールは、通常は特定のウィンドウ メッセージを親ウィンドウに送ります。このようなメッセージには、ユーザーによる操作を通知するメッセージ (WM_COMMAND など) や、親ウィンドウから情報を取得するメッセージ (WM_CTLCOLOR など) があります。ActiveX コントロールは、一般にこれとは別の方法で親ウィンドウと通信します。通知は、イベントを発生させる (イベント通知を送信する) ことによって行われ、コントロール コンテナに関する情報は、コンテナのアンビエント プロパティにアクセスすることによって取得されます。このような通信手段が使われるため、ActiveX コントロール コンテナでは、コントロールによって送られるウィンドウ メッセージを処理する必要がありません。
COleControl は、サブクラス化した Windows コントロールによって送られるウィンドウ メッセージをコンテナが受け取るのを防ぐために、コントロールの親として機能する補助ウィンドウを作成します。この補助ウィンドウは、"リフレクタ" と呼ばれます。リフレクタが作成されるのは、Windows コントロールをサブクラス化していて、コントロール ウィンドウと同じサイズと位置の ActiveX コントロールの場合だけです。リフレクタ ウィンドウは、特定のウィンドウ メッセージを受け取り、それをコントロールに送り返します。こうして返送されたメッセージは、コントロールのウィンドウ プロシージャで、ActiveX コントロールに適した方法 (イベントを発生させるなど) で処理されます。リフレクタ ウィンドウによって受け取られるウィンドウ メッセージとそれに応じて返送されるメッセージの一覧については、「返送されたウィンドウ メッセージの ID」を参照してください。
ActiveX コントロール コンテナ自体がメッセージを返送するようにデザインされている場合もあります。この場合は、COleControl がリフレクタ ウィンドウを作成する必要がなくなるため、サブクラス化した Windows コントロールの実行時のオーバーヘッドが減少します。COleControl は、MessageReflect アンビエント プロパティの値が TRUE かどうかを確認して、コンテナがメッセージの返送をサポートしているかどうかを検出します。
返送されたウィンドウ メッセージを処理するには、コントロールのメッセージ マップにエントリを追加し、ハンドラ関数を実装します。返送されるメッセージは、Windows によって定義されている標準のメッセージではないため、クラス ビューを使ってメッセージ ハンドラを追加することはできません。ただし、ハンドラはプログラマが簡単に追加できます。
返送されたウィンドウ メッセージのメッセージ ハンドラを直接追加するには、次の手順に従ってください。
コントロール クラスの .H ファイルで、ハンドラ関数を宣言します。関数の戻り値の型に LRESULT を指定し、WPARAM 型と LPARAM 型の 2 つのパラメータを指定します。次に例を示します。
class CMyAxSubCtrl : public COleControl { ... protected: LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam); };
コントロール クラスの .CPP ファイルで、メッセージ マップに ON_MESSAGE エントリを追加します。エントリのパラメータには、メッセージの識別子とハンドラ関数の名前を指定します。次に例を示します。
BEGIN_MESSAGE_MAP(CMyAxSubCtrl, COleControl) ON_MESSAGE(OCM_COMMAND, &CMyAxSubCtrl::OnOcmCommand) END_MESSAGE_MAP()
同じく .CPP ファイルで、返送されたメッセージを処理する OnOcmCommand メンバ関数を実装します。パラメータ wParam と lParam は、元のウィンドウ メッセージと同じです。
返送されたメッセージの処理の例については、MFC ActiveX コントロールのサンプル BUTTON を参照してください。この例では、OnOcmCommand ハンドラが、BN_CLICKED 通知コードを検出し、それに応じて Click イベントを発生させます (送信します)。