YOU CAN CODE!

 

With The Case Of UCanCode.net  Release The Power OF  Visual C++ !   HomeProducts | PurchaseSupport | Downloads  
Download Evaluation
Pricing & Purchase?
E-XD++Visual C++/ MFC Products
Overview
Features Tour 
Electronic Form Solution
Visualization & HMI Solution
Power system HMI Solution
CAD Drawing and Printing Solution

Bar code labeling Solution
Workflow Solution

Coal industry HMI Solution
Instrumentation Gauge Solution

Report Printing Solution
Graphical modeling Solution
GIS mapping solution

Visio graphics solution
Industrial control SCADA &HMI Solution
BPM business process Solution

Industrial monitoring Solution
Flowchart and diagramming Solution
Organization Diagram Solution

Graphic editor Source Code
UML drawing editor Source Code
Map Diagramming Solution

Architectural Graphic Drawing Solution
Request Evaluation
Purchase
ActiveX COM Products
Overview
Download
Purchase
Technical Support
  General Q & A
Discussion Board
Contact Us

Links

Get Ready to Unleash the Power of UCanCode .NET


UCanCode Software focuses on general application software development. We provide complete solution for developers. No matter you want to develop a simple database workflow application, or an large flow/diagram based system, our product will provide a complete solution for you. Our product had been used by hundreds of top companies around the world!

"100% source code provided! Free you from not daring to use components because of unable to master the key technology of components!"


VC++ MFC Example: Create or show Progress Bar/Edit Control/Combobox Control/icon in a status bar
 

 
 

It is based on the technology contributed ("Showing progress bar in a status bar pane") and the amendation. This implementation is done as a set of related MFC classes, with a reasonably clean interface. It also corrects an error in the original code in which an attempt to create a control for a pane that was clipped by the containing frame would create a control at the left of the status bar because the GetItemRect method returns an empty rectangle for this case.

In the above bitmap, the rightmost four buttons create or destroy a control in the status bar. The smiley face cycles through three faces and then destroys the control; the progress bar cycles through five steps of progress bar and then destroys the control. The combo box and edit controls are alternatively created or destroyed. To illustrate what is happening, I log events in the window, a CListView. I also log events such as edit-change and combo-selection-change, a technique I discuss below.

I needed to display a sequence of icons in the status bar to indicate a background thread was running, paused, terminated, etc. Once I saw how this was done, I decided to change several text status panes to also use icons, which meant that it would be easier if I had a C++ class. Having done this for one class, I decided to do it for several. The zipfile includes a general CWnd-derived class, and friend classes for edit, static, progress, and combo controls. You can use these examples to add your own control classes. The basic CWnd-derived class is shown below.

 

Adding a new Control

To add a new control, you must first add a new indicator to your status bar.

Adding a new Indicator

1. Go into the resource editor and add a string, with an ID such as ID_INDICATOR_PROGRESS. Put some spaces or other characters into the string. The length of the string determines the width of the status pane. (My library sets the pane contents to "" so you can use a string like WWWW if you want).

2. In MainFrm.cpp, find the indicators array, whose name is "indicators", and add your new ID to the array in the position you want it to appear in the status bar. It should follow the ID_SEPARATOR line.

3. Add the include files to MainFrm.cpp, ahead of the include of MainFrm.h. If you are using one of the specific classes you will have to include "StatusControl.h" before the desired class, for example:


	#include "StatusControl.h"
	#include "StatusProgress.h"
4. Declare a variable, public or protected, in MainFrm.h, of the appropriate type, based on one of the types in the library (CStatusProgress for a progress bar). For the rest of this example, assume it is called "progressbar".

5. To create the window, you can either create it "on demand" or create it during the "InitInstance" handler, depending on how you need it. When you create it on demand you will typically destroy it when it is not needed.


	progressbar.Create(&m_wndStatusBar, ID_INDICATOR_PROGRESS,
					WS_VISIBLE | PBS_SMOOTH);
Note that my library automatically adds the WS_CHILD flag to the styles. If you have done an on-demand create, you can destroy it by doing

	progressbar.DestroyWindow();
6. To perform operations on the window, just call the normal methods of the superclass, for example:

	progressbar.SetRange(0, 500);
	progressbar.StepIt();
	progressbar.SetPos(value);
