Windows对话框和滚动条

Windows对话框和滚动条

cheungmine 2010-1

 

尽管在Windows平台上编程多年,对滚动条的理解还是很肤浅。尤其给对话框添加滚动条。参考下面的文章:

 

http://www.codeproject.com/KB/dialog/scroll_dialog.aspx

 

Scrollable Dialog in Pure Win32 API

By Alex Blekhman

 

可以给纯Win32 SDK的程序极为方便地添加滚动条:水平或垂直。


我的目的是给WTL写的程序添加滚动条。我以WTL开发Windows Mobile 6程序为例,说明如何给这样的对话框添加滚动条。


首先是用VS2005+WTL8.0创建一个WTL Mobile Application Wizard项目,WceDlg。

Platforms选择:Windows Mobile 6 Professional SDK。

Application Type:勾选Dialog Based。

其余默认,按Finish。

 

我仅修改系统自动生成的对话框类文件:WceDlgDialog.h

 

// WceDlgDialog.h : interface of the CWceDlgDialog class
// cheungmine 2010
/////////////////////////////////////////////////////////////////////////////

#pragma once

////////////////////////////////////////////////////////////////
// From:
//   http://www.codeproject.com/KB/dialog/scroll_dialog.aspx
//   Scrollable Dialog in Pure Win32 API
//   By Alex Blekhman
//

static BOOL SD_OnInitDialog(HWND hwnd, HWND /*hwndFocus*/, LPARAM /*lParam*/)
{
    RECT rc = {};
    GetClientRect(hwnd, &rc);

    const SIZE sz = { rc.right - rc.left, rc.bottom - rc.top };

    SCROLLINFO si = {};
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
    si.nPos = si.nMin = 1;

    si.nMax = sz.cx;
    si.nPage = sz.cx;
    SetScrollInfo(hwnd, SB_HORZ, &si, FALSE);

    si.nMax = sz.cy;
    si.nPage = sz.cy;
    SetScrollInfo(hwnd, SB_VERT, &si, FALSE);

    return FALSE;
}

static void SD_OnCommand(HWND hwnd, int id, HWND /*hwndCtl*/, UINT /*codeNotify*/)
{
    switch(id)
    {
    case IDOK:
    case IDCANCEL:
        EndDialog(hwnd, 0);
        break;
    }
}

static void SD_ScrollClient(HWND hwnd, int bar, int pos)
{
    static int s_prevx = 1;
    static int s_prevy = 1;

    int cx = 0;
    int cy = 0;

    int& delta = (bar == SB_HORZ ? cx : cy);
    int& prev = (bar == SB_HORZ ? s_prevx : s_prevy);

    delta = prev - pos;
    prev = pos;

    if(cx || cy)
    {
        ::ScrollWindowEx(hwnd, cx, cy, NULL, NULL, 0, 0, SW_SCROLLCHILDREN);
    }
}

static void SD_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    if(state != SIZE_RESTORED && state != SIZE_MAXIMIZED)
        return;

    SCROLLINFO si = {};
    si.cbSize = sizeof(SCROLLINFO);

    const int bar[] = { SB_HORZ, SB_VERT };
    const int page[] = { cx, cy };

    for(size_t i = 0; i < sizeof(bar)/sizeof(bar[0]); ++i)
    {
        si.fMask = SIF_PAGE;
        si.nPage = page[i];
        SetScrollInfo(hwnd, bar[i], &si, TRUE);

        si.fMask = SIF_RANGE | SIF_POS;
        GetScrollInfo(hwnd, bar[i], &si);

        const int maxScrollPos = si.nMax - (page[i] - 1);

        // Scroll client only if scroll bar is visible and window's
        // content is fully scrolled toward right and/or bottom side.
        // Also, update window's content on maximize.
        const bool needToScroll =
            (si.nPos != si.nMin && si.nPos == maxScrollPos) ||
            (state == SIZE_MAXIMIZED);

        if(needToScroll)
        {
            SD_ScrollClient(hwnd, bar[i], si.nPos);
        }
    }
}

static int SD_GetScrollPos(HWND hwnd, int bar, UINT code)
{
    SCROLLINFO si = {};
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
    GetScrollInfo(hwnd, bar, &si);

    const int minPos = si.nMin;
    const int maxPos = si.nMax - (si.nPage - 1);

    int result = -1;

    switch(code)
    {
    case SB_LINEUP   /*SB_LINELEFT*/:
        result = max(si.nPos - 1, minPos);
        break;

    case SB_LINEDOWN /*SB_LINERIGHT*/:
        result = min(si.nPos + 1, maxPos);
        break;

    case SB_PAGEUP   /*SB_PAGELEFT*/:
        result = max(si.nPos - (int)si.nPage, minPos);
        break;

    case SB_PAGEDOWN /*SB_PAGERIGHT*/:
        result = min(si.nPos + (int)si.nPage, maxPos);
        break;

    case SB_THUMBPOSITION:
        // do nothing
        break;

    case SB_THUMBTRACK:
        result = si.nTrackPos;
        break;

    case SB_TOP    /*SB_LEFT*/:
        result = minPos;
        break;

    case SB_BOTTOM /*SB_RIGHT*/:
        result = maxPos;
        break;

    case SB_ENDSCROLL:
        // do nothing
        break;
    }

    return result;
}

static void SD_OnHVScroll(HWND hwnd, int bar, UINT code)
{
    const int scrollPos = SD_GetScrollPos(hwnd, bar, code);

    if(scrollPos == -1)
        return;

    SetScrollPos(hwnd, bar, scrollPos, TRUE);
    SD_ScrollClient(hwnd, bar, scrollPos);
}

static void SD_OnHScroll(HWND hwnd, HWND /*hwndCtl*/, UINT code, int /*pos*/)
{
    SD_OnHVScroll(hwnd, SB_HORZ, code);
}

static void SD_OnVScroll(HWND hwnd, HWND /*hwndCtl*/, UINT code, int /*pos*/)
{
    SD_OnHVScroll(hwnd, SB_VERT, code);
}

////////////////////////////////////////////////////////////////


class CWceDlgDialog :
    public CAppStdOrientedDialogImpl<CWceDlgDialog>,
    public CUpdateUI<CWceDlgDialog>,
    public CMessageFilter, public CIdleHandler
{
public:
    DECLARE_APP_DLG_CLASS(NULL, IDR_MAINFRAME, L"Software//WTL//WceDlg")

 

    // 资源中,把下面的对话框拉的大大的,上面放一些Button,Edit之类的控件。

    // 并设置对话框垂直和水平滚动条为true
    enum { IDD = IDD_MAINDLG };


    virtual BOOL PreTranslateMessage(MSG* pMsg)
    {
        return CWindow::IsDialogMessage(pMsg);
    }

// CAppWindow operations
    bool AppHibernate( bool bHibernate)
    {
        // Insert your code here or delete member if not relevant
        return bHibernate;
    }

    bool AppNewInstance( LPCTSTR lpstrCmdLine)
    {
        // Insert your code here or delete member if not relevant
        return false;
    }

    void AppSave()
    {
        CAppInfo info;
        // Insert your code here or delete member if not relevant
    }

    virtual BOOL OnIdle()
    {
        return FALSE;
    }

    BEGIN_UPDATE_UI_MAP(CWceDlgDialog)
    END_UPDATE_UI_MAP()


    BEGIN_MSG_MAP(CWceDlgDialog)
        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

        // add by cheungmine
        MESSAGE_HANDLER(WM_COMMAND, OnCommand)   
        MESSAGE_HANDLER(WM_SIZE, OnSize)
        MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
        MESSAGE_HANDLER(WM_VSCROLL, OnVScroll)


        COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
        CHAIN_MSG_MAP(CAppStdOrientedDialogImpl<CWceDlgDialog>)
    END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):
//    LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//    LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//    LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

    LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        HWND hMenuBar = CreateMenuBar(ATL_IDM_MENU_DONECANCEL);
        UIAddToolBar(hMenuBar);
        UIAddChildWindowContainer(m_hWnd);

        // add by cheungmine
        SD_OnInitDialog(m_hWnd, (HWND)(wParam), lParam);

       
        // register object for message filtering and idle updates
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        ATLASSERT(pLoop != NULL);
        pLoop->AddMessageFilter(this);
        pLoop->AddIdleHandler(this);

        return bHandled = FALSE;
    }

    // add by cheungmine
    LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SD_OnCommand(m_hWnd, (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam));

        // 交由系统继续处理
        return bHandled = FALSE;
    }

    // add by cheungmine
    LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SD_OnSize(m_hWnd, (UINT)(wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam));

        // 交由系统继续处理
        return bHandled = FALSE;
    }

    // add by cheungmine
    LRESULT OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SD_OnHScroll(m_hWnd, (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam));
       
        return 0;
    }

    // add by cheungmine
    LRESULT OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SD_OnVScroll(m_hWnd, (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam));
       
        return 0;
    }



    LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
        CAboutDlg dlg;
        dlg.DoModal();
        return 0;
    }
};

 

 

编译并部署到设备,可以看到对话框正确地设置了滚动条。再次感谢 Alex Blekhman.

下面是 Alex Blekhman的原代码

 

////////////////////////////////////////////////////////////////////////////////
//
#include "StdAfx.h"

INT_PTR CALLBACK SD_DialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

////////////////////////////////////////////////////////////////////////////////
//
int WINAPI _tWinMain(
    HINSTANCE hInstance,
    HINSTANCE /*hPrevInstance*/,
    LPWSTR /*lpCmdLine*/,
    int /*nShowCmd*/)
{
    DialogBox(
        hInstance,
        MAKEINTRESOURCE(IDD_DIALOG1),
        GetDesktopWindow(),
        SD_DialogProc);

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
BOOL SD_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
void SD_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
void SD_OnSize(HWND hwnd, UINT state, int cx, int cy);
void SD_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
void SD_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
void SD_OnHVScroll(HWND hwnd, int bar, UINT code);
void SD_ScrollClient(HWND hwnd, int bar, int pos);
int SD_GetScrollPos(HWND hwnd, int bar, UINT code);

INT_PTR CALLBACK SD_DialogProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch(uMsg)
    {
    HANDLE_MSG(hwndDlg, WM_INITDIALOG, SD_OnInitDialog);
    HANDLE_MSG(hwndDlg, WM_COMMAND, SD_OnCommand);
    HANDLE_MSG(hwndDlg, WM_SIZE, SD_OnSize);
    HANDLE_MSG(hwndDlg, WM_HSCROLL, SD_OnHScroll);
    HANDLE_MSG(hwndDlg, WM_VSCROLL, SD_OnVScroll);
    }

    return FALSE;
}

BOOL SD_OnInitDialog(HWND hwnd, HWND /*hwndFocus*/, LPARAM /*lParam*/)
{
    RECT rc = {};
    GetClientRect(hwnd, &rc);

    const SIZE sz = { rc.right - rc.left, rc.bottom - rc.top };

    SCROLLINFO si = {};
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
    si.nPos = si.nMin = 1;

    si.nMax = sz.cx;
    si.nPage = sz.cx;
    SetScrollInfo(hwnd, SB_HORZ, &si, FALSE);

    si.nMax = sz.cy;
    si.nPage = sz.cy;
    SetScrollInfo(hwnd, SB_VERT, &si, FALSE);

    return FALSE;
}

void SD_OnCommand(HWND hwnd, int id, HWND /*hwndCtl*/, UINT /*codeNotify*/)
{
    switch(id)
    {
    case IDOK:
    case IDCANCEL:
        EndDialog(hwnd, 0);
        break;
    }
}

void SD_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    if(state != SIZE_RESTORED && state != SIZE_MAXIMIZED)
        return;

    SCROLLINFO si = {};
    si.cbSize = sizeof(SCROLLINFO);

    const int bar[] = { SB_HORZ, SB_VERT };
    const int page[] = { cx, cy };

    for(size_t i = 0; i < ARRAYSIZE(bar); ++i)
    {
        si.fMask = SIF_PAGE;
        si.nPage = page[i];
        SetScrollInfo(hwnd, bar[i], &si, TRUE);

        si.fMask = SIF_RANGE | SIF_POS;
        GetScrollInfo(hwnd, bar[i], &si);

        const int maxScrollPos = si.nMax - (page[i] - 1);

        // Scroll client only if scroll bar is visible and window's
        // content is fully scrolled toward right and/or bottom side.
        // Also, update window's content on maximize.
        const bool needToScroll =
            (si.nPos != si.nMin && si.nPos == maxScrollPos) ||
            (state == SIZE_MAXIMIZED);

        if(needToScroll)
        {
            SD_ScrollClient(hwnd, bar[i], si.nPos);
        }
    }
}

void SD_OnHScroll(HWND hwnd, HWND /*hwndCtl*/, UINT code, int /*pos*/)
{
    SD_OnHVScroll(hwnd, SB_HORZ, code);
}

void SD_OnVScroll(HWND hwnd, HWND /*hwndCtl*/, UINT code, int /*pos*/)
{
    SD_OnHVScroll(hwnd, SB_VERT, code);
}

void SD_OnHVScroll(HWND hwnd, int bar, UINT code)
{
    const int scrollPos = SD_GetScrollPos(hwnd, bar, code);

    if(scrollPos == -1)
        return;

    SetScrollPos(hwnd, bar, scrollPos, TRUE);
    SD_ScrollClient(hwnd, bar, scrollPos);
}

void SD_ScrollClient(HWND hwnd, int bar, int pos)
{
    static int s_prevx = 1;
    static int s_prevy = 1;

    int cx = 0;
    int cy = 0;

    int& delta = (bar == SB_HORZ ? cx : cy);
    int& prev = (bar == SB_HORZ ? s_prevx : s_prevy);

    delta = prev - pos;
    prev = pos;

    if(cx || cy)
    {
        ScrollWindowEx(hwnd, cx, cy, NULL, NULL, 0, 0, 0);
    }
}

int SD_GetScrollPos(HWND hwnd, int bar, UINT code)
{
    SCROLLINFO si = {};
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
    GetScrollInfo(hwnd, bar, &si);

    const int minPos = si.nMin;
    const int maxPos = si.nMax - (si.nPage - 1);

    int result = -1;

    switch(code)
    {
    case SB_LINEUP /*SB_LINELEFT*/:
        result = max(si.nPos - 1, minPos);
        break;

    case SB_LINEDOWN /*SB_LINERIGHT*/:
        result = min(si.nPos + 1, maxPos);
        break;

    case SB_PAGEUP /*SB_PAGELEFT*/:
        result = max(si.nPos - (int)si.nPage, minPos);
        break;

    case SB_PAGEDOWN /*SB_PAGERIGHT*/:
        result = min(si.nPos + (int)si.nPage, maxPos);
        break;

    case SB_THUMBPOSITION:
        // do nothing
        break;

    case SB_THUMBTRACK:
        result = si.nTrackPos;
        break;

    case SB_TOP /*SB_LEFT*/:
        result = minPos;
        break;

    case SB_BOTTOM /*SB_RIGHT*/:
        result = maxPos;
        break;

    case SB_ENDSCROLL:
        // do nothing
        break;
    }

    return result;
}

 

 


 

 

你可能感兴趣的:(windows,command,application,insert,dialog,scroll)