Back to Amir Israeli Homepage

[CStatusBarWndInsertor : Inserts child windows to StatusBar panes]

This article was contributed by Amir Israeli Amir Israeli.
Amir Israeli Web Site

Environment: Win 95/98 VC6 SP4

CStatusBarWndInsertor class
This is a relatively a simple class , since all the gory details lie within its' base class : CHook : be sure to know what hooks are all about.
The default app wizard creates a simple status bar , In many cases the user adds custom effects to the visual appearence of the status bar : acommon affects are icons in the first pane : progress bars etc. In order to use these affer\cts you define a handler in CFrameWnd derived class : or multiple handlers for several commands. In most cases not only you want to add an action handler but also an update handler.
So we need to add many handlers of type ON_COMMAN( ...) + ON_UPDATE_COMMAND_UI . which is very*10 bad : CFrameWnd grows huge and become a Supermarket of many goods : so many goods that you find none.
The solution of this problem is having multiple classes : all derived from CWnd. Each class responsible for 1 or several goodies : for example a class that draws an icon(even streched icon) , a class for visual text effects and so on. Then you need to set these classes as children of status bar.You need also to keep watching position and sizes of status bars panes so the windows will be positioned in those places. (above the surface of each pane). Ingeneral this is what "CStatusBarWndInsertor" class does : it hooks the Status bar : (any simple status bar) and uses a structure to keep track of inserted windows (pane index+CWnd*)

	class CPaneWnd {
	public:
		CPaneWnd() { nIndex=-1; m_hWnd=NULL; }
		int nIndex;
		HWND m_hWnd;
	};
A single structure is created for each window that is inserted to the status bar. All structures (that are dynamicly created) are destroyed when the status bar destroys or when the class is destroyed .(what comes first) : The class doesn't destroy the CWnd derived classes that are inserted to this class. These structures are saved in a list object which is good enough for status bars (since in most cases there are less than 5 panes: less than 5 windows). (dynamic array may be a better solution since it includes indexes : no searches but : may include empty places) There are very few public functions : the first 2 function set status bar in the object (the create a hook to this status bar). A good place to put it is CFrameWnd-derived-class OnCreate handler. : after creating the status bar.
	BOOL SetStatusBar(CStatusBar *m_pStatusBar);
	BOOL SetStatusBar(HWND m_hStatusBar);
Adding and removing of windows into/from the panes is done with the help of thes functions : after removing a window you need to redraw the status bar : (Invalide it) so changes take effect. As you see RemoveWindow can take 2 types of parameters : index that indicates the index of pane in status bar, or a pointer to the window inserted.
	BOOL AddWindow(CWnd *m_pWnd,int nIndex=-1);
	void RemoveWindow(CWnd *m_pWnd);
	void RemoveWindow(int nIndex=-1);
Several internal functions (helper functions) are used in order to call actions from multiple places : such as a search for a window in some index , removing all items (in destruction of object or status bar : what comes first)
	void RemoveWindow(HWND m_hWnd);
	POSITION FindWindow(HWND m_hWnd);
	POSITION FindWindow(int nIndex=-1);
	void RemoveAt(POSITION pos);
	void RemoveAll();
The functionality of the CHook derived class is located at
	virtual LRESULT WindowProc2(UINT msg, WPARAM wp, LPARAM lp);
	void RepositionWindows();
A look into the WindowProc2 shows a hook of WM_SIZE message : whenever message arrives to Status bar : we first let status bar update its size : by calling the "Next" hook in chain , (the last hook calls original window), Then we set positions of all the windows according the new positions that status bar panes take. (RepositionWindows()) . Another hooked message is WM_DESTROY : to remove all positioned windows when the status bar destroys. (child windows are destroyed too)
 // messages of status bar
LRESULT CStatusBarWndInsertor::WindowProc2(UINT msg, WPARAM wp, LPARAM lp)
{
	LRESULT lr = 0;
	switch (msg)
	{
	case WM_SIZE:
		{
			// update state or size of window
			lr = Next(msg, wp, lp);	
			// re-position all windows in place
			RepositionWindows();
			return lr; // message already given to status bar
		}
		break;

	case WM_DESTROY: 
		// when destroying status bar remove all windows
		RemoveAll();
		break;
	}

	// always give the message to the status bar
	return Next(msg, wp, lp);
}
Repositining of windows is simple : you get the RECT (area in client coordinates) of panes by sending SB_GETRECT message , and then you move the child to proper place :
	if (::SendMessage(m_hHookedWnd,SB_GETRECT,(WPARAM)nIndex,(LPARAM)&rc))
		::MoveWindow(hWnd,rc.left,rc.top,rc.Width(),rc.Height(),TRUE);
How to useCopied from example code:
Create CStatusBarWndInsertor instance within mainframe. then create it within OnCreate handler in .cpp file.
class CMainFrame : public CFrameWnd
{
protected: // create from serialization only
	CMainFrame();

// ...
protected:
	CProgress prg;
	CStatusBarWndInsertor m_statusWndIns;
	CEdit m_wndEdit;
	CButton  m_wndButton;
};
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// ... toolbar stuff

	// creating status bar
	if (!m_wndStatusBar.Create(this) ||  
		!m_wndStatusBar.SetIndicators(indicators,
		  sizeof(indicators)/sizeof(UINT)))
	{
		TRACE0("Failed to create status bar\n");
		return -1;      // fail to create
	}
	
	// status bar created
	
	// creating the window that will be inserted to pane :
	// on the stack : (a progress bar + also activates it)
	prg.Create(ID_PROGRESS,this,CRect(0,0,0,0),TRUE);
	prg.Play();
	prg.SetWindowText("hello to you");

	// creating 2 additional controls as more examples
	m_wndEdit.Create(ES_LEFT|WS_CHILD|WS_VISIBLE,CRect(0,0,1,1),this,IDC_COMBO);
	m_wndButton.Create(_T("Try"),BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE,CRect(0,0,1,1),this,IDC_BUTTON);

	
	/* This is the most importent part : creating the StatusBar hook 
	and add windows to status bar (pane 1,2,3) : windows that previously 
	created : 4 lines and job is done !!!
	The object is supply a self contained windows in order to lighten and
	shorten the implementation of CFrameWnd derived class : a good example for
	a self contained window is CProgress */
	m_statusWndIns.SetStatusBar(&m_wndStatusBar);
	m_statusWndIns.AddWindow(&prg,1);
	m_statusWndIns.AddWindow(&m_wndEdit,2);
	m_statusWndIns.AddWindow(&m_wndButton,3);

	// TODO: Delete these three lines if you don't want the toolbar to
	//  be dockable
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);

	return 0;
}
This picture demonstarate the result of the status bar See my remarks within the code. You shold add handlers in CMainFrame if your classes aren't self contained.


Move to downloads downloads

History

Date Posted: February 3, 2001

Home  |  Links  |  About