Embed Progress Bars in a List Control

Environment: VC6

Description:

This control can be used for displaying the progress of multiple events simultaneously. Each line in a list control can have its own progress bar. Progress bars operate independently and (can be) simultaneously.

This is also a very simple example of how to embed one control inside another by making the embedded control a child of the main window.

How to Use:

Create a list control on a dialog and make the variable of type CProgressList. For each item inserted into the list, you may call CreateProgress(ItemIndex) (not all items have to have a progress bar). This inserts the progress bar for that item. Then you can call SetProgress(ItemIndex). You must also add the CAdvHeaderCtrl class to your project.

Implementation:

I created a new MFC extension class derived from CListCtrl. Then added a method to create some static columns (for testing purposes), and a method to insert a couple items.

To create the progress bars, I added a method to create a progress bar for the specified index (passed in). This method creates an instance of a nested class that contains the index, sub-index, and CProgressCtrl members. After creating the CProgressEntry (the nested class), you have to call Create on the CProgressCtrl member to pass the progress control the style (which must include WS_CHILD), the parent (this-the ptr to the list control itself), and the window coordinates to occupy. The window coordinates are going to be exactly the same as the bounding rectangle of the specified index/subitem, so I used this->GetSubItemRect(item, subitem, Rect) to get the coordinates for the subitems square. Then I passed this rectangle into the progress controls create function.

void CProgressListCtrl::CreateProgress(int Index)
{
  // can only create progress for an existing item
  if (Index >= GetItemCount())
     return;

  // CProgressEntry is a nested class containing Index,
  // SubIndex, and CProgressCtrl members.

  CProgressEntry* ProgEntry = new CProgressEntry(Index, 2);
  CRect ItemRect;
  GetSubItemRect(Index, ProgEntry->m_SubIndex, LVIR_BOUNDS, 
                                ItemRect);
  int left = ItemRect.left;
  int top = ItemRect.top;
  int right = ItemRect.right;
  int bottom = ItemRect.bottom;
  (ProgEntry->m_Prog).Create(PBS_SMOOTH | WS_CHILD | WS_VISIBLE, 
                     CRect(left, top, right, bottom), this, 1);
  (ProgEntry->m_Prog).SetRange(0, 100);
  (ProgEntry->m_Prog).SetPos(0);
  m_ProgEntries[Index] = ProgEntry;
}

Here is the code for setting the progress bar for an item:

void CProgressListCtrl::SetProgress(int Index, int prog)
{
  CProgressEntry* ProgEntry;
  if (m_ProgEntries.Lookup(Index, ProgEntry) == TRUE)
     (ProgEntry->m_Prog).SetPos(prog);
}

Now the progress control is connected to the list control (through parent/child), and it is created in the right place. However, when the user scrolls the window, or moves/resizes it, we must tell the progress control the new coordinates to occupy. So I added some message handlers for Horizontal scroll, Vertical scroll, and move to get the new sub item rectangle and pass it to the Progress Ctrl using MoveWindow. All message handlers call ResizeProg().

void CProgressListCtrl::ResizeProg()
{
  CRect ItemRect;
  CProgressEntry* ProgEntry=0;
  int Index=0;
  POSITION pos = m_ProgEntries.GetStartPosition();
  while (pos != NULL) {
    m_ProgEntries.GetNextAssoc(pos, Index, ProgEntry);
    GetSubItemRect(ProgEntry->m_Index, ProgEntry->m_SubIndex, 
                                  LVIR_BOUNDS, ItemRect);
    int left = ItemRect.left;
    int top = ItemRect.top;
    int right = ItemRect.right;
    int bottom = ItemRect.bottom;
    (ProgEntry->m_Prog).MoveWindow(left, top, 
                       (right - left), (bottom - top));
  }
}

What about re-sizing the columns? This was a bit tricky. I had to use a class derived from CHeaderCtrl (CAdvHeaderCtrl), and hook the header to the ListCtrl. This header control was borrowed from Matt Weagles "Using the Header Control" example on www.codeproject.com. The message we need is HDN_ENDTRACK-it is sent when the user is done dragging the column divider (resizing the column). But it is sent to the list control header, NOT to the list control. So I made my new header class handle this message and call a function on the list ctrl (it could also send a message) to tell the list control to resize the progress bars (and move them).

void CAdvHeaderCtrl::OnEndTrack(NMHDR * pNMHDR, 
                                LRESULT* pResult)
{
  NMHEADER *pHdr = (NMHEADER*)pNMHDR;
  ((CProgressListCtrl*)GetParent())->ResizeProg();
  *pResult = 0;
}

To hook up the header, I created a member variable inside the list control class of type derived header. Then call (from somewhere in your initialization code I called it from CreateColumns)

m_Header->Init(GetHeaderCtrl())

This passes a pointer to the default header control into the new derived header ctrl class. The Init function simply SubClasses the default header control and voila, it is all hooked up. Now the new header can receive the column resize message (HDN_ENDTRACK) and tell the parent (the list control).

