C language > Beginner questions

ListView Progressbar

(1/5) > >>

WiiLF23:
Hey everyone.

I am having issues with implementing progress bars using the following code *in Pelles C*

ListViewProgress.cpp
http://www.rohitab.com/discuss/topic/36617-listview-with-progress-bar/#entry10078425

Pelles C version:

--- Code: ---#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <wchar.h>

#pragma comment(lib, "uxtheme.lib")
#pragma comment(lib, "comctl32.lib")
#include <uxtheme.h>
#include <tmschema.h>

#include "main.h"

#define NELEMS(a)  (sizeof(a) / sizeof((a)[0]))

static INT_PTR CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM);

typedef struct {
INT  iSubItem;
INT  iProgress;
BOOL bSmooth;
} LISTVIEWPROGRESS;

static HANDLE ghInstance;

HFONT MakeFont(LPTSTR szFont, INT nPointSize, INT nWeight, BOOL bItalic, BOOL bUnderline, BOOL bStrikeOut, DWORD dwCharSet) // by Napalm
{
HDC hDC = GetDC(HWND_DESKTOP);
nPointSize = -MulDiv(nPointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
ReleaseDC(HWND_DESKTOP, hDC);
return CreateFont(nPointSize, 0, 0, 0, nWeight, bItalic, bUnderline, bStrikeOut,
dwCharSet, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, szFont);
}

VOID DrawProgressBar(HTHEME hTheme, HDC hdc, LPRECT rcBar, BOOL bSmooth, INT iProgress) // by Napalm
{
RECT rcContent, rcChunk;

// Find the content area inside the progress bar area.
GetThemeBackgroundContentRect(hTheme, hdc, PP_BAR, 0, rcBar, &rcContent);

// Work out the how much of the content area is for our progress-bar percentage.
iProgress = rcContent.left + (int)((rcContent.right - rcContent.left) *
((float)max(min(iProgress, 100), 0) / 100.0f));

// Draw the progress-bar background with transparent corners.
HRGN hRgn = CreateRoundRectRgn(rcBar->left, rcBar->top, rcBar->right + 1, rcBar->bottom + 1, 4, 4);
SelectClipRgn(hdc, hRgn);
DrawThemeBackground(hTheme, hdc, PP_BAR, 0, rcBar, NULL);
SelectClipRgn(hdc, NULL);
DeleteObject(hRgn);

if(bSmooth){
// Draw the smooth progress-bar percentage.
rcChunk = rcContent;
rcChunk.right = iProgress;
DrawThemeBackground(hTheme, hdc, PP_CHUNK, 0, &rcChunk, NULL);
}else{
// Draw the chunked progress-bar percentage.
int nChunkSize = 6, nSpaceSize = 2;
GetThemeMetric(hTheme, hdc, PP_BAR, 0, TMT_PROGRESSCHUNKSIZE, &nChunkSize);
GetThemeMetric(hTheme, hdc, PP_BAR, 0, TMT_PROGRESSSPACESIZE, &nSpaceSize);
rcChunk = rcContent;
rcChunk.right = rcChunk.left;
while(rcChunk.left < iProgress){
rcChunk.right = min(rcChunk.left  + nChunkSize, rcContent.right);
DrawThemeBackground(hTheme, hdc, PP_CHUNK, 0, &rcChunk, NULL);
rcChunk.left  = min(rcChunk.right + nSpaceSize, rcContent.right);
}
}
}

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, WCHAR *pszCmdLine, int nCmdShow)
{
    INITCOMMONCONTROLSEX icc;
    WNDCLASSEX wcx;

    ghInstance = hInstance;

    icc.dwSize = sizeof(icc);
    icc.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&icc);

    wcx.cbSize = sizeof(wcx);
    if (!GetClassInfoEx(NULL, MAKEINTRESOURCE(32770), &wcx))
        return 0;

    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_ICO_MAIN));
    wcx.lpszClassName = L"ListViewClass";
    if (!RegisterClassEx(&wcx))
        return 0;

    return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)MainDlgProc);
}

static HWND hWndListView;
static HWND hTestListView;