7. You must add an OnSize handler to CMainFrame. A typical MDI instance is shown below. In this handler, call the Reposition method for each status bar control you have created:

	void CMainFrame::OnSize(UINT nType, int cx, int cy)
	    {
	     CMDIFrameWnd::OnSize(nType, cx, cy);
	     progressbar.Reposition();
	    }

Updating a control from a thread

If you are showing the progress of a background thread, I've found it best to have the background thread use a user-defined message to notify the main window that it should update the progress bar. There are some interesting conditions that will cause the application to lock up if you try to access the controls directly from another thread...for example, if the GUI thread blocks, any thread that does a SendMessage to it will have to wait for the GUI thread to run again, and this can actually create a deadlock situation.

 

Use a user-defined message.

While the traditional method for defining a user-defined message is to use a symbol based on WM_USER, I've been done in so often by this technique that I only do Registered Window Messages. I'll show both methods here. The problem with WM_USER-based messages is that you do not have a guarantee that your message is unique. If you later write a DLL and don't track the WM_USER numbers you are using, you get conflicts; you can't use DLLs other people have written that use user-defined messages to post to a window; and you can be done in by Microsoft, who has preempted any number of WM_USER messages for their own controls. Note that now programmers are encouraged to use the symbol WM_APP instead of WM_USER, but this only solves the problem of conflicts with Microsoft code, not with DLLs you or other programmers may write. Since a Registered Window Message is no harder to use than a WM_USER/WM_APP-based message, I now use these exclusively.

 

Common to both WM_USER/WM_APP and Registered Window messages:

1. Choose a name for your message. I tend to do names like "UWM_" to indicate a "User WM_" message. For example


	// UWM_STEP_PROGRESS	(WPARAM, LPARAM ignored)
	// UWM_SET_PROGRESS	(WPARAM is value, LPARAM ignored)
1. In MainFrm.h, add a declaration for a handler function in the afx_msg section:

	afx_msg LRESULT OnStepProgress(WPARAM, LPARAM);
	afx_msg LRESULT OnSetProgress(WPARAM, LPARAM);
2. In MainFrm.cpp, add the implementations of the functions:

	LRESULT CMainFrame::OnStepProgress(WPARAM, LPARAM)
	    {
	     progressbar.StepIt();
	     return 0;
	    }
	LRESULT CMainFrame::OnSetProgress(WPARAM wParam, LPARAM)
	    {
	     progressbar.SetPos((int)wParam);
	     return 0;
	    }
4. To invoke the operation from the thread, do a PostMessage (not SendMessage) to the main frame window.

	AfxGetMainWnd()->PostMessage(UWM_STEP_PROGRESS);

Using a WM_USER message

1. Pick a value, such as (WM_APP+103), and define a symbol in an include file.


	#define UWM_STEP_PROGRESS (WM_APP+103)
	#define UWM_SET_PROGRESS  (WM_APP+104)
2. In the module that implements the thread, and in MainFrm.cpp, include this definition file.

3. In the message map for CMainFrame, add a line for each message:


	ON_MESSAGE(UWM_STEP_PROGRESS, OnStepProgress)
	ON_MESSAGE(UWM_SET_PROGRESS, OnSetProgress)

Using a Registered Window Message:

1. Choose a name for the message. I prefer to pick a useful name and then suffix a truly unique ID using GUIDGEN, but that is not critical. Define a string with the name, and put this in a header file, for example:


	#define UWM_STEP_PROGRESS_MESSAGE _T("UWM_STEP_PROGRESS")
	#define UWM_SET_PROGRESS_MESSAGE _T("UWM_SET_PROGRESS")
My names always have a GUIGEN suffix and tend to look like the one below, which guarantees that they will never, ever, under any possible conditions, conflict with any other Registered Window Message from anyone in the Known Universe. (Well, OK, there is a 1 in 2-to-the-63-power chance, or something like that, but this means the chance of this happening within the Heat Death of the Universe are pretty slim).

_T("UWM_STEP_PROGRESS-{152C2190-A98C-11d2-838D-886273000000}")
2. In each module that implements the thread, and in MainFrm.cpp, include this definition file.

