Optimizing Control Drawing

When a control is instructed to draw itself into a container-supplied device context, it typically selects GDI objects (such as pens, brushes, and fonts) into the device context, performs its drawing operations, and restores the previous GDI objects. If the container has multiple controls that are to be drawn into the same device context, and each control selects the GDI objects it requires, time can be saved if the controls do not individually restore previously selected objects. After all the controls have been drawn, the container can automatically restore the original objects.

To detect whether a container supports this technique, a control can call the COleControl::IsOptimizedDraw member function. If this function returns TRUE, the control can skip the normal step of restoring the previously selected objects.

Consider a control that has the following (unoptimized) OnDraw function:

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);
}

The pen and brush in this example are local variables, meaning their destructors will be called when they go out of scope (when the OnDraw function ends). The destructors will attempt to delete the corresponding GDI objects. But they should not be deleted if you plan to leave them selected into the device context upon returning from OnDraw.

To prevent the CPen and CBrush objects from being destroyed when OnDraw finishes, store them in member variables instead of local variables. In the control's class declaration, add declarations for two new member variables:

class CMyAxOptCtrl : public COleControl
{


...


   CPen m_pen;
   CBrush m_brush;
};

Then, the OnDraw function can be rewritten as follows:

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);
}

This approach avoids creation of the pen and brush every time OnDraw is called. The speed improvement comes at the cost of maintaining additional instance data.

If the ForeColor or BackColor property changes, the pen or brush needs to be created again. To do this, override the OnForeColorChanged and OnBackColorChanged member functions:

void CMyAxOptCtrl::OnForeColorChanged()
{
   m_pen.DeleteObject();
}

void CMyAxOptCtrl::OnBackColorChanged()
{
   m_brush.DeleteObject();
}

Finally, to eliminate unnecessary SelectObject calls, modify OnDraw as follows:

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);
   }
}

See Also

Reference

COleControl Class

MFC ActiveX Control Wizard

Concepts

MFC ActiveX Controls: Optimization

MFC ActiveX Controls

MFC ActiveX Controls

MFC ActiveX Controls: Painting an ActiveX Control