Effect of MoveWindow over ListView Control: why column headers hides away after calling the function?

211 views Asked by At

I have a dialog box with a listview control (lvc) in it. Because there are many editbox before lvc( 57 controls including lvc ), lvc is out of the screen. So i have to make a vertical scroll bar.

Vertical scroll bar:
Range : 0-250
every SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN and SB_LINEUP changes scroll position by 25

#define max_scroll_range 250
#define min_scroll_range 0
#define scroll_step  max_scroll_range/10

Project file overview

enter image description here


I am providing all the files so that you could see the problem live.


main.cpp

#include "include/basefile01.h"

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
HINSTANCE gi ;
HWND parentHwnd;
/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("IGAM");

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = gi = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = (HICON)MAKEINTRESOURCE(APP_ICON_XL);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = MAKEINTRESOURCE(MAIN_MENU);                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH );

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    parentHwnd = hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           _T("IGAM"),       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
    case WM_COMMAND:
        switch(LOWORD(wParam)){
        case MAIN_MENU_file_new_invoice:
                DialogBox(gi, MAKEINTRESOURCE(NEW_INVOICE_DIALOG), parentHwnd, (DLGPROC)invoice_dialog_box_message_handle);
            break;
        }
        break;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

this file calls the DialogBox which connects it with other files.


basefile01.cpp

#include "../include/basefile01.h"
#include <vector>
#define max_scroll_range 250
#define min_scroll_range 0
#define scroll_step  max_scroll_range/10

long iPrevVscroll = 0;

BOOL CALLBACK invoice_dialog_box_message_handle(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
    static long iVscroll;
    switch(msg)
    {
    case WM_INITDIALOG:
        iVscroll = 0;
        SetScrollRange(hwnd, SB_VERT, min_scroll_range, max_scroll_range, TRUE);
        SetScrollPos(hwnd, SB_VERT, min_scroll_range, TRUE);
        create_listViews_invoice_dialog(hwnd);
        return TRUE;

    case WM_COMMAND:
        switch(LOWORD(wp))
        {
            case IDCANCEL:EndDialog(hwnd, wp); return TRUE;
        }
    break;
    /**
        there are two variables @iVscroll and @iPrevVscroll
        they are there because the every child window will be moved up
        by the between previous scroll position and the current scroll position
    */
    case WM_VSCROLL:
        switch(LOWORD(wp))
        {
            case SB_PAGEDOWN:
            case SB_LINEDOWN : iVscroll += scroll_step; break;
            case SB_PAGEUP:
            case SB_LINEUP: iVscroll -= scroll_step; break;
            case SB_THUMBTRACK: iVscroll = HIWORD(wp); break;
        };
        /// iVscroll should be in the range of min_scroll_range and max_scroll_range
        iVscroll = ( iVscroll < min_scroll_range ? min_scroll_range : ( iVscroll > max_scroll_range ? max_scroll_range : iVscroll ) );
        /// don't  move any child windows if iVscroll is at boundaries
        if(!iVscroll || iVscroll == iPrevVscroll ) return TRUE;
        SetScrollPos(hwnd, SB_VERT, iVscroll, TRUE);
        /// Enumerate to all child windows of the dialog box; this is better than calling and moving each window in a loop
        EnumChildWindows(hwnd, invoice_dialog_child_enum_scroll, (LPARAM)iVscroll);
        iPrevVscroll = iVscroll;
        /// redraw the whole dialog box client area
        InvalidateRect (hwnd, NULL, TRUE) ;
        UpdateWindow(hwnd);


        return TRUE;

    default :return FALSE;
    }
}

BOOL CALLBACK invoice_dialog_child_enum_scroll(HWND hwnd, LPARAM lp)
{
    long s = (long) lp;
    RECT rc;
    GetWindowRect(hwnd, &rc);
    /// the above function return the coordinates relative to the top left of the screen at which\
        i am looking at. So to convert those coordinates which are relative to\
        the dialog box the following function is used
    MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rc, 2);
    /// move the window by the difference between @iVscroll and @iPrevVscroll
    MoveWindow(hwnd, rc.left, rc.top - ( s - iPrevVscroll ), rc.right-rc.left, rc.bottom - rc.top, TRUE);
    //SetWindowPos(hwnd, HWND_TOP,rc.left, rc.top - ( s - iPrevVscroll ), rc.right-rc.left, rc.bottom - rc.top,SWP_NOOWNERZORDER | SWP_NOSIZE);
}

BOOL create_listViews_invoice_dialog(HWND hwnd)
{
    /**
        this function creates the listview successfully
    */

    /// initializing the listview common control
    INITCOMMONCONTROLSEX icx;
    icx.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icx.dwICC = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx(&icx);

    RECT LV_rect = {10,25,380,375};
    /// changing to the above coordinates to coordinates in dialog units
    MapDialogRect(hwnd, &LV_rect);
    /// creating the list view control
    HWND lv_hwnd = CreateWindow(
                                WC_LISTVIEW,
                                L"",
                                WS_CHILD | LVS_REPORT | LVS_EDITLABELS | WS_VISIBLE,
                                LV_rect.left, LV_rect.top,
                                LV_rect.right - LV_rect.left,
                                LV_rect.bottom - LV_rect.top,
                                hwnd,
                                (HMENU)nid_itd_lv,
                                (HINSTANCE)GetWindowLong(GetParent(hwnd), GWL_HINSTANCE),
                                NULL
                                );
    /// adding some extended styles and adding columns
    ListView_SetExtendedListViewStyle(lv_hwnd, LVS_EX_FLATSB | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_LABELTIP );
    create_invoice_lv_columns(lv_hwnd);
}

BOOL create_invoice_lv_columns(HWND hwnd)
{
    LVCOLUMN lvc;
    lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_TEXT;
    /// some variables deceleration
    int col_fmt[5] = { LVCFMT_CENTER, LVCFMT_LEFT, LVCFMT_CENTER, LVCFMT_CENTER, LVCFMT_CENTER };
    int col_wid[5] = { 30, 90, 50, 30, 70 };
    std::vector<TCHAR*> col_nam(5);
    col_nam[0] = _T("S.No"); col_nam[1] = _T("Description"); col_nam[2] = _T("HSN"); col_nam[3] = _T("QTY"); col_nam[4] = _T("Rate");
    for(int i =0; i < 5; i++)
    {
        lvc.fmt = col_fmt[i];
        lvc.cx = col_wid[i];
        lvc.pszText = col_nam[i];
        lvc.iSubItem =  i;
        ListView_InsertColumn(hwnd, i, &lvc);
    }
}

basefile01.h

#define _UNICODE
#define UNICODE

#include <tchar.h>
#define _WIN32_IE 0x0700
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#include "main_rsc.h"

BOOL CALLBACK invoice_dialog_box_message_handle(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK invoice_dialog_child_enum_scroll(HWND, LPARAM);
BOOL create_listViews_invoice_dialog(HWND);
BOOL create_invoice_lv_columns(HWND);

main_res.h

#define MAIN_MENU 1
    #define NEW_INVOICE_DIALOG_MENU 2
    #define NEW_INVOICE_DIALOG 3
    #define NEW_INVOICE_DIALOG_MENU_save    4
    #define NEW_INVOICE_DIALOG_MENU_help    6
    #define APP_ICON_XL 7
    #define APP_ICON_S 8

    #define MAIN_MENU_file_new_invoice  102
    #define MAIN_MENU_file_new_challan  103
    #define MAIN_MENU_file_open         104
    #define MAIN_MENU_file_export_jpeg  105
    #define MAIN_MENU_file_export_pdf   106

    #define nid_ponumber_c          107
    #define nid_ponumber_t          108
    #define nid_podate_c            109
    #define nid_podate_t            110
    #define nid_vcode_c             111
    #define nid_vcode_t             112
    #define nid_inv_no_c            113
    #define nid_inv_no_t            114
    #define nid_inv_date_t          115
    #define nid_inv_date_c          116
    #define nid_seller_gstin_c      117
    #define nid_seller_gstin_t      118
    #define nid_seller_info         119
    #define nid_seller_state_c      120
    #define nid_seller_state_t      121

    #define nid_ship_info           122
    #define nid_ship_date_c         123
    #define nid_ship_date_t         124
    #define nid_ship_add_t          125
    #define nid_ship_add_c          126
    #define nid_ship_mode_t         127
    #define nid_ship_mode_c         128
    #define nid_ship_to_t           129
    #define nid_ship_to_c           130
    #define nid_ship_gstin_t        131
    #define nid_ship_gstin_c        132
    #define nid_ship_state_t        133
    #define nid_ship_state_c        134
    #define nid_order_info          135

    #define nid_bt_info             136
    #define nid_bt_to_t             137
    #define nid_bt_to_c             138
    #define nid_bt_add_t            139
    #define nid_bt_add_c            140
    #define nid_bt_gstin_t          141
    #define nid_bt_gstin_c          142
    #define nid_bt_state_t          143
    #define nid_bt_state_c          144

    #define nid_is_bt_st_same_      145

    #define nid_tax_info            146
    #define nid_tax_rc_t            147
    #define nid_tax_rc_c            148
    #define nid_tax_cgst_t          149
    #define nid_tax_cgst_c          150
    #define nid_tax_sgst_t          151
    #define nid_tax_sgst_c          152
    #define nid_tax_igst_t          153
    #define nid_tax_igst_c          154

    #define nid_itd_info            155
    #define nid_itd_lv              156
    #define nid_itd_mat_t           157
    #define nid_itd_mat_c           158
    #define nid_itd_qty_t           159
    #define nid_itd_qty_c           160
    #define nid_itd_hsn_t           161
    #define nid_itd_hsn_c           162
    #define nid_itd_rate_t          163
    #define nid_itd_rate_c          164

main_res.rc

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "../include/main_rsc.h"

///////// MENUS

MAIN_MENU MENU
{
    POPUP "File"
    {
        POPUP "New"
        {
            MENUITEM "Invoice", MAIN_MENU_file_new_invoice
            MENUITEM "Challan", MAIN_MENU_file_new_challan
        }
        MENUITEM "&open", MAIN_MENU_file_open
        POPUP "&Export"
        {
            MENUITEM "JPEG", MAIN_MENU_file_export_jpeg
            MENUITEM "PDF", MAIN_MENU_file_export_pdf
        }
    }
}

NEW_INVOICE_DIALOG_MENU MENU
{
    MENUITEM "&save", NEW_INVOICE_DIALOG_MENU_save
    MENUITEM "&help",NEW_INVOICE_DIALOG_MENU_help,HELP
}

//////////////// ICONS

APP_ICON_XL ICON "icons/icon02_256.ico"
APP_ICON_S ICON "icons/icon02_16.ico"

/**
    DIALOG resource
*/

NEW_INVOICE_DIALOG DIALOGEX 0,0,500,300
CAPTION "New Invoice"
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_VSCROLL
MENU NEW_INVOICE_DIALOG_MENU
FONT 8, "Ms Shell Dlg"
{
    GROUPBOX "Purchase Order Information", nid_order_info, 10, 10, 370, 65
    LTEXT "PO NUMBER",nid_ponumber_t,       20, 20, 50, 12
    EDITTEXT nid_ponumber_c,                70, 20, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "PO DATE", nid_podate_t,          20, 40, 50, 12
    EDITTEXT nid_podate_c,                  70, 40, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "Vendor Code", nid_vcode_t,       20, 60, 50, 12
    EDITTEXT nid_vcode_c,                   70, 60, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL

    GROUPBOX "Seller Information", nid_seller_info, 10,80, 180, 70
    LTEXT "Invoice No.", nid_inv_no_t,   20, 90, 50, 20
    EDITTEXT nid_inv_no_c,                  70, 90, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "Invoice date", nid_inv_date_t,   20, 110, 50, 12
    EDITTEXT nid_inv_date_c,                70, 110, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "GSTIN", nid_seller_gstin_t,      20, 130, 50, 12
    EDITTEXT nid_seller_gstin_c,            70, 130, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL

    GROUPBOX "Billed To", nid_bt_info, 10, 155, 180, 100
    LTEXT "Name", nid_bt_to_t, 20,170,50,12
    EDITTEXT nid_bt_to_c, 70, 170,100,12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "Address", nid_bt_add_t, 20,190,50,12
    EDITTEXT nid_bt_add_c, 70,190,100,12,  ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "GSTIN", nid_bt_gstin_t, 20, 210, 50,12
    EDITTEXT nid_bt_gstin_c, 70,210,100,12,  ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "State", nid_bt_state_t, 20, 230,50,12
    EDITTEXT nid_bt_state_c, 70,230,100,12,  ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL

    CHECKBOX "Shipped to is same as Billed to", nid_is_bt_st_same_, 200, 230, 180,20

    GROUPBOX "Shipping Information", nid_ship_info, 200, 80, 180, 140
    LTEXT "Name ", nid_ship_to_t, 210, 100, 50, 12
    EDITTEXT nid_ship_to_c, 260, 100, 100, 12,ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "Address", nid_ship_add_t , 210, 120, 50,12
    EDITTEXT nid_ship_add_c, 260, 120,100,10, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "GSTIN", nid_ship_gstin_t, 210,140,50,12
    EDITTEXT nid_ship_gstin_c, 260,140,100,12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "Date", nid_ship_date_t, 210,160,50,12
    EDITTEXT nid_ship_date_c, 260,160,100,12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "Mode", nid_ship_mode_t, 210, 180,50,12
    EDITTEXT nid_ship_mode_c, 260, 180,100,12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL
    LTEXT "State", nid_ship_state_t, 210,200,50,12
    EDITTEXT nid_ship_state_c, 260,200,100,12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_AUTOHSCROLL

    GROUPBOX "Taxing information", nid_tax_info, 10,270,370, 45
    CTEXT "CGST (%)", nid_tax_cgst_t, 25, 280, 100, 12
    EDITTEXT nid_tax_cgst_c, 25,295, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_NUMBER
    CTEXT "SGST (%)", nid_tax_sgst_t, 135, 280, 100, 12
    EDITTEXT nid_tax_sgst_c, 135,295, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_NUMBER
    CTEXT "IGST (%)", nid_tax_igst_t, 255, 280, 100, 12
    EDITTEXT nid_tax_igst_c, 255,295, 100, 12, ES_LEFT | WS_BORDER | WS_TABSTOP | WS_GROUP | ES_NUMBER

}

by the way I am using codes bock on windows vista home basics SPS2 (32 bit)


Problem

Problem is with the MoveWindow function inside invoice_dialog_child_enum_scroll function in basefile01.cpp.

whenever WM_VSCROLL is processed and the lvc is moved up and all of its column header vanishes.
enter image description here
however there is no mistake or error ( as fas as I know ) in making lisview and its columns because when i change the coordinates of the lvc, so that when it is created it is on the screen which is visible to me at that time, and the moment when I click the line down/up, page down/up and thumbtrack, those column headers vanishes.

enter image description here

and also that if you compare the size of the lvc in both the images, first one is undesirably too small.

as mush as i could debug this project, it is MoveWindow which is making this problem and SetWindowPos is no better.

I know that this post is very long but any of your suggestion would be great!
Thanks for your time.

2

There are 2 answers

1
Sanmveg saini On

As suggested by @Barmak Shemirani and @Paul Sanders, I made whole new project but this time without extra controls and only with listview.

And i have found out that the problem was with the MoveWindow inside EnumChildWindow but I don't know why?

But Check the following link you know know why and also, i have posted the code of that project.

Effect of MoveWindow in EnumChildWindows over listview inside the Dialog Box: Why ListView Header is not correctly scrolling

0
Barmak Shemirani On

EnumChildWindows will go through all child windows including descendants. Meanwhile, ListView control has a child header control, that header control ends up being moved in enum procedure. You should instead move the listview control only. You can use a for loop to go through the child windows, not the descendants:

for(HWND child = GetWindow(hwnd, GW_CHILD); child; 
        child = GetWindow(child, GW_HWNDNEXT)) { }
    ...

Also use SetScrollInfo to more accurately set the scroller bar information. Example:

std::map<HWND, int> item_positions;

BOOL CALLBACK diaproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM)
{
    static HWND hlist;
    switch(msg)
    {
    case WM_INITDIALOG:
    {
        hlist = CreateWindow(WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | LVS_REPORT,
            10, 100, 300, 2000, hwnd, NULL, NULL, NULL);
        ListView_SetExtendedListViewStyle(hlist, LVS_EX_GRIDLINES);
        std::vector<TCHAR*> col_names = { TEXT("text1"), TEXT("text2") };
        LVCOLUMN lvc;
        lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_TEXT;
        for(int i = 0; i < (int)col_names.size(); i++)
        {
            lvc.fmt = LVCFMT_LEFT;
            lvc.cx = 100;
            lvc.pszText = col_names[i];
            lvc.iSubItem = i;
            ListView_InsertColumn(hlist, i, &lvc);
        }

        RECT rc;
        GetClientRect(hwnd, &rc);

        //find the lowest point in dialog:
        RECT list_rect;
        GetClientRect(hlist, &list_rect);
        POINT pt = { list_rect.right, list_rect.bottom };
        ScreenToClient(hwnd, &pt);

        SCROLLINFO info = { sizeof(SCROLLINFO) };
        info.fMask = SIF_ALL;
        info.nMin = 0;
        info.nMax = pt.y + rc.bottom - 1;
        info.nPage = rc.bottom;
        info.fMask = SIF_ALL;
        SetScrollInfo(hwnd, SB_VERT, &info, TRUE);

        //save y-positions for later
        for(HWND child = GetWindow(hwnd, GW_CHILD); child;
            child = GetWindow(child, GW_HWNDNEXT))
        {
            GetWindowRect(child, &rc);
            pt = { rc.left, rc.top };
            ScreenToClient(hwnd, &pt);
            item_positions[child] = pt.y;
        }

        return FALSE;
    }

    case WM_VSCROLL:
    {
        SCROLLINFO info = { sizeof(info) };
        info.fMask = SIF_ALL;
        GetScrollInfo(hwnd, SB_VERT, &info);
        int pos = info.nPos;
        int track = HIWORD(wParam);
        switch(LOWORD(wParam))
        {
        case SB_LEFT: pos = info.nMin; break;
        case SB_RIGHT: pos = info.nMax; break;
        case SB_LINELEFT: pos--; break;
        case SB_LINERIGHT: pos++;  break;
        case SB_PAGELEFT: pos -= info.nPage; break;
        case SB_PAGERIGHT: pos += info.nPage; break;
        case SB_THUMBPOSITION: 
        case SB_THUMBTRACK: pos = track; break;
        }
        SetScrollPos(hwnd, SB_VERT, pos, FALSE);

        int count = 0;
        for(HWND child = GetWindow(hwnd, GW_CHILD); child; 
                child = GetWindow(child, GW_HWNDNEXT))
            count++;

        HDWP hdwp = BeginDeferWindowPos(count);
        for(HWND child = GetWindow(hwnd, GW_CHILD); child; 
            child = GetWindow(child, GW_HWNDNEXT))
        {
            RECT rc;
            GetWindowRect(child, &rc);
            POINT pt = { rc.left, rc.top }; 
            ScreenToClient(hwnd, &pt);

            if(item_positions.find(child) != item_positions.end())
            {
                int y = pos - item_positions[child];
                DeferWindowPos(hdwp, child, NULL, pt.x, -y, 0, 0, 
                    SWP_NOSIZE | SWP_NOACTIVATE);
            }
        }
        EndDeferWindowPos(hdwp);
        return TRUE;
    }

    case WM_COMMAND:
        if(LOWORD(wParam) == IDCANCEL) 
            EndDialog(hwnd, wParam); 
        return TRUE;

    default: return FALSE;
    }
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int)
{
    DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, (DLGPROC)diaproc);
    return 0;
}