コントロールの描画の最適化
コンテナーが提供するデバイス コンテキストに描画を行う場合、コントロールは通常、デバイス コンテキストに GDI オブジェクト (ペン、ブラシ、フォントなど) を選択して描画を行い、描画が終わると前の GDI オブジェクトを復元します。 同じデバイス コンテキストに描画されるコントロールがコンテナーに複数あり、各コントロールがそれぞれ必要な GDI オブジェクトを選択する場合は、前に選択されたオブジェクトを各コントロールが個別に復元しないようにすると、時間を節約できます。 この場合は、すべてのコントロールの描画が終了すると、コンテナーによって元のオブジェクトが自動的に復元されます。
この機能がコンテナーでサポートされているかどうかを調べるには、コントロールで COleControl::IsOptimizedDraw メンバー関数を呼び出します。 TRUE が返された場合は、前に選択されたオブジェクトを復元するための通常の手順を省略できます。
例として、次のような、最適化されていない OnDraw 関数を持つコントロールについて考えてみます。
void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
CPen pen(PS_SOLID, 0, TranslateColor(GetForeColor()));
CBrush brush(TranslateColor(GetBackColor()));
CPen* pPenSave = pdc->SelectObject(&pen);
CBrush* pBrushSave = pdc->SelectObject(&brush);
pdc->Rectangle(rcBounds);
pdc->SelectObject(pPenSave);
pdc->SelectObject(pBrushSave);
}
この例のペンとブラシはローカル変数なので、スコープの外に出ると (OnDraw 関数が終了すると)、それぞれのデストラクターが呼び出されます。 呼び出されたデストラクターは、対応する GDI オブジェクトを削除しようとします。 OnDraw から戻ったときにこれらの GDI オブジェクトがデバイス コンテキストに選択されたままにするには、削除されないようにする必要があります。
OnDraw の終了時に CPen オブジェクトと CBrush オブジェクトが破棄されないようにするには、これらのオブジェクトをローカル変数ではなくメンバー変数に格納します。 コントロールのクラス宣言に、次のような 2 つの新しいメンバー変数の宣言を追加します。
class CMyAxOptCtrl : public COleControl
{
...
CPen m_pen;
CBrush m_brush;
};
次に、OnDraw 関数を次のように書き換えます。
void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
CPen pen(PS_SOLID, 0, TranslateColor(GetForeColor()));
CBrush brush(TranslateColor(GetBackColor()));
CPen* pPenSave = pdc->SelectObject(&pen);
CBrush* pBrushSave = pdc->SelectObject(&brush);
pdc->Rectangle(rcBounds);
pdc->SelectObject(pPenSave);
pdc->SelectObject(pBrushSave);
}
この方法を使うと、OnDraw が呼び出されるたびにペンとブラシが作成されることがなくなります。 ただし、描画にかかる時間は短縮されますが、管理するインスタンス データは増えます。
ForeColor プロパティまたは BackColor プロパティが変更された場合は、ペンまたはブラシを作成し直す必要があります。 この場合は、OnForeColorChanged メンバー関数と OnBackColorChanged メンバー関数を次のようにオーバーライドします。
void CMyAxOptCtrl::OnForeColorChanged()
{
m_pen.DeleteObject();
}
void CMyAxOptCtrl::OnBackColorChanged()
{
m_brush.DeleteObject();
}
最後に、不要な SelectObject の呼び出しを取り除くために、OnDraw を次のように変更します。
void CMyAxOptCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
if (m_pen.m_hObject == NULL)
m_pen.CreatePen(PS_SOLID, 0, TranslateColor(GetForeColor()));
if (m_brush.m_hObject == NULL)
m_brush.CreateSolidBrush(TranslateColor(GetBackColor()));
CPen* pPenSave = pdc->SelectObject(&m_pen);
CBrush* pBrushSave = pdc->SelectObject(&m_brush);
pdc->Rectangle(rcBounds);
if (! IsOptimizedDraw())
{
pdc->SelectObject(pPenSave);
pdc->SelectObject(pBrushSave);
}
}