static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HANDLE hThemeProgress = NULL;
static HFONT hfArial = NULL;
static LISTVIEWPROGRESS lvProgress[11];

    switch (uMsg)
    {
case WM_CREATE:
{
// Dummy message. So we initialize our hThemeProgress handle.
SendMessage(hwndDlg, WM_THEMECHANGED, 0, 0);

// Create a List-View with some funky styles.
hWndListView = GetDlgItem(hwndDlg, LISTVIEW_CONTROL);

ListView_SetExtendedListViewStyle(hWndListView, LVS_EX_FULLROWSELECT);

// Set our List-View options.
hfArial = MakeFont(L"Arial", 11, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET);
SendMessage(hWndListView, WM_SETFONT, (WPARAM)hfArial, FALSE);
ListView_SetTextColor(hWndListView, RGB(10, 10, 160));

// Populate our list view with test data and assign the progress bars.
WCHAR szText[32] = { 0 };
LVCOLUMN lvColumn = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, 150, szText };

LVITEM lvItem = { 0 };
lvItem.pszText  = szText;
for(lvItem.iItem = 0; lvItem.iItem < 11; lvItem.iItem++){
lvProgress[lvItem.iItem].iSubItem  = 2;
lvProgress[lvItem.iItem].iProgress = lvItem.iItem * 10;
lvProgress[lvItem.iItem].bSmooth   = TRUE;
lvItem.lParam = (LPARAM)&lvProgress[lvItem.iItem];
for(lvItem.iSubItem = 0; lvItem.iSubItem < 4; lvItem.iSubItem++){
lvItem.mask = LVIF_TEXT | ((lvItem.iSubItem == 0) ? LVIF_PARAM : 0);
wsprintf(szText, L"Item %c%d", L'A' + lvItem.iSubItem, lvItem.iItem);
if(lvItem.iSubItem == 0)
ListView_InsertItem(hWndListView, &lvItem);
else if(lvItem.iSubItem != 2)
ListView_SetItem(hWndListView, &lvItem);
}
}
lvProgress[2].bSmooth = FALSE;
lvProgress[7].bSmooth = FALSE;
lvProgress[8].bSmooth = FALSE;

// Timer for test updates.
SetTimer(hwndDlg, 0, 75, 0);
}
return 0;

case WM_INITDIALOG:
{
// I dont want to create a new control - I want to use the ListView control already added by visual designer (2nd line below)
hWndListView = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | LVS_REPORT | LVS_NOSORTHEADER, 0, 0, 0, 0, hwndDlg, (HMENU)LISTVIEW_CONTROL, ghInstance, NULL);
//hWndListView = GetDlgItem(hwndDlg, LISTVIEW_CONTROL);

// Set our List-View options.
hfArial = MakeFont(L"Arial", 11, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET);
SendMessage(hWndListView, WM_SETFONT, (WPARAM)hfArial, FALSE);
ListView_SetTextColor(hWndListView, RGB(10, 10, 160));

WCHAR szText[32] = { 0 };
LVCOLUMN lvColumn = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH, LVCFMT_LEFT, 150, szText };

    lvColumn.mask = LVCF_TEXT | LVCF_WIDTH;
    lvColumn.cx = 100;
    lvColumn.pszText = L"Column A";
    ListView_InsertColumn(hWndListView, 0, &lvColumn);

    lvColumn.cx = 100;
    lvColumn.pszText = L"Column B";
    ListView_InsertColumn(hWndListView, 1, &lvColumn);

lvColumn.cx = 200;
    lvColumn.pszText = L"Column C";
    ListView_InsertColumn(hWndListView, 2, &lvColumn);

lvColumn.cx = 100;
    lvColumn.pszText = L"Column D";
    ListView_InsertColumn(hWndListView, 3, &lvColumn);
           
LVITEM lvItem = { 0 };
lvItem.pszText  = szText;
for(lvItem.iItem = 0; lvItem.iItem < 11; lvItem.iItem++){
lvProgress[lvItem.iItem].iSubItem  = 2;
lvProgress[lvItem.iItem].iProgress = lvItem.iItem * 10;
lvProgress[lvItem.iItem].bSmooth   = TRUE;
lvItem.lParam = (LPARAM)&lvProgress[lvItem.iItem];
for(lvItem.iSubItem = 0; lvItem.iSubItem < 4; lvItem.iSubItem++){
lvItem.mask = LVIF_TEXT | ((lvItem.iSubItem == 0) ? LVIF_PARAM : 0);
wsprintf(szText, L"Item %c%d", L'A' + lvItem.iSubItem, lvItem.iItem);
if(lvItem.iSubItem == 0)
ListView_InsertItem(hWndListView, &lvItem);
else if(lvItem.iSubItem != 2)
ListView_SetItem(hWndListView, &lvItem);
}
}
lvProgress[2].bSmooth = FALSE;
lvProgress[7].bSmooth = FALSE;
lvProgress[8].bSmooth = FALSE;

// Timer for test updates.
SetTimer(hwndDlg, 0, 75, 0);

return TRUE;
}

case WM_TIMER:
    {
        //hWndListView = GetDlgItem(hwndDlg, LISTVIEW_CONTROL);
        int itemCount = ListView_GetItemCount(hWndListView);

        // Increment and loop each progress bar.
        // We also build an update rectangle as we go to reduce flicker when we finally repaint.
        RECT rcItem, rcUpdate;
        SetRectEmpty(&rcUpdate);
        for (int iItem = 0; iItem < itemCount; iItem++) {
            if (++lvProgress[iItem].iProgress > 100)
lvProgress[iItem].iProgress = 0;

            ListView_GetSubItemRect(hWndListView, iItem, lvProgress[iItem].iSubItem, LVIR_BOUNDS, &rcItem);
            UnionRect(&rcUpdate, &rcUpdate, &rcItem);
        }
        RedrawWindow(hWndListView, &rcUpdate, NULL, RDW_INVALIDATE | RDW_NOERASE | RDW_UPDATENOW);
    }
    return 0;

