Updating Progress Bar

This is a discussion on Updating Progress Bar within the Windows Programming forums, part of the Platform Specific Boards category; Hi, I try to send messages to update the progress bar as it counts to 100, but the progress bar ...

  1. #1
    Registered User
    Join Date
    Mar 2011
    Posts
    211

    Updating Progress Bar

    Hi, I try to send messages to update the progress bar as it counts to 100, but the progress bar does not update, and then it says the program is not responding.

    Code:
    #include <windows.h>
    #include <commctrl.h>
    #include "progress_bar.h"
    
    #define length 100
    
    //---------------------------------------------------------------------------
    HWND hWnd;
    HINSTANCE hInst;
    LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    //---------------------------------------------------------------------------
    INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    				   LPSTR lpCmdLine, int nCmdShow)
    {
    	hInst = hInstance;
    
    	DialogBox(hInst, MAKEINTRESOURCE(IDD_CONTROLS_DLG),
    	          hWnd, reinterpret_cast<DLGPROC>(DlgProc));
    
    	return FALSE;
    }
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg,
    		       WPARAM wParam, LPARAM lParam)
    {
    	INITCOMMONCONTROLSEX InitCtrlEx;
    
    	InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
    	InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;
    	InitCommonControlsEx(&InitCtrlEx);
    
    	switch(Msg)
    	{
    	case WM_INITDIALOG:
    		CreateWindowEx(0, PROGRESS_CLASS, NULL,
    		               WS_CHILD | WS_VISIBLE | PBS_SMOOTH ,
    			      20, 20, 260, 17,
    			      hWndDlg, NULL, hInst, NULL);
    			      
    		return TRUE;
    
    	case WM_COMMAND:
    		switch(wParam)
    		{
    		case IDOK: 
            {
                int pb_pos;
                SendMessage(hWndDlg, PBM_SETRANGE, 0, MAKELPARAM(0, length ));
    			while(pb_pos != length)
                {           
                            pb_pos = SendMessage(hWndDlg, PBM_GETPOS, 0, 0);
                            pb_pos++;
                            SendMessage(hWndDlg, PBM_SETPOS, pb_pos, 0);
                }
    			return TRUE;
            }
    		case IDCANCEL:
            {
    			EndDialog(hWndDlg, 0);
    			return TRUE;}
    		}
    		break;
    	}
    
    	return FALSE;
    }
    Code:
    #define IDD_CONTROLS_DLG 100
    Code:
    #include "progress_bar.h"
    #include <afxres.h>
    
    IDD_CONTROLS_DLG DIALOG 260, 200, 200, 120
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "Windows Controls"
    FONT 8, "MS Shell Dlg"
    BEGIN
        DEFPUSHBUTTON   "Load", IDOK, 30, 100, 50, 14
        DEFPUSHBUTTON   "Close", IDCANCEL, 120, 100, 50, 14
    END
    Thanks for the help

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    All the stuff to intialize common controls should be moved to your windmain function.

    As it is now you are reintializing them every time a message is passed to your dialog proc.

    Usually progress bars are updated on a timer...

  3. #3
    Registered User
    Join Date
    Mar 2011
    Posts
    211
    Ok, so I moved this
    Code:
        INITCOMMONCONTROLSEX InitCtrlEx;       InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);     InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;     InitCommonControlsEx(&InitCtrlEx);
    to WinMain, same results.

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Ok, now you're creating a progress bar in WM_INITDIALOG, which is ok, but you're not saving the handle returned by create window so you can send messages to it. Sending messages to the dialog itself (hwndDlg ?) won't update the progress bar. You have to send them directly to the control itself.


    But then...
    You're already using resources to create the buttons etc.
    You should include the progress bar there as well...

    Then you can use SendDlgItemMessage() to the controls ID instead of it's handle.
    Last edited by CommonTater; 12-11-2011 at 06:55 PM.

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,854
    Controls created with the WS_CHILD style should have the HMENU param set to to a unique ID number (not NULL).

    You can then use this ID number to send msgs to the control.

    Code:
    //in resource.h or other global scope file
    //add define for progressbar ctrl
    #define IDC_PROGBAR 40001
    
    //in WM_INIT
    //create progressbar with ID number
    CreateWindowEx(0, PROGRESS_CLASS, NULL, 
                           WS_CHILD | WS_VISIBLE | PBS_SMOOTH , 
                      20, 20, 260, 17, 
                      hWndDlg, (HMENU)IDC_PROGBAR, hInst, NULL); 
    
    //Use progressbars ID number to get it's HWND
    hWndPB = GetDlgItem(hWndDlg, IDC_PROGBAR);
    
    //and use this HWND to send it msgs
    pb_pos = SendMessage(hWndPB , PBM_GETPOS, 0, 0); 
    
    //or combine into one line
    SendMessage(GetDlgItem(hWndDlg, IDC_PROGBAR), PBM_SETPOS, pb_pos, 0);
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by novacain View Post
    Controls created with the WS_CHILD style should have the HMENU param set to to a unique ID number (not NULL).

    You can then use this ID number to send msgs to the control.

    Code:
    //in resource.h or other global scope file
    //add define for progressbar ctrl
    #define IDC_PROGBAR 40001
    
    //in WM_INIT
    //create progressbar with ID number
    CreateWindowEx(0, PROGRESS_CLASS, NULL, 
                           WS_CHILD | WS_VISIBLE | PBS_SMOOTH , 
                      20, 20, 260, 17, 
                      hWndDlg, (HMENU)IDC_PROGBAR, hInst, NULL); 
    
    //Use progressbars ID number to get it's HWND
    hWndPB = GetDlgItem(hWndDlg, IDC_PROGBAR);
    
    //and use this HWND to send it msgs
    pb_pos = SendMessage(hWndPB , PBM_GETPOS, 0, 0); 
    
    //or combine into one line
    SendMessage(GetDlgItem(hWndDlg, IDC_PROGBAR), PBM_SETPOS, pb_pos, 0);
    Or as a third option, since CreateWindow returns a window handle...
    Code:
    hwndpb = CreateWindowEx(0, PROGRESS_CLASS, NULL, 
                           WS_CHILD | WS_VISIBLE | PBS_SMOOTH , 
                      20, 20, 260, 17, 
                      hWndDlg, (HMENU)IDC_PROGBAR, hInst, NULL);
    OR you can do this...

    Code:
    // send progress bar message
    SendDlgItemMessage(hwndDlg, IDC_PROGBAR,Message,Wparam,Lparam);

  7. #7
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,854
    If you use the HWND returned by the CreateWindowEx() make sure it is declared as a STATIC HWND, or the value will be lost once the WM_INIT msgs completes (and not be valid for any other msgs).
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by novacain View Post
    If you use the HWND returned by the CreateWindowEx() make sure it is declared as a STATIC HWND, or the value will be lost once the WM_INIT msgs completes (and not be valid for any other msgs).
    Yes, good suggestion... although I generally keep an array of HWNDs at the page-global scope so I have them handy...

    Code:
    // after includes and defines
    
    HWND Handles[10];
    Makes life a little bit easier.

  9. #9
    Registered User
    Join Date
    Mar 2011
    Posts
    211
    So I can choose to access it through a handle returned by CreateWindowEx. Or by an ID (like 105) set as something like IDC_PROGBAR.

    Before I was trying to update it via the dialog window handle.

    But where do I put the messages that I want to send to the Progress bar to update it? Can they be put anywhere? For example, let's say I want it to update when you click the button load.
    Last edited by binks; 12-12-2011 at 09:54 AM.

  10. #10
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Then you would put the messages in the "click" handler for your button.

    Although, it's far more common to do that with a timer, so that it appears to be updating on it's own.

  11. #11
    Registered User
    Join Date
    Mar 2011
    Posts
    211
    And how would you do it with a timer? And if you wanted to load actual files, it wouldn't be in a loop, it would just run through the code like so.
    Code:
    //Load database
    
    //Then advance progress bar
    
    //Load savegame
    
    //Advance progress bar - and so forth
    I now changed the code to this, which works. How do you get the fancy styles for the progress bar colour, like that fancy green for Win7? Also, after I click load, and it finishes, it says it's not responding and crashes. Is it not ending properly?

    Code:
    #include <windows.h>
    #include <commctrl.h>
    #include "progress_bar.h"
    
    #define length 100000
    
    //---------------------------------------------------------------------------
    HWND hWnd;
    HINSTANCE hInst;
    LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    HWND hwndpb;
    //---------------------------------------------------------------------------
    INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    				   LPSTR lpCmdLine, int nCmdShow)
    {
    	hInst = hInstance;
    
    	DialogBox(hInst, MAKEINTRESOURCE(IDD_CONTROLS_DLG),
    	          hWnd, reinterpret_cast<DLGPROC>(DlgProc));
    	          
        INITCOMMONCONTROLSEX InitCtrlEx;
    
    	InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
    	InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;
    	InitCommonControlsEx(&InitCtrlEx);
    
    	return FALSE;
    }
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg,
    		       WPARAM wParam, LPARAM lParam)
    {
    
    	switch(Msg)
    	{
    	case WM_INITDIALOG:
    		hwndpb = CreateWindowEx(0, PROGRESS_CLASS, NULL,
    		               WS_CHILD | WS_VISIBLE | PBS_SMOOTH ,
    			      20, 20, 260, 17,
    			      hWndDlg, (HMENU)IDC_PROGBAR, hInst, NULL);
    			      
    		return TRUE;
    
    	case WM_COMMAND:
    		switch(wParam)
    		{
    		case IDLOAD: 
            {
                int pb_pos;
                SendMessage(hwndpb, PBM_SETRANGE, 0, MAKELPARAM(0, length ));
    			while(pb_pos != length)
                {           
                            pb_pos = SendMessage(hwndpb, PBM_GETPOS, 0, 0);
                            pb_pos++;
                            SendMessage(hwndpb, PBM_SETPOS, pb_pos, 0);
                }
    			return TRUE;
            }                break;
    		case IDCANCEL:
            {
    			EndDialog(hWndDlg, 0);
    			return TRUE;}
    		}
    		break;
    	}
    
    	return FALSE;
    }

  12. #12
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    In your last code sample... Lines 21 to 25 should be above line 17... there's no point intializing comctrls AFTER the dialog returns.

    To do this on a timer so that the progress bar travels on it's own you will need to start a timer in your WM_INITDIALOG function right after creating the progress bar. Then you use SetTimer() to start it counting and use the WM_TIMER message to update your status bar. Udating twice a second is usually adequat for short operations, once a second for long ones (more than a minute).

    Also be aware that tight loops like the one in lines 51 to 56 are a no-no in Windows. They will actually stop a program from multitasking and may lag the entire system, since they don't give up time slices.

    As for visual styles you need to include a Manifest in your program's resources... The one below is usually sufficient.
    You will need to change the name of the program (filename without extension), Version and Description fields to suit your app.
    If your program is 64bit change "x86" to "amd64"

    PHP Code:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <assemblyIdentity type="win32"
                        name="MyProgram"
                        version="1.0.0.0"
                        processorArchitecture="X86" />
      <description>
        My Program Description 
      </description>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type="win32"
                            name="Microsoft.Windows.Common-Controls"
                            version="6.0.0.0"
                            processorArchitecture="X86"
                            publicKeyToken="6595b64144ccf1df"
                            language="*" />
        </dependentAssembly>
      </dependency>
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
          <requestedPrivileges>
            <requestedExecutionLevel  level="AsInvoker" 
                                      uiAccess="False" /> 
          </requestedPrivileges>
        </security>
      </trustInfo>
    </assembly>

  13. #13
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,854
    Quote Originally Posted by binks View Post
    And how would you do it with a timer?
    Generally I do not use a timer, as I want the progressbar to reflect the actual progress of the operation.

    If you use a timer to update then you have an indicator that the task has not finished, not how much of the task has finished.
    A timer driven PB will step to 100% then need to be reset to 0% then steped to 100%, (rinse and repeat).


    Quote Originally Posted by binks View Post
    And if you wanted to load actual files, it wouldn't be in a loop, it would just run through the code like so.
    Yes. If you have 10 steps to finish the task, set the range of the PB to 10 and step 1 per step.

    If you need to open 100 files, set the range (max number of steps) to 100 and SET POS each time a file is opened (etc)

    I would not call GET POS each time, just update based on where you are in the task (ie load save game is step 3 so just call SET POS = 3, which is much quicker than GET POS then POS ++ then SET POS).

    Code:
    	//set the PB range (max number of steps) in this case  10
    	SendMessage(hwndpb, PBM_SETRANGE, 0, 10);
    
    	//reset the PB to the start (ie no ticks / steps)
    	SendMessage(hwndpb, PBM_SETPOS, 0, 0);
    
    	//perform each step to complete task
    	LoadDataBase();
    	SendMessage(hwndpb, PBM_SETPOS, 1, 0);
    
    	//next task
    	LoadSaveGame();
    	SendMessage(hwndpb, PBM_SETPOS, 2, 0);
    
    	//next task etc
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  14. #14
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by novacain View Post
    Generally I do not use a timer, as I want the progressbar to reflect the actual progress of the operation.

    If you use a timer to update then you have an indicator that the task has not finished, not how much of the task has finished.
    A timer driven PB will step to 100% then need to be reset to 0% then steped to 100%, (rinse and repeat).
    Um, no...

    Calculate the postion of the progress bar in the WM_TIMER handler according to the state of your task.

    For example, loading a file, line by line... In the WM_TIMER do a calculation from the line counter of how much is loaded vs the size of the file, and update the progress bar... works nicely.

    Code:
    case WM_TIMER :  //twice a second 
      sendmessage(hProgBar,PBM_SETPOS,(WPARAM)(Lines * 100) / total, 0);
    I do this during start up of a couple of large applications I have out there... the program needs to load a rather large index file via the network, so I first determine the size of the file, then as I read in structs I keep track of how many are read. In the WM_TIMER the position is set to reflect the percentage read...

    I had another appy --quick little thing that had to grind through about 3,000 files and convert them to a different format, took about 3 seconds per file-- so I figured out how many files there were, tracked how many I'd done, then once a second the WM_TIMER fired... a quick calculation and a message to update the progress bar.

    Both provide a true indication of progress... Mind you these are multi-tasking applications where the UI runs independent of the other processes, and WM_TIMER is not blocked by the function call.

  15. #15
    Registered User
    Join Date
    Mar 2011
    Posts
    211
    So I only have to change these fields to reflect my project?
    Code:
    <assemblyIdentity type="win32" 
                        name="MyProgram" 
                        version="1.0.0.0" 
                        processorArchitecture="X86" /> 
      <description> 
        My Program Description  
      </description>
    And do I have to make any changes to this?
    Code:
          <assemblyIdentity type="win32" 
                            name="Microsoft.Windows.Common-Controls" 
                            version="6.0.0.0" 
                            processorArchitecture="X86" 
                            publicKeyToken="6595b64144ccf1df" 
                            language="*" />
    And to include it, do I just add
    Code:
    #include "style.manifest"
    Or do I have to include it with the project options, similarly to adding linker commands or including a resource (.rc) file.?

    And is there anything I need to enable the style, or is it automatically done once included w/ the project?

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Progress bar
    By JFonseka in forum C Programming
    Replies: 9
    Last Post: 02-26-2008, 02:49 PM
  2. Progress Bar
    By mikeman118 in forum Windows Programming
    Replies: 3
    Last Post: 11-06-2007, 09:16 PM
  3. Updating a progress bar
    By NewGuy100 in forum C++ Programming
    Replies: 2
    Last Post: 03-13-2006, 12:00 PM
  4. Progress Bar
    By magic.mike in forum Windows Programming
    Replies: 3
    Last Post: 01-26-2005, 03:10 PM
  5. Progress Bar in DLL.
    By UW_COOP in forum Windows Programming
    Replies: 2
    Last Post: 06-12-2003, 02:00 AM

Tags for this Thread


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21