3. In each module (including MainFrm.cpp) that uses the symbol, register the message:


	const WORD UWM_STEP_PROGRESS =
                   ::RegisterWindowMessage(UWM_STEP_PROGRESS_MESSAGE);
	const WORD UWM_SET_PROGRESS =
                   ::RegisterWindowMessage(UWM_SET_PROGRESS_MESSAGE);
4. In the message map for CMainFrame, add a line for each message:

	ON_REGISTERED_MESSAGE(UWM_STEP_PROGRESS, OnStepProgress)
	ON_REGISTERED_MESSAGE(UWM_SET_PROGRESS, OnSetProgress)
The static and edit controls (CStatusStatic and CStatusEdit) when created will be assigned the same font as their parent, the status bar control.

Receiving notifications from active controls

For active controls such as the Edit and ComboBox controls, if you want to receive notifications such as EN_CHANGE, CBN_SELCHANGE, etc. you will have to subclass the CStatusBar control with one of your own. You can then create the appropriate handlers for these notifications; typically you will probably reflect them up to the mainframe itself. An example of this is included in the sample code. I chose to handle this by intercepting the WM_COMMAND message and if it was for one of my controls, sending it upwards to the mainframe.

Unfortunately, this is a bit painful because ClassWizard doesn't give you any help. You can use ClassWizard to create a class which is a subclass of CStatusBar, for example, I call mine CActiveStatusBar (for a status bar that has an active control). I then went into the header file and manually added the line shown below in the AFX_VIRTUAL section:


	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CActiveStatusBar)
        virtual BOOL OnCommand(WPARAM, LPARAM);
	//}}AFX_VIRTUAL
Then I went into ActiveStatusBar and added the function which reflects my commands upwards:

BOOL CActiveStatusBar::OnCommand(WPARAM wParam, LPARAM lParam)
    {
     switch(LOWORD(wParam))
	{ /* one of ours? */
	 case ID_INDICATOR_COMBO:
	 case ID_INDICATOR_EDIT:
	    return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);
	} /* one of ours? */

     // Not one of ours
     return CControlBar::OnCommand(wParam, lParam);
    } // CActiveStatusBar::OnCommand
At that point, I went into the MainFrm.cpp file and added the handlers, Unfortunately this really does have to be done "by hand". For example, add to the MainFrm.h file:

	afx_msg void OnSelChangeStatusCombo();
and to the MainFrm.cpp file, add to the message map:

	ON_CBN_SELCHANGE(ID_STATUS_COMBO, OnSelChangeStatusCombo)
and finally add the handler:

void CMainFrame::OnSelchangeStatusCombo()
{
 int n = c_StatusCombo.GetCurSel();
 if(n == CB_ERR)
     return;
 CString s;
 c_StatusCombo.GetLBText(n, s);
 //...do something with the text here
}

Details of the code

Each window that is created is created as a child window of the status bar, and is assigned a control ID which is the same as its pane ID. Note that this means that you cannot create two such windows for the same pane simultaneously, an unlikely thing to actually do.

StatusControl.cpp

The heart of the code is sketched below. The general class is called CStatusControl, and has two critical operations which are declared static so they can be shared with the friend classes. In the code below the boilerplate MFC comments have been dropped.


class CStatusControl : public CWnd
{
public:
        friend class CStatusEdit;
	friend class CStatusProgress;
	friend class CStatusStatic;
	friend class CStatusCombo;
	CStatusControl();
	BOOL Create(LPCTSTR classname, CStatusBar * parent, UINT id, DWORD style);
	void Reposition();
	virtual ~CStatusControl();
protected:
	static void reposition(CWnd * wnd);
	static BOOL setup(CStatusBar * parent, UINT id, CRect & r);
	DECLARE_MESSAGE_MAP()
}
The source code file is shown below, less the MFC boilerplate comments:

#include "stdafx.h"
#include "StatusControl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CStatusControl::CStatusControl(){}

CStatusControl::~CStatusControl(){}

BEGIN_MESSAGE_MAP(CStatusControl, CWnd)
	//{{AFX_MSG_MAP(CStatusControl)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
This function is a static function used by the friend classes to compute the target rectangle. It handles the special case where the pane in question has been clipped by the parent frame; in this case, GetItemRect returns a (0,0,0,0) rectangle. The code below creates a very narrow window off the right end of the status bar. If the proper protocol for the OnSize handler is obeyed, resizing the window to make the pane visible will have the desired effect.

