Optimizing Control Drawing
| Overview | How Do I | FAQ | | Sample
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 of 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 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 CMyCtrl::OnDraw(CDC* pdc, CRect& rcBounds, CRect& rcInvalid)
{
CPen pen(PS_SOLID, 0, TranslateColor(GetForeColor()));
CBrush brush(TranslateColor(GetBackColor()));
CPen* pPenSave = pdc->SelectObject(&pen);
CBrush* pBrushSave = pdc->SelectObject(&brush);
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 and 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 CMyCtrl : public COleControl
{
.
.
.
CPen m_pen;
CBrush m_brush;
}
Then, the OnDraw
function can be rewritten as follows:
void CMyCtrl::OnDraw(CDC* pdc, CRect& rcBounds, 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);
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 and member functions:
void CMyCtrl::OnForeColorChanged()
{
m_pen.DeleteObject();
}
void CMyCtrl::OnBackColorChanged()
{
m_brush.DeleteObject();
}
Finally, to eliminate unnecessary SelectObject calls, modify OnDraw
as follows:
void CMyCtrl::OnDraw(CDC* pdc, CRect& rcBounds, 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);
Rectangle(rcBounds);
if (! IsOptimizedDraw())
{
pdc->SelectObject(pPenSave);
pdc->SelectObject(pBrushSave);
}
}
See Also , Serialization, Basic Components of an ActiveX Control, Create a Program with the MFC ActiveX ControlWizard, ActiveX Controls: Painting an ActiveX Control