NO

Author Topic: ListView Progressbar  (Read 1696 times)

Offline WiiLF23

  • Member
  • *
  • Posts: 66
ListView Progressbar
« on: October 16, 2023, 02:47:28 AM »
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: [Select]
#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);
}

main.h
Code: [Select]
#define DLG_MAIN  1001
#define IDR_ICO_MAIN  8001
#define LISTVIEW_CONTROL 100

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.
« Last Edit: October 16, 2023, 03:00:04 AM by WiiLF23 »

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
Re: ListView Progressbar
« Reply #1 on: October 16, 2023, 01:15:53 PM »
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...
/Pelle

Offline WiiLF23

  • Member
  • *
  • Posts: 66
Re: ListView Progressbar
« Reply #2 on: October 16, 2023, 05:16:30 PM »
Thank you Pelle, I appreciate the response. I will follow that link and consult the resource provided.

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: ListView Progressbar
« Reply #3 on: October 16, 2023, 05:31:32 PM »
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.
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

Offline WiiLF23

  • Member
  • *
  • Posts: 66
Re: ListView Progressbar
« Reply #4 on: October 16, 2023, 06:04:07 PM »
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.
« Last Edit: October 16, 2023, 10:58:22 PM by WiiLF23 »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: ListView Progressbar
« Reply #5 on: October 17, 2023, 11:25:51 PM »
As I supposed the problem is the filtering to notification messages from the dialog window procedure.
Subclassing the dialog window solves the problem.  8)
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

Offline Robert

  • Member
  • *
  • Posts: 245
Re: ListView Progressbar
« Reply #6 on: October 18, 2023, 02:55:37 AM »
As I supposed the problem is the filtering to notification messages from the dialog window procedure.
Subclassing the dialog window solves the problem.  8)

Thanks, frankie, for this very informative example.

In should be noted that the example uses ComCtl32.dll version 6.

Quote
ComCtl32.dll version 6 is Unicode only. The common controls supported by ComCtl32.dll version 6 should not be subclassed (or superclassed) with ANSI window procedures.
More details here:

https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview


Also, frankie, in your code I see functions have a preamble like, for example, this

Code: [Select]
/*+@@fnc@@----------------------------------------------------------------*//*!
   \brief DialogSubclassedProcedure
   \date Created  on Tue Oct 17 23:00:57 2023
   \date Modified on Tue Oct 17 23:00:57 2023
\*//*-@@fnc@@----------------------------------------------------------------*/

Could you give us a brief explanation of the functionality of these comments and, if not of your invention, a reference?

Thanks again.

Offline Stefan Pendl

  • Global Moderator
  • Member
  • *****
  • Posts: 582
    • Homepage
Re: ListView Progressbar
« Reply #7 on: October 18, 2023, 09:21:55 AM »
Also, frankie, in your code I see functions have a preamble like, for example, this

Code: [Select]
/*+@@fnc@@----------------------------------------------------------------*//*!
   \brief DialogSubclassedProcedure
   \date Created  on Tue Oct 17 23:00:57 2023
   \date Modified on Tue Oct 17 23:00:57 2023
\*//*-@@fnc@@----------------------------------------------------------------*/

Could you give us a brief explanation of the functionality of these comments and, if not of your invention, a reference?

This seems to be related to automatic documenting the source by doxygen.
---
Stefan

Proud member of the UltraDefrag Development Team

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: ListView Progressbar
« Reply #8 on: October 18, 2023, 03:24:36 PM »
As I supposed the problem is the filtering to notification messages from the dialog window procedure.
Subclassing the dialog window solves the problem.  8)

Thanks, frankie, for this very informative example.

In should be noted that the example uses ComCtl32.dll version 6.

Quote
ComCtl32.dll version 6 is Unicode only. The common controls supported by ComCtl32.dll version 6 should not be subclassed (or superclassed) with ANSI window procedures.
More details here:

https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview

Robert you're right, but in this case there aren't text strings involved, I just handle graphic functions in the subclass code, so it should be safe enough.
Also in the link to MS docs that you provided few lines down, there is a deeper description:
Quote
All strings passed to the procedure are Unicode strings even if Unicode is not specified as a preprocessor definition.
Anyway for the sake of safety you can change the code to UNICODE.

Also, frankie, in your code I see functions have a preamble like, for example, this

Code: [Select]
/*+@@fnc@@----------------------------------------------------------------*//*!
   \brief DialogSubclassedProcedure
   \date Created  on Tue Oct 17 23:00:57 2023
   \date Modified on Tue Oct 17 23:00:57 2023
\*//*-@@fnc@@----------------------------------------------------------------*/

Could you give us a brief explanation of the functionality of these comments and, if not of your invention, a reference?