case WM_THEMECHANGED:
{
if(hThemeProgress){
CloseThemeData(hThemeProgress);
hThemeProgress = NULL;
}
hThemeProgress = OpenThemeData(hwndDlg, L"Progress");
}
return 0;

case WM_SIZE:
{
hWndListView = GetDlgItem(hwndDlg, LISTVIEW_CONTROL);
// Resize the ListView when our test window gets resized.
RECT rcClient;
GetClientRect(hwndDlg, &rcClient);
InflateRect(&rcClient, -10, -10);
SetWindowPos(hWndListView, NULL,
rcClient.left,    rcClient.top,
rcClient.right  - rcClient.left,
rcClient.bottom - rcClient.top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
return 0;

case WM_NOTIFY:
{
LPNMHDR lpNMHdr = (LPNMHDR)lParam;
// We received a Custom Draw notification message. Was it for our ListView?
if(lpNMHdr->idFrom == LISTVIEW_CONTROL && lpNMHdr->code == NM_CUSTOMDRAW){
LPNMLVCUSTOMDRAW pCustomDraw = (LPNMLVCUSTOMDRAW)lParam;
// Is this part of the drawing state for each sub-item?
if(pCustomDraw->nmcd.dwDrawStage == (CDDS_ITEMPOSTPAINT | CDDS_SUBITEM)){
LISTVIEWPROGRESS *lvp = (LISTVIEWPROGRESS *)pCustomDraw->nmcd.lItemlParam;
// Does the current sub-item match the progress-bar sub-item?
if(pCustomDraw->iSubItem == lvp->iSubItem){
RECT rcBar;
// Get the sub-item rectangle.
ListView_GetSubItemRect(lpNMHdr->hwndFrom, pCustomDraw->nmcd.dwItemSpec,
pCustomDraw->iSubItem, LVIR_BOUNDS, &rcBar);
// Get a little extra room.
InflateRect(&rcBar, -2, -2);
// Draw the progress bar to the sub-item location.
DrawProgressBar(hThemeProgress, pCustomDraw->nmcd.hdc, &rcBar, lvp->bSmooth, lvp->iProgress);
// Skip the default drawing action since we've done this stages drawing.
return CDRF_SKIPDEFAULT;
}
}
// Else do the default drawing action and request we get post-subitem drawing notification.
return CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYSUBITEMDRAW;
}
}
break;

case WM_DESTROY:
if(hThemeProgress)
CloseThemeData(hThemeProgress);
if(hfArial)
DeleteObject(hfArial);
PostQuitMessage(0);
return 0;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                case IDOK:
                    EndDialog(hwndDlg, TRUE);
                    return TRUE;
            }
            break;

        case WM_CLOSE:
            EndDialog(hwndDlg, 0);
            return TRUE;
    }

    return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
}

--- End code ---

main.h

--- Code: ---#define DLG_MAIN  1001
#define IDR_ICO_MAIN  8001
#define LISTVIEW_CONTROL 100

--- End code ---

What is happening at this point after launching application:



Left: Pelles C
Right: (from here - http://www.rohitab.com/discuss/topic/36617-listview-with-progress-bar/#entry10078425)

Can someone tell me what is going on? Additionally, I dont want to "Create a control programmatically", as I have a ListView control added though the resource editor (dialog design mode).

3 days at this now and I am  just burning hours and hours at this silly issue. Can someone point out what the problem is so I can finally continue developing this application?

I need the code from the link (aka ListViewProgress.cpp).
This still does not provide me a method to update specific progress rows in the listview based on the index of subitem0, but that is outside the scope of my question

Any help is greatly appreciated. Thank you.

Pelle:
The original code from http://www.rohitab.com works fine with Pelles C.

The difference to the code posted here starts in WinMain():
- The original code creates a traditional application window, where one of the first messages will be WM_CREATE.
- The code posted here uses the trick of creating the application window as a dialog, where one of the first messages will be WM_INITDIALOG and not WM_CREATE (so some needed code will never be executed).

I'm pretty sure the code can be rewritten to work with the application window as a dialog, but I leave the exact details for someone else...

WiiLF23:
Thank you Pelle, I appreciate the response. I will follow that link and consult the resource provided.

frankie:
I confirm that the original code works well.
I can't find the problem, but I suspect that using the dialog the notification messages aren't sent to the dialog procedure.
Probably it requires the subclassing of the ListView to work.
I'll check when I'll have time.

WiiLF23:
Thank you frankie. It is solid code, but I agree the notification is incorrect in respect to Pelles C.

Sub-classing has brought me great things outside of this criteria (UI updates from another thread etc) so this just may be the way.
This will certainly help out others as well.

EDIT
Dropping source file as a generic Win32 program compiles successful producing the exact result I was after as shown in the right side screenshot. This is interesting. I have a lot to learn about this.

WindowProcedure  => DialogProcedure etc. I should be able to get this working within the hour. Thanks for all the help guys.

Navigation

[0] Message Index

[#] Next page

Go to full version