Coin163

首页 > Windows程序设计--设置滚动条详解二

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

2021腾讯云限时秒杀,爆款1核2G云服务器298元/3年!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1062

2021阿里云最低价产品入口+领取代金券(老用户3折起),
入口地址https://www.aliyun.com/minisite/goods

用以下一个结构和三个函数就能设置滚动条了
1.滚动条信息结构

//SetScrollInfo函数设置这个结构的信息
//GetScrollInfo函数返回这个结构的信息
typedef struct tagSCROLLINFO {
         UINT cbSize; //存储这个结构的大小,以字节为单位
         UINT fMask;  //标志这个结构的哪些值被设置或被获取
         int nMin;    //滚动条的最小位置
         int nMax;    //滚动条的最大位置//一般这两个参数一起构成滚动条范围
         UINT nPage;  //页面大小
         int nPos;    //滚动条位置
         int nTrackPos; //可以随着用户的拖动而显示当前滚动条位置
} SCROLLINFO; 
typedef SCROLLINFO FAR* LPSCROLLINFO; 
/* fMask: SIF_ALL:结构中参数全部有效(都可以被用) SIF_DISABLENOSCROLL:这个值是只有当设置滚动条的参数使用。如果滚动条的新参数使滚动条不必要的,禁用滚动条,而不是删除它。 SIF_PAGE:nPage有效 SIF_POS:nPos有效 SIF_RANGE:nMin,nMax有效 SIF_TRACKPOS:nTrackPos有效 */

2.获取滚动条信息

BOOL GetScrollInfo( 
  HWND hwnd,   //窗口句柄
  int fnBar,   //指定返回哪个滚动条的参数,SB_CTL(控件),SB_HORZ(水平滚动条),SB_VERT(垂直滚动条)
  LPSCROLLINFO lpsi //滚动条信息结构
); 

3.设置滚动条信息

int SetScrollInfo( 
  HWND hwnd,   //窗口句柄 
  int fnBar,   //指定哪个滚动条的参数
  LPSCROLLINFO lpsi, //滚动条信息结构 
  BOOL fRedraw    //是否重画滚动条,也就是即时显示滚动条
                  //TRUE表示重画,FALSE表示不重画
); 

4.滚动窗口

BOOL ScrollWindow(         
    HWND hWnd,       //窗口句柄
    int XAmount,     //指定水平滚动以设备为单位的数量。如果窗体被滚动模式为
                     //CS_OWNDC或CS_CLASSDC,此参数则使用逻辑单位而不使用设备单位。
                     //当向左滚动窗体内容时,参数值必须为负。

    int YAmount,  //指定垂直滚动以设备为单位的数量。
    const RECT *lpRect,//指向所指定将被滚动的客户区域部分的RECT结构。若此参数为NULL,则整个客户区域均被滚动。
    const RECT *lpClipRect//指向包含类似于剪辑滚动条RECT结构。只有剪辑矩形条内部的位受影响。
                          //由外向内的滚动矩形内部被着色,而由矩形内向外的滚动将不被着色。
);

主要步骤:
1.向窗口添加滚动条风格
2.WM_CREATE消息中设置字体信息.
3.在WM_SZIE消息中获取客户区宽度和高度
4.在WM_SIZE消息中设置滚动条范围和位置
5.在WM_VSCROLL和WM_HSCROLL消息中滚动滑块的位置
6.在WM_PAINT消息中显示滚动滑块后的内容

详细代码如下:


#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 };
    //滚动条信息结构
    SCROLLINFO si = {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;

        //设置垂直滚动条范围和初始位置
        si.cbSize = sizeof(si);//要使用这个结构,必须要把它的大小传入进去
        si.fMask = SIF_RANGE | SIF_PAGE|SIF_POS;//范围、页面大小和位置三个参数有效
        si.nMin = 0;//设置范围的最小值
        si.nMax = cyClient/cyChar+200;//设置范围的最大值后面的根据实际值来加
        si.nPos = 0;//设置滚动条初始位置
        si.nPage= cyClient / cyChar;//设置页面大小
        //用SetScrollInfo函数设置垂直滚动条信息(结构中的值传入函数)
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

        //设置水平滚动条范围和初始位置
        si.cbSize = sizeof(si);//要使用这个结构,必须要把它的大小传入进去
        si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;//范围、页面大小和位置三个参数有效
        si.nMin = 0;//设置范围的最小值
        si.nMax = cxClient / cxChar+20;//设置范围的最大值
        si.nPos = 0;//设置滚动条初始位置
        si.nPage = cxClient / cxChar;//设置页面大小
        //用SetScrollInfo函数设置水平滚动条信息(结构中的值传入函数)
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

        return 0;


    case WM_VSCROLL:

         //获取垂直滚动条全部信息
        si.cbSize = sizeof(si);//不管设置还是获取都要有这个
        si.fMask = SIF_ALL;//获取所有参数信息
        GetScrollInfo(hwnd, SB_VERT, &si);
        //记录未改变时的滑块位置
        iVertPos = si.nPos;
        //接收到用户拖动滑块消息
        switch (LOWORD(wParam))
        {
            //向上移动一个单位
        case SB_LINEUP:
            si.nPos -= 1;
            break;
            //想下移动一个单位
        case SB_LINEDOWN:
            si.nPos += 1;
            break;
            //翻上一页
        case SB_PAGEUP:
            si.nPos -= si.nPage;
            break;
            //翻下一页
        case SB_PAGEDOWN:
            si.nPos += si.nPage;
            break;
        case SB_TOP:
            si.nPos = si.nMin;
            break;
        case SB_BOTTOM:
            si.nPos = si.nMax;
            break;
        case SB_THUMBTRACK:
            si.nPos = si.nTrackPos;
            break;
        default:
            break;
        }
        //把新的滑块位置保存到结构中
        si.fMask = SIF_POS;
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
        GetScrollInfo(hwnd, SB_VERT, &si);
        //当滑块改变的时候重新显示它
        if (si.nPos != iVertPos)
        {
            //滚动窗口
            ScrollWindow(hwnd, 0, cyChar*(iVertPos - si.nPos), nullptr, nullptr);
            UpdateWindow(hwnd);
        }


        return 0;
    case WM_HSCROLL:

        //获取水平滚动条全部信息
        si.cbSize = sizeof(si);
        si.fMask = SIF_ALL;
        GetScrollInfo(hwnd, SB_HORZ, &si);
        //记录未改变时的滑块位置
        iHorzPos=si.nPos;
        //接收到用户拖动滑块消息
        switch (LOWORD(wParam))
        {
            //向左移动一个单位
        case SB_LINELEFT:
            si.nPos -= 1;
            break;
            //想下移动一个单位
        case SB_LINERIGHT:
            si.nPos += 1;
            break;
            //翻上一页
        case SB_PAGELEFT:
            si.nPos -= si.nPage;
            break;
            //翻下一页
        case SB_PAGERIGHT:
            si.nPos += si.nPage;
            break;
        case SB_THUMBTRACK:
            si.nPos = si.nTrackPos;
            break;
        default:
            break;
        }
        //把新的滑块位置保存到结构中
        si.fMask = SIF_POS;
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
        GetScrollInfo(hwnd, SB_HORZ, &si);

        //当滑块改变的时候重新显示它
        if (si.nPos != iHorzPos)
        {
            ScrollWindow(hwnd, cxChar*(iHorzPos - si.nPos), 0, nullptr, nullptr);
            UpdateWindow(hwnd);
        }

        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);
        //获取垂直滚动条位置
        si.cbSize = sizeof(si);
        si.fMask = SIF_POS;
        GetScrollInfo(hwnd, SB_VERT, &si);
        iVertPos = si.nPos;

        // 获取水平滚动条位置
        GetScrollInfo(hwnd, SB_HORZ, &si);
        iHorzPos = si.nPos;

        for (int i = 0;i < 70;i++)
        {
            ix = cxChar*(1 - iHorzPos);
            iy = cyChar*(i - iVertPos);
            TextOut(hdc,ix,iy,L"1234567845623132",wcslen(L"1234567845623132") );
        }

        EndPaint(hwnd, &ps);
        return 0;

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

要注意几点:
1.设置范围si.nMax的时候,它决定了你能滑动多少行。比如你要显示100行,但是客户区只能显示50行,那么你的si.nMax就要设置为cyClient/cyChar(客户区大小)+50(或大于50)才能显示完.
2.初始位置可以自己设置,一般系统默认初始化为0了。
3.si.cbSize一点要设置,不管是在设置或获取结构信息的时候,都要把结构体大小传入。
4.在WM_VSCROLL和WM_HSCROLL中,处理滑块移动前要把当前位置保存下来,处理完后把现在的位置保存起来,比较是否一样,不一样代表滑块移动了,就滚动窗口。

这些都是手动设置滚动条的范围,而不能根据输入多少文本,滚动条范围随之改变。

原文

用以下一个结构和三个函数就能设置滚动条了 1.滚动条信息结构 //SetScrollInfo函数设置这个结构的信息//GetScrollInfo函数返回这个结构的信息typedef struct tagSCROLLINFO { UINT c

------分隔线----------------------------