Thanks again.
As Stefan supposed this is an utility to insert DoxyGen comments.
Right clicking inside a function you get a menu entry to edit comments for the function or the module (see 'menu' image).
Then will appear a dialog to entry data as in the 'Dialog' image. If you fill the Dialog with the data in the image you will get in the editor:
Code: [Select]
/*+@@fnc@@----------------------------------------------------------------*//*!
 \brief InitListView
 \details Initialize the list view control
 \param [in] hWnd Dialog window handle
 \param [in] hWndListView list view control window handle
 \return None
 \exception None
 \pre To be called on Dialog entry
 \bug None known
 \author frankie
 \version 1.0.1
 \date Created  on Tue Oct 17 22:36:33 2023
 \date Modified on Wed Oct 18 15:15:15 2023
 \warning Call before to show Dialog
\*//*-@@fnc@@----------------------------------------------------------------*/
void InitListView(HWND hWnd, HWND hWndListView)
{
// Dummy message. So we initialize our hThemeProgress handle.
SendMessage(hWnd, WM_THEMECHANGED, 0, 0);

ListView_SetExtendedListViewStyle(hWndListView, LVS_EX_FULLROWSELECT);

It is in an old Addin of mine, and with some modification in a dropped project of mine.
The actual version is a patched one too messed up to publish. When I'll have time to fix it maybe I'll republish it.
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

Offline WiiLF23

  • Member
  • *
  • Posts: 66
Re: ListView Progressbar
« Reply #9 on: October 19, 2023, 07:27:02 PM »
The primary motivation behind this was to avoid GUI frameworks (QT4 for example), and to rely on native win32 controls.

All input has been more than appreciated!



Additionally - Draw the progress percentage (wide string):

Code: [Select]
void DrawProgressBar(HTHEME hTheme, HDC hdc, LPRECT rcBar, BOOL bSmooth, INT iProgress)
{
RECT rcContent, rcChunk;

int trackprog = iProgress;
GetThemeBackgroundContentRect(hTheme, hdc, PP_BAR, 0, rcBar, &rcContent);
iProgress = rcContent.left + (int)((rcContent.right - rcContent.left) * ((float)max(min(iProgress, 100), 0) / 100.0f));

int ellipse_width_height = 0, ellipse_height = 0;
HRGN hRgn = CreateRoundRectRgn(rcBar->left, rcBar->top, rcBar->right + 1, rcBar->bottom + 1, ellipse_width_height, ellipse_height);
SelectClipRgn(hdc, hRgn);
DrawThemeBackground(hTheme, hdc, PP_BAR, 0, rcBar, NULL);
SelectClipRgn(hdc, NULL);
DeleteObject(hRgn);

if (bSmooth)
{
rcChunk = rcContent;
rcChunk.right = iProgress;
DrawThemeBackground(hTheme, hdc, PP_CHUNK, 0, &rcChunk, NULL);

int progressPercentage = (trackprog > 100) ? 100 : ((trackprog < 0) ? 0 : trackprog);

WCHAR szProgressText[5];
wsprintf(szProgressText, L"%d%%", progressPercentage);

SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0, 0, 0)); // Set text color to black
DrawText(hdc, szProgressText, -1, rcBar, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

}
else
{
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);
}
}
}

« Last Edit: October 20, 2023, 06:25:58 AM by WiiLF23 »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: ListView Progressbar
« Reply #10 on: October 20, 2023, 11:10:25 AM »
To use the modified code, with progress percentage, in an ANSI project simply change the functions wsprintf to wsprintfW, and DrawText to DrawTextW, as in the amended following code:
Code: [Select]
void DrawProgressBar(HTHEME hTheme, HDC hdc, LPRECT rcBar, BOOL bSmooth, INT iProgress)
{
RECT rcContent, rcChunk;

int trackprog = iProgress;
GetThemeBackgroundContentRect(hTheme, hdc, PP_BAR, 0, rcBar, &rcContent);
iProgress = rcContent.left + (int)((rcContent.right - rcContent.left) * ((float)max(min(iProgress, 100), 0) / 100.0f));

int ellipse_width_height = 0, ellipse_height = 0;
HRGN hRgn = CreateRoundRectRgn(rcBar->left, rcBar->top, rcBar->right + 1, rcBar->bottom + 1, ellipse_width_height, ellipse_height);
SelectClipRgn(hdc, hRgn);
DrawThemeBackground(hTheme, hdc, PP_BAR, 0, rcBar, NULL);
SelectClipRgn(hdc, NULL);
DeleteObject(hRgn);

int progressPercentage = (trackprog > 100) ? 100 : ((trackprog < 0) ? 0 : trackprog);
WCHAR szProgressText[5];
wsprintfW(szProgressText, L"%d%%", progressPercentage);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0, 0, 0));  // Set text color to black

if (bSmooth)
{
rcChunk       = rcContent;
rcChunk.right = iProgress;
DrawThemeBackground(hTheme, hdc, PP_CHUNK, 0, &rcChunk, NULL);
}
else
{
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);
}
}

DrawTextW(hdc, szProgressText, -1, rcBar, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

EDIT: Modified code to write percentage for both styles (smoothed or not).
« Last Edit: October 20, 2023, 02:30:10 PM by frankie »
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

Offline John Z

  • Member
  • *
  • Posts: 796
Re: ListView Progressbar
« Reply #11 on: October 20, 2023, 12:14:19 PM »
As I supposed the problem is the filtering to notification messages from the dialog window procedure.
Subclassing the dialog window solves the problem.  8)

Thank you frankie!  Great example in subclassing and code style as well.  :)

John Z

Offline WiiLF23

  • Member
  • *
  • Posts: 66
Re: ListView Progressbar
« Reply #12 on: October 20, 2023, 09:04:07 PM »
Good work guys! I am now working on 2 functions to add item / update row progressbar by index.

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: ListView Progressbar
« Reply #13 on: October 22, 2023, 09:45:12 PM »
I made a general list view progress bar control here.
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

Offline WiiLF23

  • Member
  • *
  • Posts: 66
Re: ListView Progressbar
« Reply #14 on: October 23, 2023, 12:47:24 AM »
Looks good thanks! I have a full complete implementation allowing me to add row with custom subitem strings (6 col), and another to update progress on the fly (like a download progress counter) by index. Works flawlessly.

Its on to treeview from here ;) - I will be contributing all my learning from this separately