Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Multiple Selection based Control Tracker

0.00/5 (No votes)
19 Dec 2001 5  
A CRectTracker derived class that is able to do much more
This article introduces a class, CControlTracker, that does much more than what CRectTracker does. We will take a look at the class design and also learn how to use the class.

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.

Control Tracker

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:
// Controls enumerated in WindowType
	enum WindowType
	{
	enmScrollBar, enmStatic, enmButton, 
	enmEdit, enmListBox, enmComboBox
	};

// Can use enumerated type to create control
	BOOL Create( WindowType wndType, ... );
// Can also use class name to create control
	BOOL Create( LPCTSTR lpszClassName, ... );
// Tracks movement rectangle for control
	void Track( CWnd* pWnd, CPoint point, 
			BOOL bAllowInvert = FALSE, 
			CWnd* pWndClipTo = NULL );
// Sets bounding rectangle for control
	void SetRect( int x1, int y1, int x2, int y2 );
	void SetRect( const CRect & rect );
private:
// Tells weather the control is currently selected
	BOOL m_bSelected;
// Returns handle masks
	UINT GetHandleMask() const;
// Sets default attributes for CRectTracker
	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:
// Creates the Control Tracker Object
	void Create( CWnd* pParentWnd );
// Adds the Control to the Tracker
	void Add( CControlRect* pObject );
// Starts tracking all the controls
	BOOL Track( const CPoint & point, UINT nFlags = 0, 
				BOOL bTrackRubberBand = TRUE );
// Sets the cursor on all the controls
	BOOL SetCursor( UINT nHitTest, UINT message );
// Tells weather the specified control is selected or not
	BOOL IsSelected( CControlRect* pObject ) const;
// Selects the specified control
// Returns TRUE if it was initially unselected else FALSE
	BOOL Select( CControlRect* pObject );
// DeSelects the specified control.
// Returns TRUE if it was initially selected else FALSE
	BOOL DeSelect( CControlRect* pObject );
// Toggles the state of the object 
// Returns TRUE if control was selected, otherwise returns FALSE
	BOOL Toggle( CControlRect* pObject );
// Draws all the controls
	void Draw( CDC* pDC ) const;
// DeSelects all the controls
// Returns the number of controls that were selected
	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:

// Create ControlTracker
m_ControlTracker.Create( this );

// Create Controls
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 ) );

// Add controls in the tracking list of ControlTracker
	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 ease using this class. Please do let me know if you want to suggest any improvements in the design or functionality.

History

  • 20th December, 2001: Initial version

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here