Clean up:

The progress entries (each progress control-the instance of the nested class) are stored in a CMap. In the list control destructor, I simply iterate the map and delete all the objects.

Other Notes:

This project was created fairly quickly, so it doesnt have extensive error handling or testing in other environments (document/view setting).

Also, there is some COM Automation code in the project that I was playing with. It works (kinda) if you want to play with it, but its not great. If you create a COM client to control this dialog app, then call Start, it will never return to the COM client (because it isnt multithreaded; Start uses ClearMsgQueue to keep the GUI active). But, if you click Start on the server app, and use the COM client to Stop it, it will work fine (because Stop returns right away). To fix it, youd have to make the progress bars run in a new thread (in the dialog class). Then Start would kick off a thread and return immediately so the COM client would be happy. E-mail me if you want to check out my sample COM client (its a C++ MFC COM project).

Downloads

Download demo project - 12 Kb
Download source 19 Kb


Comments

  • Progress bar overlaps column header

    Posted by doroubo on 02/10/2008 10:38pm

    Hello, I tried adding more entries and tried resizing the column header for the progress bar. When I scrolled down, the progress bar overlaps the column header. How can I fix this? Thanks.

    Reply
  • Hello

    Posted by eptgrant on 09/30/2007 10:12pm

    Im looking to do the same thing with Visual Basic 6.0. Is this possible? if so could someone help me?
    
    much thanks.
    Grant

    Reply
  • one bug and the right method

    Posted by pizzq on 12/11/2004 02:35am

    when HDN_ENDTRACK message is received, the head ctrl column width havn't changed, so OnEndTrack call ResizeProg and ResizeProg call GetSubItemRect is error,I think pass pHdr->pitem->cxy to the ResizeProg is right.(pizzq@hotmail.com)

    Reply
  • very very good

    Posted by chengyayu on 06/11/2004 10:36am

    your work is excellent !

    Reply
  • The more item increase, the more very slow

    Posted by Legacy on 08/07/2002 12:00am

    Originally posted by: Y

    It's too slow

    Reply
  • Amazing look, excellent work!

    Posted by Legacy on 07/02/2002 12:00am

    Originally posted by: Piggy_OnDiet

    Amazing look, excellent work!

    Reply
  • CListView

    Posted by Legacy on 03/01/2002 12:00am

    Originally posted by: Barbacot

    What about using CListView instead of CListCtrl?

    Reply
  • Simple Modification

    Posted by Legacy on 12/24/2001 12:00am

    Originally posted by: RAF

    Good work!!!

    Try simple modyfication for beeter resisable.
    Call ResizeProg in this method:


    void CMyHeaderCtrl::OnLButtonUp(UINT nFlags, CPoint point)
    {
    // TODO: Add your message handler code here and/or call

    CHeaderCtrl::OnLButtonUp(nFlags, point);

    ((CPanTechListCtr*)GetParent())->ResizeProg();
    Invalidate();
    }

    Reply
  • Keep It Up

    Posted by Legacy on 12/02/2001 12:00am

    Originally posted by: AMER BUTT

    Good work beneficial for new commers. Please keep it up.
    
    thanks

    Reply
  • When VSCOLL is happen, Progress ctrl overlaps with ListCtrl hearder!

    Posted by Legacy on 11/07/2001 12:00am

    Originally posted by: DongGyu-Lee

    From scroll bar, if progress bar is disapperred to upward,
    
    Progress ctrl overlaps with ListCtrl hearder.
    for solve the problem, add this code to ResizeProg()

    while (pos != NULL)
    {
    ..
    ..
    CRect rect;
    GetHeaderCtrl()->GetClientRect(rect);

    if(top>=rect.bottom)
    (ProgEntry->m_Progress).MoveWindow(left, top, (right - left), (bottom - top));
    else
    (ProgEntry->m_Progress).MoveWindow(left, -20, (right - left), (bottom - top));

    // modified code from
    //Comments from - cyt (2001/08/19)
    // under the comment:Resizing - wimel (2001/06/25)
    GetItemRect(ProgEntry->m_Index,&rect,LVIR_BOUNDS);
    InvalidateRect(&rect);
    }

    • Got fixed

      Posted by doroubo on 02/11/2008 12:31am

      Thanks for the solution...

      Reply
    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • This paper introduces IBM Java on the IBM PowerLinux 7R2 server and describes IBM's implementation of the Java platform, which includes IBM's Java Virtual Machine and development toolkit.

  • In the competitive marketplace that surrounds us today, customers shouldn't have to settle for legacy desktop or application delivery simply because they've relied on a certain vendor in the past. This white paper reviews how three customers decided to partner with VMware, and how they benefited from the latest VDI and app trends to improve the end-user experience, increase productivity, reliability and stability to deliver better SLAs - with lower cost and less time needed to manage end users.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds