Listbox scrollbar notification

Started by jboyes, October 07, 2012, 06:45:06 PM

Previous topic - Next topic

jboyes

I'm afraid I am looking for some help again - but it has been a while.

I am writing an application to download DMX slot information to the EEPROM of a PIC micocontroller. I have a listbox containing the values and next to it I have placed a textbox which displays the appropriate index numbers alongside.

When I scroll the listbox, I want the indices to change along with the data values. I can get this to work but I have to click on a separate button each time or (unnecessarily) select an item from the listbox in order to trigger my routine for updating the indices.

What I need is a way of getting notification when the listbox is scrolled but I just cannot figure out how to do it.

I found an old forum topic that suggested using a listview control and 'making it look like a listbox' but I am unable to get rid of the icons.

Any help would be appreciated especially if you could show me how to get notifications from the listbox because the listbox is the neatest (and easiest) way of listing the values.

John.

frankie

"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

jboyes

Thanks Frankie but that is exactly what I have been using (I have also used a separate button to trigger my routine). What I want though is notification of changes to the scrollbar position without having to click anything else other than the scrollbar itself.

John.

CommonTater

Quote from: jboyes on October 07, 2012, 09:05:25 PM
Thanks Frankie but that is exactly what I have been using (I have also used a separate button to trigger my routine). What I want though is notification of changes to the scrollbar position without having to click anything else other than the scrollbar itself.

Try using a LISTVIEW control instead of a LISTBOX and TEXTBOX ...

The listview in details mode will let you define multiple collumns that will always stay aligned.

Stefan Pendl

#4
How about a multi column list box?
Use the style LBS_MULTICOLUMN and the message LB_SETCOLUMNWIDTH.

A simple list view is used by the project I am involved with, check out vollist.c.
---
Stefan

Proud member of the UltraDefrag Development Team

jboyes

Many thanks for all the help offered.

Tater:
I may end up using a Listview but I struggle to understand how to set them up. They don't seem to be as well documented as other controls.

Stephan:
Reading the documentation about multi-column listboxes, I read, "List boxes with the LBS_MULTICOLUMN style cannot be vertically scrolled. The list box ignores any WM_VSCROLL messages it receives." And, of course, it's vertical scrolling that I want  :(

Everything I want to do is working fine except fot the fact that I cannot get notification of any scrolling.  An idea has occured to me that, if I set up a Timer to notify every (say) 0.5 secs, I can compare the GetScrollPos(hScroll,SB_VERT) with the previous value (kept in memory) and, if the two values do not match, I can update my text box.  Sounds simple but I wonder if the timer would be using up too much unnecessary CPU.

John.

frankie

From MS:
"For a multiple-selection list box, the LBN_SELCHANGE notification code is sent whenever the user presses an arrow key, even if the selection does not change."
Try enabling multiple selection eventually filtering them out (if you don't want multiple selection) or subclass the control and hook on windows refresh/update
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

jboyes

Quote from: frankie on October 08, 2012, 02:39:12 PM
From MS:
"For a multiple-selection list box, the LBN_SELCHANGE notification code is sent whenever the user presses an arrow key, even if the selection does not change."
Try enabling multiple selection eventually filtering them out (if you don't want multiple selection) or subclass the control and hook on windows refresh/update

Thanks Frankie,

I had already tried LBN_SELCHANGE notification and it did work but, unfortunately, did not respond to any change in the scrollbar thumb or the arrows.

I'm ashamed to say that I haven't a clue about subclassing which is why my question was posted in the 'Beginners' section.


To ALL responders:

The 'Daft' idea that I came up with of using a Timer actually WORKS!  It works very well and does exactly what I wanted to acheive. I have set the perion to 0.3 seconds and I haven't bothered to check if the scroll position has changed because the code that is executed is very short.

Thanks again for all your help.

John.

TimoVJL

#8
Simple ListBox subclassing example
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>

#define IDC_LBOX 4001

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char *szAppName = "WinFrame";
char *szFrameClass = "cWinFrame";
HWND hFrame;
HANDLE hInst;

HWND hList;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcx;
MSG msg;

wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = (WNDPROC) WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground= (HBRUSH)COLOR_3DSHADOW;
wcx.lpszMenuName = NULL;
wcx.lpszClassName= szFrameClass;
wcx.hIconSm = 0;

if (!RegisterClassEx(&wcx))
return 0;
hInst = hInstance;

hFrame = CreateWindowEx(0, szFrameClass, szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
260, 250,
NULL, NULL, hInst, NULL);
if(!hFrame) return 0;
ShowWindow(hFrame, nCmdShow);
UpdateWindow(hFrame);

while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

void OnCreate(HWND hWnd);

LRESULT CALLBACK SubclassWndProcLB(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam);
WNDPROC wndProcOrigLB;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_CREATE:
OnCreate(hWnd);
return 0;
case WM_DESTROY:
SetWindowLongPtr(hList, GWLP_WNDPROC, (LONG_PTR)wndProcOrigLB);
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}

void OnCreate(HWND hWnd)
{
//InitCommonControls();
hList = CreateWindowEx(0, "ListBox", NULL,
WS_CHILD | WS_BORDER | WS_TABSTOP | WS_VISIBLE | WS_VSCROLL
, 1, 50, 200, 100
, hWnd, (HMENU)IDC_LBOX, hInst, NULL);
for (int i = 1; i <= 10; i++) {
TCHAR szTmp[10];
wsprintf(szTmp, "row %d", i);
SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)szTmp);
}
wndProcOrigLB = (WNDPROC)SetWindowLongPtr(hList, GWLP_WNDPROC, (LONG_PTR)SubclassWndProcLB);
SetFocus(hList);
}
// Subclass Window Proc
LRESULT CALLBACK SubclassWndProcLB(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// debug it
TCHAR szTmp[100];
wsprintf(szTmp, TEXT("%Xh"), uMsg);
SetWindowText(hFrame, szTmp);
//
CallWindowProc(wndProcOrigLB, hwnd, uMsg, wParam, lParam);
}
May the source be with you

CommonTater

#9
Setting up a listview control ... probably your best solution here...



    InitCommonControls();


    LVCOLUMN lc = {0};                // list view colum struct

    // log window
    gLog = CreateWindowEx(WS_EX_WINDOWEDGE,WC_LISTVIEW,NULL,
                            WS_CHILD | WS_VISIBLE | WS_TABSTOP |
                            LVS_REPORT | LVS_NOSORTHEADER,
                            0,0,0,0,gMain,(HMENU) 300,gInst,NULL);

   // add columns
   lc.mask = LVCF_TEXT | LVCF_WIDTH;
    lc.pszText = L"Time";
    lc.cx = 150;
    ListView_InsertColumn(gLog,0,&lc);
    lc.pszText = L"From";
    lc.cx = 100;
    ListView_InsertColumn(gLog,1,&lc);
    lc.pszText = L"Event";
    lc.cx = 300;
    ListView_InsertColumn(gLog,2,&lc);


From there just follow the API ... HERE

There are macros for almost everything it does....




DMac

#10
Simple is as simple does.

From my article http://www.codeproject.com/Articles/77957/Win32-SDK-PropertyGrid-Made-Easy

QuoteDetecting Begin and End Scroll Events in a Listbox

The Listbox doesn't have a scrollbar component, instead it draws a scroll bar in the non-client area of the control probably using DrawFrameControl(). Consequently one cannot subclass the scrollbar in order to detect mouse events. The following snippet demonstrates one way to work around this and detect begin and end scroll.
static LRESULT CALLBACK ListBox_Proc(HWND hList, UINT msg,
        WPARAM wParam, LPARAM lParam)
{
    HWND hParent = GetParent(hList);

    // Note: Instance data is attached to listbox's parent
    Control_GetInstanceData(hParent, &g_lpInst);

    switch (msg)
    {
        //
        // Skip stuff
        //

        case WM_MBUTTONDOWN:
        case WM_NCLBUTTONDOWN:
            //The listbox doesn't have a scrollbar component, it draws a scroll
            // bar in the non-client area of the control.  A mouse click in the
            // non-client area then, equals clicking on a scroll bar.  A click
            // on the middle mouse button equals pan, we'll handle that as if
            // it were a scroll event.
            ListBox_OnBeginScroll(hList);
            g_lpInst->fScrolling = TRUE;
            break;

        case WM_SETCURSOR:
            //Whenever the mouse leaves the non-client area of a listbox, it
            // fires a WM_SETCURSOR message.  The same happens when the middle
            // mouse button is released.  We can use this behavior to detect the
            // completion of a scrolling operation.
            if (g_lpInst->fScrolling)
            {
                ListBox_OnEndScroll(hList);
                g_lpInst->fScrolling = FALSE;
            }
            break;

            //
            // more stuff
            //
There you have it, no need for substitutions or timers.
No one cares how much you know,
until they know how much you care.

jboyes

Wow!  What a brilliant forum this is!

One day I had NO solutions to my problem - now I have FOUR viable solutions.

The problem I now have is which one to choose. I would like to try them all (and, in time, I probably will) but for now I need to just get on with my project.

Many thanks to all who have replied, I am very grateful to you all.

John.