Introduction
Often in developing designer and CAD related applications, one needs
the functionality of moving and resizing selected objects around.
MFC run-time provides one class i.e. CRectTracker that does something
like this, but it has several shortcomings, the most important one
being that it does not have functionality for multiple object selection
and resizing. This article introduces a class, CControlTracker that
does much more than what CRectTracker does.

It inherits from
CRectTracker, and essentially has the same
interface as
CRectTracker.
CControlTracker works closely with
another class
CControlRect, which inherits from
CRectTracker
and
CWnd. This means that you can use
CControlRect to create
any control (by calling
CWnd::Create) and add in the list of
controls managed by
CControlTracker. Then by simply overriding
OnLButtonDown and
OnSetCursor, you can get all the advanced
functionality of moving/resizing controls:
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point)
{
m_ControlTracker.Track( point , nFlags , true );
CFrameWnd::OnLButtonDown(nFlags, point);
}
BOOL CMainFrame::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if( m_ControlTracker.SetCursor( nHitTest, message ) )
return TRUE;
return CFrameWnd::OnSetCursor(pWnd, nHitTest, message);
}
Class Design
CControlRect inherits from
CWnd, so you can pass
CControlRect*
wherever
CWnd* is expected. It also inherits from
CRectTracker.
This is to allow every Control to "Track" itself. You can safely
create all controls including buttons, static and ListBox controls
and even activex controls, just as the way you would do with
CWnd.
class CControlRect : public CRectTracker , public CWnd {
public:
enum WindowType
{
enmScrollBar, enmStatic, enmButton,
enmEdit, enmListBox, enmComboBox
};
BOOL Create( WindowType wndType, ... );
BOOL Create( LPCTSTR lpszClassName, ... );
void Track( CWnd* pWnd, CPoint point,
BOOL bAllowInvert = FALSE,
CWnd* pWndClipTo = NULL );
void SetRect( int x1, int y1, int x2, int y2 );
void SetRect( const CRect & rect );
private:
BOOL m_bSelected;
UINT GetHandleMask() const;
void Initialize();
...
};
CControlTracker manages all objects of
CControlRect class. It keeps an
array of all controls added by a call to
CControlTracker::Add. It also
keeps an array of currently selected objects. Handles are only drawn
for selected objects.
class CControlTracker : public CRectTracker
{
public:
void Create( CWnd* pParentWnd );
void Add( CControlRect* pObject );
BOOL Track( const CPoint & point, UINT nFlags = 0,
BOOL bTrackRubberBand = TRUE );
BOOL SetCursor( UINT nHitTest, UINT message );
BOOL IsSelected( CControlRect* pObject ) const;
BOOL Select( CControlRect* pObject );
BOOL DeSelect( CControlRect* pObject );
BOOL Toggle( CControlRect* pObject );
void Draw( CDC* pDC ) const;
int DeSelectAll();
...
};
How to use it
Create instances of
CControlTracker and
CControlRect (controls) in your
class. I have created controls in my application window class:
class CMainFrame : public CFrameWnd
{
CControlTracker m_ControlTracker;
CControlRect r1, r2;
...
};
Override
OnLButtonDown,
OnSetCursor,
OnDraw and
OnCreate. In the
OnCreate member of the Parent class, add the following code:
m_ControlTracker.Create( this );
r1.Create( CControlRect::enmStatic, "Static Text Control",
this , SS_CENTERIMAGE | SS_BITMAP, CRect( 10,10,70,70 ) );
r2.Create( CControlRect::enmEdit, "Button Control",
this, WS_DISABLED, CRect( 160,110,300,250 ) );
m_ControlTracker.Add( &r1 );
m_ControlTracker.Add( &r2 );
Future Work
In the near future, I would be working on incorporating
LineTracker
functionality into this tracker. You would be able to make a cool
UML editor with quite ease using this class. Please do let me know if
you want to suggest any improvements in the design or functionality.