Windows程序设计--设置滚动条详解一

先讲怎么用普通的方法设置滚动条。重点是设置滚动条而不是文本输出,所以示例程序的文本很简洁。

说到设置滚动条我们就会想到三点:
1.用鼠标拖动滑块使客户区内容重绘
2.用键盘控制滑块移动
3.用鼠标滚轮控制滑块移动

那么我们就把这三个功能都给用上

用到的函数有以下几个:

//设置滚动条范围
//返回TRUE表示成功,FALSE表示失败
BOOL SetScrollRange( 
  HWND hWnd, //窗口句柄
  int nBar,  //滚动条类型
  int nMinPos, //滚动条的最小位置
  int nMaxPos, //滚动条的最大位置
  BOOL bRedraw //重绘标志
); 
//滚动条位置
//0表示失败,返回先前位置的值表示成功
int SetScrollPos( 
  HWND hWnd, //窗口句柄
  int nBar, //滚动条类型
  int nPos, //滚动条新位置
  BOOL bRedraw // 重绘标志
); 
//向窗口过程发送消息              
LRESULT SendMessage(
  HWND hWnd,   //接收消息的窗口句柄
  UINT Msg,    //发送的消息
  WPARAM wParam, //附加消息
  LPARAM lParam  //附加消息
); 
//该函数查询或设置系统级参数。该函数也可以在设置参数中更新用户配置文件。          
BOOL SystemParametersInfo( 
  UINT uiAction,  // 该参数指定要查询或设置的系统级参数,MSDN有详细介绍
  UINT uiParam,  //与查询或设置的系统参数有关。
  PVOID pvParam, //与查询或设置的系统参数有关
  UINT fWinIni  //如果设置系统参数,则它用来指定是否更新用户配置文件(Profile)
);

知道并懂得怎么用这些函数后就可以开始设置我们的滚动条了。一般滚动条分为水平滚动条和垂直滚动条。所以一般我们有一下几个步骤来设置滚动条:
1.向需要设置滚动条的窗口添加滚动条风格(WS_HSCROLL|WS_VSCROLL)这个是最主要的,因为你不添加这个风格无论你在窗口过程写多少代码都是无用功。
2.在WM_CREATE消息中获取当前字体大小。

GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
cyChar = tm.tmHeight + tm.tmExternalLeading;

3.在WM_SIZE消息中获取客户区宽度和高度:

cxClient=LOWORD(lParam);//宽度
cyClient=HIWORD(lParam);//高度

4.在WM_消息中设置滚动条范围和初始位置
SetScrollRange
SetScrollPos
5.在WM_VSCROLL和WM_HSCROLL消息中设置新的滑块位置
6.在WM_KETDOWN消息中利用SendMessage发送消息给WM_VSCROLL和WM_HSCROLL以设置新的滑块位置
7.滚轮滑动利用SendMessage发送消息给WM_VSCROLL和WM_HSCROLL以设置新的滑块位置
8.在WM_PAINT消息中显示滚动滑块后的内容
基本就以上8点就可以设置滚动条了。细节更改可以自己研究.

设置滚动条代码如下:


#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//全局变量存储错误信息
DWORD dwError = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Scroll");
    HWND         hwnd=nullptr;
    MSG          msg = {0};
    WNDCLASS     wndclass = {0};
    //创建窗口类
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = nullptr;
    wndclass.lpszClassName = szAppName;
    //注册窗口类
    if (!RegisterClass(&wndclass))
    {
        MessageBox(nullptr, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }
    //创建窗口
    hwnd = CreateWindow(szAppName, TEXT("ScrollDemo"),
        WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL,//添加滚动条风格
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        nullptr, nullptr, hInstance, nullptr);
    if (!hwnd)
    { 
        dwError = GetLastError();
        return 0;
    }
    //显示窗口
    ShowWindow(hwnd, iCmdShow);
    //更新窗口
    UpdateWindow(hwnd);
    //获取队列中的消息
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}



//窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //字体的宽度,高度,cxCaps是宽字符的宽度
    static int  cxChar = 0, cxCaps = 0, cyChar = 0;
    //滚动条的垂直位置和水平位置
    static int iVertPos = 0, iHorzPos = 0;
    //获取当前客户区可容纳的行数和列数
    static int cxColumn = 0, cyLine = 0;
    //客户区宽度和高度
    static int cxClient = 0, cyClient = 0;
    //鼠标滑动信息
    static int  iDeltaPerLine = 0, iAccumDelta = 0;
    ULONG       ulScrollLines = 0;
    int i = 0, temp = 0, ix = 0, iy = 0;
    //设备环境句柄
    HDC         hdc = nullptr;
    //绘图信息结构
    PAINTSTRUCT ps = { 0 };
    //字体信息结构
    TEXTMETRIC  tm = { 0 };

    switch (message)
    {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        //获取字体信息,一般系统默认
        GetTextMetrics(hdc, &tm);
        //字符宽度
        cxChar = tm.tmAveCharWidth;
        //判断是否是宽字符,宽字符就是1.5cxChar
        cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
        //字符高度
        cyChar = tm.tmHeight + tm.tmExternalLeading;

        ReleaseDC(hwnd, hdc);
    case WM_SETTINGCHANGE:
        //SPI_GETWHEELSCROLLLINES:用于Windows NT 4.0及以后版本、
        //Windows 98。当前轨迹球转动时,获取滚动的行数。参数pvParam
        //必须指向UINT类型变量以接收行数。缺省值是3。
        if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0))
        {
            dwError = GetLastError();
            break;
        }

        // 增量数值用WHEEL_DELTA来标识,它等于120

        if (ulScrollLines)//每滚动一行的增量是40
            iDeltaPerLine = WHEEL_DELTA / ulScrollLines;
        else
            iDeltaPerLine = 0;

        return 0;

    case WM_SIZE:
        //获取客户区宽度和高度,每当你改变窗口大小的时候它
        //都会发生改变。
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        //获取当前客户区可容纳的列数
        cxColumn = cxClient / cxChar;
        //获取当前客户区可容纳的行数
        cyLine = cyClient / cyChar;

        //设置垂直滚动条范围
        if (!SetScrollRange(hwnd, SB_VERT, 0, cyClient / cyChar, FALSE))
        {
            dwError = GetLastError();
            break;
        }
        //设置垂直滚动条初始位置
        SetScrollPos(hwnd, SB_VERT, iVertPos, TRUE);

        //设置水平滚动条范围 
        if (!SetScrollRange(hwnd, SB_HORZ, 0, cxClient / cxChar, TRUE))
        {
            dwError = GetLastError();
            break;
        }
        //设置垂直滚动条初始位置   
        SetScrollPos(hwnd, SB_HORZ, iHorzPos, TRUE);

        return 0;

      //垂直滚动条消息
    case WM_VSCROLL:

        //记录未改变时的滑块位置
        temp = iVertPos;
        //接收到用户拖动滑块消息
        switch (LOWORD(wParam))
        {
            //向上移动一个单位
        case SB_LINEUP:
            iVertPos -= 1;
            break;
            //想下移动一个单位
        case SB_LINEDOWN:
            iVertPos += 1;
            break;
            //翻上一页
        case SB_PAGEUP:
            iVertPos -= cyClient / cyChar;
            break;
            //翻下一页
        case SB_PAGEDOWN:
            iVertPos += cyClient / cyChar;
            break;
        case SB_TOP:
            iVertPos = 0;
            break;
        case SB_BOTTOM:
            iVertPos = cyClient / cyChar;
            break;
        case SB_THUMBTRACK:
            iVertPos = HIWORD(wParam);
            break;
        default:
            break;
        }
        //防止范围超出
        iVertPos = max(0, min(iVertPos, cyClient / cyChar));
        //当滑块改变的时候重新显示它
        if (temp != iVertPos)
        {
            //设置新的滑块位置
            SetScrollPos(hwnd, SB_VERT, iVertPos, TRUE);
            //使客户区无效,以便重绘它
            InvalidateRect(hwnd, nullptr, TRUE);
        }


        return 0;
        //水平滚动条消息
    case WM_HSCROLL:

        //记录未改变时的滑块位置
        temp = iHorzPos;
        //接收到用户拖动滑块消息
        switch (LOWORD(wParam))
        {
            //向左移动一个单位
        case SB_LINELEFT:
            iHorzPos -= 1;
            break;
            //想下移动一个单位
        case SB_LINERIGHT:
            iHorzPos += 1;
            break;
            //翻上一页
        case SB_PAGELEFT:
            iHorzPos -= cyClient / cyChar;
            break;
            //翻下一页
        case SB_PAGERIGHT:
            iHorzPos += cyClient / cyChar;
            break;
        case SB_THUMBTRACK:
            iHorzPos = HIWORD(wParam);
            break;
        default:
            break;
        }
        //防止范围超出
        iHorzPos = max(0, min(iHorzPos, cxClient / cxChar));
        //当滑块改变的时候重新显示它
        if (temp != iHorzPos)
        { 
           //设置新的滑块位置
            SetScrollPos(hwnd, SB_HORZ, iHorzPos, TRUE);
            //使客户区无效,以便于重绘
            InvalidateRect(hwnd, nullptr, TRUE);
        }

        return 0;

        //键盘消息
    case WM_KEYDOWN:

        //如果有按下方向键
        switch (LOWORD(wParam))
        {
            //如果按下方向键上
        case VK_UP:
            //想窗口过程发送消息
            SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
            break;
        case VK_DOWN:
            SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
            break;
        case VK_LEFT:
            SendMessage(hwnd, WM_HSCROLL, SB_LINELEFT, 0);
            break;
        case VK_RIGHT:
            SendMessage(hwnd, WM_HSCROLL, SB_LINERIGHT, 0);
            break;
            //PageUp
        case VK_PRIOR:
            SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
            break;
            //PageDown
        case VK_NEXT:
            SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
            //Bottom
        case VK_END:
            SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
            break;
        case VK_HOME:
            SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
            break;
        default:
            break;
        }
        return 0;

        //鼠标滚轮消息
    case WM_MOUSEWHEEL:
        //如果增量等于0就是没有滚动就退出
        if (iDeltaPerLine == 0)
            break;

        iAccumDelta += (short)HIWORD(wParam);     // 120 or -120

        while (iAccumDelta >= iDeltaPerLine)
        {
            SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
            iAccumDelta -= iDeltaPerLine;
        }

        while (iAccumDelta <= -iDeltaPerLine)
        {
            SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
            iAccumDelta += iDeltaPerLine;
        }

        return 0;
        //重绘
    case WM_PAINT:

        hdc = BeginPaint(hwnd, &ps);

        for (;i < cyLine;i++)
        {
            //当滑块位置改变时,输出的起始位置也随之改变
            //其原理就是把不需要输出的输出到屏幕外面
            ix = cxChar*(1 - iHorzPos);
            iy = cyChar * (i - iVertPos);
            TextOutW(hdc, ix, iy, L"一二三四五六", wcslen(L"一二三四五六"));
        }

        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

代码中已经有很详细的注释了,所以这里大概再理清一下代码就行了。
1.
当我们创建窗口的时候会先触发WM_CREATE消息,这时,字体大小信息被获取到了,因为没有这个字体大小的信息,你根本不懂一个客户区可以容纳的行数和列数。
2.
接着会触发WM_SIZE消息,我们的客户区大小也能获取到了,然后就可以设置滚动条的范围和初始位置了。
3.
将滚动条初始化后就会触发WM_PAINT消息,文本和滚动条就会在窗口显示了。但是WM_HSCROLL和WM_VSCROLL这两个消息还没被触发,因为你并没有移动滑块。
4.
当你移动滑块后就会触发对应的水平或垂直滚动条消息,接着滑块的位置就会在里面被改变。
5.最后就会被重绘在客户区中。

键盘消息和鼠标滚轮消息对滑块的改变是通过SendMessage函数发送对应的消息到滚动条消息中,这样就可以达到键盘鼠标移动滑块的效果了。

滚动条消息如下:

#define SB_LINEUP       0

#define SB_LINELEFT           0

#define SB_LINEDOWN           1

#define SB_LINERIGHT          1

#define SB_PAGEUP         2

#define SB_PAGELEFT           2

#define SB_PAGEDOWN           3

#define SB_PAGERIGHT          3

#define SB_THUMBPOSITION   4

#define SB_THUMBTRACK         5

#define SB_TOP                6

#define SB_LEFT           6

#define SB_BOTTOM        7

#define SB_RIGHT          7

#define SB_ENDSCROLL          8

其中的SB_THUMBTRACK 和SB_THUMBPOSITION只能处理一个,前者是设置滑块及时显示,后者是设置滑块被释放后显示.

WM_VSCROLL和WM_HSCROLL中的wParam和lParam参数:

wParam消息参数被分为一个低字组和一个高字组。wParam的低字组是一个数值,它指出了鼠标对滚动条进行的操作。

而一般lParam在控件窗口中才有意义,所以这里不必管它.

关于虚拟键消息可以百度。这里不详细讲。滚动条的设置还可以利用一个滚动条信息结构和两个函数。这种方法是比较常用的,下次会详细讲解它。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值