更新 : 2007 年 11 月
ここでは、ActiveX コントロールの描画プロセスと、コードを変更して描画プロセスを最適化する方法について説明します。前に選択された GDI オブジェクトをコントロールが個別に復元しないようにすることによって描画を最適化する方法については、「コントロールの描画の最適化」を参照してください。この場合は、すべてのコントロールの描画が終了すると、コンテナによって元のオブジェクトが自動的に復元されます。
ここでは、MFC ActiveX コントロール ウィザードの既定の設定で作成されるコントロールを例として使用します。MFC ActiveX コントロール ウィザードを使ってスケルトン コントロール アプリケーションを作成する方法については、「MFC ActiveX コントロール ウィザード」を参照してください。
以下の内容について説明します。
コントロールの描画プロセスと描画をサポートするために ActiveX コントロール ウィザードが作成するコード
描画プロセスを最適化する方法
メタファイルを使ってコントロールを描画する方法
ActiveX コントロールの描画プロセス
ActiveX コントロールが初めて表示されるときや再描画されるときの描画プロセスは、MFC を使って開発されたほかのアプリケーションの場合とほぼ同様ですが、1 つだけ重要な違いがあります。それは、ActiveX コントロールにはアクティブな状態とアクティブでない状態があるということです。
アクティブなコントロールは、ActiveX コントロール コンテナに子ウィンドウとして表示されます。また、その他のウィンドウと同様に、WM_PAINT メッセージを受け取ると自身を描画します。このメッセージは、コントロールの基本クラスである COleControl によって、OnPaint 関数で処理されます。既定の実装では、コントロールの OnDraw 関数が呼び出されます。
アクティブでないコントロールでは、描画方法が異なります。コントロールがアクティブでない場合、コントロールのウィンドウは表示されていないかまたは存在しないため、描画メッセージを受け取ることができません。このため、代わりにコントロール コンテナが直接コントロールの OnDraw 関数を呼び出します。このプロセスは、OnPaint メンバ関数が呼び出されないという点で、アクティブなコントロールの描画プロセスとは異なります。
このように、ActiveX コントロールの更新方法は、コントロールの状態によって異なります。ただし、OnDraw メンバ関数はいずれの場合にも呼び出されるため、通常は描画コードの大半をこのメンバ関数内で記述します。
OnDraw メンバ関数は、コントロールの描画を処理します。コントロールがアクティブでない場合は、コントロール コンテナが、コントロール コンテナのデバイス コンテキストとコントロールが占めている四角形領域の座標を引数として、OnDraw を呼び出します。
フレームワークによって OnDraw メンバ関数に渡される四角形には、コントロールが占めている領域が含まれています。コントロールがアクティブな場合、左上隅の座標は (0, 0) になり、コントロールを格納している子ウィンドウのデバイス コンテキストが渡されます。コントロールがアクティブでない場合、左上隅の座標は (0, 0) になるとは限りません。また、渡されるデバイス コンテキストは、そのコントロールがあるコントロール コンテナのデバイス コンテキストになります。
![]() |
---|
OnDraw を変更する場合は、四角形の左上隅が常に (0, 0) とは限らないことに注意しておく必要があります。また、描画は、OnDraw に渡される四角形の中だけで行うようにしてください。四角形の領域外に描画すると、予想外の結果になる可能性があります。 |
MFC ActiveX コントロール ウィザードによってコントロールの実装ファイル (.CPP) に記述される既定の実装を次に示します。白いブラシで四角形が描画され、現在の背景色で楕円が塗りつぶされます。
void CMyAxUICtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}
![]() |
---|
コントロールを描画するときは、OnDraw 関数にパラメータ pdc として渡されるデバイス コンテキストの状態を考慮に入れないでください。デバイス コンテキストは、コンテナ アプリケーションによって提供される場合もあるため、必ずしも既定の状態に初期化されるとは限りません。特に、描画コードが依存するペン、ブラシ、色、フォントなどのリソースは明示的に選択するようにしてください。 |
描画コードの最適化
コントロールの自己描画が正しく行われるようになったら、次に OnDraw 関数を最適化します。
ActiveX コントロールの既定の描画では、コントロール領域全体が描画されます。単純なコントロールではこのままでも問題ありませんが、通常は、コントロール全体を描画するよりも、更新が必要な部分だけを再描画する方が時間を短縮できます。
OnDraw 関数の描画動作は、再描画が必要な四角形の領域を rcInvalid として渡すだけで、簡単に最適化できます。この領域は、通常コントロール領域全体より小さくなるため、描画プロセスが高速化されます。
メタファイルを使ったコントロールの描画
OnDraw 関数のパラメータ pdc は、ほとんどの場合、画面のデバイス コンテキスト (DC: device context) を指します。ただし、コントロールのイメージを印刷する場合や、印刷プレビュー セッションの間などには、"メタファイル DC" と呼ばれる特別な種類の DC が描画のために渡されます。画面 DC では、受信した要求はすぐに処理されますが、メタファイル DC では後で再生するために格納されます。一部のコンテナ アプリケーションでも、デザイン モードでメタファイル DC を使ってコントロール イメージを描画します。
コンテナは、IViewObject::Draw と IDataObject::GetData の 2 つのインターフェイス関数を使用して、メタファイルの描画を要求できます。IViewObject::Draw は、メタファイル以外の描画要求にも使用できます。パラメータの 1 つとしてメタファイル DC を渡すと、MFC フレームワークによって COleControl::OnDrawMetafile が呼び出されます。これは仮想メンバ関数であるため、特別な処理を行うには、コントロール クラスでオーバーライドします。既定の動作では、COleControl::OnDraw が呼び出されます。
コントロールを画面デバイス コンテキストとメタファイル デバイス コンテキストの両方で描画できるようにするには、両方のデバイス コンテキストでサポートされているメンバ関数だけを使用します。また、座標系がピクセル単位でないことも必要です。
OnDrawMetafile の既定の実装では、コントロールの OnDraw 関数が呼び出されます。OnDrawMetafile をオーバーライドしない場合は、メタファイル デバイス コンテキストと画面デバイス コンテキストの両方に対応したメンバ関数だけを使用するようにしてください。次の表は、メタファイル デバイス コンテキストと画面デバイス コンテキストの両方で使用できる CDC のメンバ関数の一覧です。これらの関数の詳細については、『MFC リファレンス』の「CDC」クラスを参照してください。
Arc |
BibBlt |
Chord |
---|---|---|
Ellipse |
Escape |
ExcludeClipRect |
ExtTextOut |
FloodFill |
IntersectClipRect |
LineTo |
MoveTo |
OffsetClipRgn |
OffsetViewportOrg |
OffsetWindowOrg |
PatBlt |
Pie |
Polygon |
Polyline |
PolyPolygon |
RealizePalette |
RestoreDC |
RoundRect |
SaveDC |
ScaleViewportExt |
ScaleWindowExt |
SelectClipRgn |
SelectObject |
SelectPalette |
SetBkColor |
SetBkMode |
SetMapMode |
SetMapperFlags |
SetPixel |
SetPolyFillMode |
SetROP2 |
SetStretchBltMode |
SetTextColor |
SetTextJustification |
SetViewportExt |
SetViewportOrg |
SetWindowExt |
SetWindowORg |
StretchBlt |
TextOut |
|
CDC のメンバ関数のほかにも、メタファイル DC に対応している関数がいくつかあります。たとえば、CPalette::AnimatePalette、CFont::CreateFontIndirect、および CBrush の 3 つのメンバ関数 CreateBrushIndirect、CreateDIBPatternBrush、および CreatePatternBrush がこれに当たります。
一方、DrawFocusRect、DrawIcon、DrawText、ExcludeUpdateRgn、FillRect、FrameRect、GrayString、InvertRect、ScrollDC、および TabbedTextOut は、メタファイルに記録されません。メタファイル DC は、実ときにデバイスに関連付けられているわけではないので、SetDIBits、GetDIBits、および CreateDIBitmap では使用できません。SetDIBitsToDevice および StretchDIBits では、出力先としてメタファイル DC を使用できます。CreateCompatibleDC、CreateCompatibleBitmap、および CreateDiscardableBitmap では、メタファイル DC を使ってもあまり意味がありません。
メタファイル DC を使用するときは、ピクセル単位の座標系は使えません。このため、パラメータ rcBounds で OnDraw に渡される四角形に合わせて、描画コード全体を調整する必要があります。rcBounds はコントロールのウィンドウのサイズを表すため、コントロールの領域外の描画を防ぐことができます。
コントロールのメタファイル描画を実装した後に、テスト コンテナを使ってメタファイルをテストします。テスト コンテナへのアクセス方法については、「テスト コンテナでのプロパティとイベントのテスト」を参照してください。
テスト コンテナを使ってコントロールのメタファイルをテストするには
テスト コンテナの [編集] メニューの [新しいコントロールを挿入] をクリックします。
[新しいコントロールを挿入] ボックスでコントロールを選択し、[OK] をクリックします。
コントロールがテスト コンテナに表示されます。
[コントロール] メニューの [メタファイルの描画] をクリックします。
別のウィンドウにメタファイルが表示されます。このウィンドウのサイズを変更すると、拡大縮小がコントロールのメタファイルにどのように影響するのかを確認できます。このウィンドウはいつでも閉じることができます。