Optimizing Control Drawing
The latest version of this topic can be found at 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
MFC ActiveX Controls: Optimization
COleControl Class
MFC ActiveX Controls
MFC ActiveX Controls
MFC ActiveX Control Wizard
MFC ActiveX Controls: Painting an ActiveX Control