This function returns FALSE if it had to create an off-view window.


BOOL CStatusControl::setup(CStatusBar * parent, UINT id, CRect & r)
    {
     int i = parent->CommandToIndex(id);

     parent->GetItemRect(i, &r);
     parent->SetPaneText(i, "");

     if(r.IsRectEmpty())
	{ /* offscreen */
	 CRect r1;
	 parent->GetWindowRect(&r1); // get parent width
	 r.left = r1.right + 1;
	 r.top =  r1.top;
	 r.right = r1.right + 2;
	 r.bottom = r1.bottom;
	 return FALSE;
	} /* offscreen */

     return TRUE;
    }
This function is a static function which is called by the Reposition method of the friend classes to actually reposition the window. Because the window encodes the pane indicator ID as its control ID, we can easily locate the pane and its rectangle. Interestingly enough, this does not require the same special-case for the empty rectangle, because the window already exists.

void CStatusControl::reposition(CWnd * wnd)
    {
     if(wnd == NULL || wnd->m_hWnd == NULL)
         return;
     UINT id = ::GetWindowLong(wnd->m_hWnd, GWL_ID);
     CRect r;

     CStatusBar * parent = (CStatusBar *)wnd->GetParent();
     int i = parent->CommandToIndex(id);
     parent->GetItemRect(i, &r);
     wnd->SetWindowPos(&wndTop, r.left, r.top, r.Width(), r.Height(), 0);
    }
This function allows you to create a window of an arbitrary window class by specifying its class name and style flags.

BOOL CStatusControl::Create(LPCTSTR classname, CStatusBar * parent, UINT id, DWORD style)
    {
     CRect r;
     setup(parent, id, r);
     return CWnd::Create(classname, NULL, style | WS_CHILD, r, parent, id);
    }
This function is the exported method for repositioning. Note that it calls the protected reposition member to actually do the work.

void CStatusControl::Reposition()
    {
     reposition(this);
    }

A sample friend class: CStatusProgress

Creating the progress control is quite simple; the Create method for the progress control class is shown below. The id passed in is the ID of the pane to be used for the control. The style, for a progress control, can be a combination of the usual WS_ styles (you must explicitly provide WS_VISIBLE, but WS_CHILD will be supplied for you) PBS_SMOOTH, and PBS_VERTICAL.

StatusProgress.h

The header file for the derived CProgressCtrl is quite simple; dropping the MFC boilerplate comments we have the code below.

class CStatusProgress : public CProgressCtrl
{
 public:
	CStatusProgress();
	BOOL Create(CStatusBar * parent, UINT id, DWORD style);
	__inline void Reposition() { CStatusControl::reposition(this); }
	virtual ~CStatusProgress();
	DECLARE_MESSAGE_MAP()
};

StatusProgress.cpp

The code below drops the boilerplate MFC comments.

#include "stdafx.h"
#include "StatusControl.h"
#include "StatusProgress.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CStatusProgress::CStatusProgress() {}

CStatusProgress::~CStatusProgress() {}

BEGIN_MESSAGE_MAP(CStatusProgress, CProgressCtrl)
	//{{AFX_MSG_MAP(CStatusProgress)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CStatusProgress::Create(CStatusBar * parent, UINT id, DWORD style)
    {
     CRect r;
     CStatusControl::setup(parent, id, r);
     return CProgressCtrl::Create(style | WS_CHILD, r, parent, id);
    }

Doing arbitrary drawing

In addition to the classes contained herein, the general Create operation in the CStatusControl class allows you to create a window of an arbitrary class, so you can do custom drawings by using your own window class. You may also choose to do arbitrary drawing by writing your own OnPaint handler for a class you derive from the CStatusStatic class I provide.

Download demo project - 10KB

The source code has been compiled under VC++ 6.0 SP1. No guarantees for any other version of the compiler. It was tested under NT 4.0 SP3.

Download source - 27KB

 

Copyright ?1998-2022 UCanCode.Net Software , all rights reserved.
Other product and company names herein may be the trademarks of their respective owners.

Please direct your questions or comments to webmaster@ucancode.net