• <Win32_8>由浅入深——滚动条


    滚动条在Win32程序中是非常常见的一个控件,它的功能和地位也就不言而喻了,在文本输出中算是一个难点……

    我将借用P先生的思路讲述两种不同风格滚动条,下面切入主题:(实例程序都是显示一张位图  当然,位图比客户区大得多,以显示出滚动条的作用)

    (1)不可变长的滚动条

    这是16windows中的版本,windows1.0就有了这个玩意儿

    它的使用主要依托以下两个API函数:

    设定滚动条范围:

    BOOL SetScrollRange(
      HWND hWnd,    // 窗口句柄
      int nBar,     // 滚动条的类型(SB_VERT、SB_HORZ、SB_CTL)
      int nMinPos,  // 范围的下边界
      int nMaxPos,  // 范围的上边界
      BOOL bRedraw  // 是否重绘滚动条块
    );

    设定滚动条的位置:

    int SetScrollPos(
      HWND hWnd,
      int nBar,
      int nPos,      // 滚动条的新位置
      BOOL bRedraw
    );

    获取滚动条的位置:

    int GetScrollPos(
      HWND hWnd,
      int nBar
    );

    我们先来瞧一瞧这种风格的滚动条的效果:

    它们的使用应该还是比较简单,只是要注意两点:

           1)图片显示移动的方向和滚动条的滚动方向应该是相反的,所以代码中你会看到贴位图的位置是一个负值

       BitBlt(hdc, -iHscrollPos, -iVscrollPos, cxBitmap, cyBitmap, hdcMem, 0, 0, SRCCOPY);

           2)这种风格的滚动条需要我们程序员自行控制它的范围,使显示的位置始终在规定的范围内

        iVscrollPos = max(0, min(iVscrollPos, cyBitmap - cyClient));//垂直滚动条位置
        iHscrollPos = max(0, min(iHscrollPos, cxBitmap - cxClient));//水平滚动条位置

    主要代码详解:

    加载位图,这个和之前讲的位图的应用是一样的

    case WM_CREATE:
    	//加载位图
    	hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
    	hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
    	GetObject(hBitmap, sizeof(BITMAP), &bitmap);
    
    	cxBitmap = bitmap.bmWidth;
    	cyBitmap = bitmap.bmHeight;
    	return 0 ;


    设定滚动条参数

    	case WM_SIZE:
    		cxClient = LOWORD(lParam);
    		cyClient = HIWORD(lParam);
    
    		//当窗口大小改变时 , 重新判断水平、垂直滚动条位置是否超出范围
    		iVscrollPos = max(0, min(iVscrollPos, cyBitmap - cyClient));
    		iHscrollPos = max(0, min(iHscrollPos, cxBitmap - cxClient));
    
    		//设定水平滚动条
    		SetScrollRange(hwnd, SB_HORZ, 0, cxBitmap - cxClient, FALSE);
    		SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE);
    		
    		//设定垂直滚动条
    		SetScrollRange(hwnd, SB_VERT, 0, cyBitmap - cyClient, FALSE);
    		SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
    		return 0;


    滚动条消息处理(这里只给出垂直的,水平的类似)

    	//处理垂直滚动条消息
    	case WM_VSCROLL:
    		switch(LOWORD(wParam))
    		{
              case SB_LINEUP:
                   iVscrollPos -= 10 ;//每一行滚动10个像素
                   break ;
         
              case SB_LINEDOWN:
                   iVscrollPos += 10 ;
                   break ;
         
              case SB_PAGEUP://翻页就是一个客户区大小
                   iVscrollPos -= cyClient ;
                   break ;
         
              case SB_PAGEDOWN:
                   iVscrollPos += cyClient ;
                   break ;
         
              case SB_THUMBTRACK:
                   iVscrollPos = HIWORD (wParam) ;
                   break ;
         
              default :
                   break ;
    		}
    
    		//保证滚动条的位置在规定的范围内
    		iVscrollPos = max(0, min(iVscrollPos, cyBitmap - cyClient));
    
    		//iVscrollPos与当前滚动条位置不同时 , 才更新滚动条的位置
    		if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
    		{
    		   SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
    		   InvalidateRect (hwnd, NULL, FALSE) ;
    		   //由于这里是图片, 就不要刷背景了(也就是最后一个参数设为FALSE), 免得闪屏很厉害
    		}
    		return 0;

    (2)可变长的滚动条

    Win32的标准风格
    它要稍微复杂点儿,它同样有一套API来维护,以下是相关的API函数:

    设定滚动条信息

    int SetScrollInfo(
      HWND hwnd,           // 窗口句柄
      int fnBar,           // 滚动条类型
      LPCSCROLLINFO lpsi,  // 滚动条信息结构体(稍后详解)
      BOOL fRedraw         // 是否重绘滚动条方块
    );

    获取滚动条信息

    BOOL GetScrollInfo(
      HWND hwnd,         // handle to window
      int fnBar,         // scroll bar type
      LPSCROLLINFO lpsi  // scroll bar parameters
    );

    滚动窗口(这里是指窗口的客户区)

    BOOL ScrollWindow(
      HWND hWnd,              // handle to window
      int XAmount,            // 水平滚动距离
      int YAmount,            // 垂直滚动距离
      CONST RECT *lpRect,     // 滚动区域范围(一般就是设为NULL,指客户区)
      CONST RECT*lpClipRect  // 剪裁区域(今天用不到,设定为NULL)
    );

    SCROLLINFO结构体

    typedef struct tagSCROLLINFO { 
        UINT cbSize;//SCROLLINFO类型大小(主要是windows为了以后能兼容),也就是sizeof(SCROLLINFO)
        UINT fMask;	//设定滚动条需要设置的参数
        int  nMin;	//滚动条上边界
        int  nMax; 	//滚动条下边界
        UINT nPage; //每一页的大小(主要用于计算滚动条块的大小)
        int  nPos; 	//滚动条的位置
        int  nTrackPos; //滚动条滚动的位置
    }   SCROLLINFO, *LPSCROLLINFO; 
    typedef SCROLLINFO CONST *LPCSCROLLINFO;

    同样,我们还是先来看看它的效果:

    你会发现随着窗口大小的变化,滚动条方块也随着变化了,它变化的依据如下:

    有一点需要注意的是:windows帮你做了以下一个设计 —— 滚动条实际滚动的范围是:nMax - nPage + 1,这主要是避免过多的滚动,当显示内容在后一行在客户区最后一行就行了,所以我们只需要从我们的视角来设定nMin和nMax,不要自己去考虑滚动条滚动的实际范围,windows都为我们做好了。

    主要代码详解:

    初始化滚动条参数

    	case WM_SIZE:
    		cxClient = LOWORD(lParam);
    		cyClient = HIWORD(lParam);
    
    		//设定垂直滚动条范围和页面大小
    		si.cbSize	= sizeof(si);
    		si.fMask	= SIF_RANGE | SIF_PAGE;
    		si.nMin		= 0;
    		si.nMax		= cyBitmap;
    		si.nPage	= cyClient;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    
    		//设定水平滚动条范围和页面大小
    		si.cbSize	= sizeof(si);
    		si.fMask	= SIF_RANGE | SIF_PAGE;
    		si.nMin		= 0;
    		si.nMax		= cxBitmap;
    		si.nPage	= cxClient;
    		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_TOP:	//置顶(先按下Shift键不放,然后点击滚动条方块上侧区域就能置顶)
                   si.nPos = si.nMin ;
                   break ;
                   
              case SB_BOTTOM://置底(同置顶)
                   si.nPos = si.nMax ;
                   break ;
    
              case SB_LINEUP:
                   si.nPos -= 10 ;//每一行滚动10个像素
                   break ;
         
              case SB_LINEDOWN:
                   si.nPos += 10 ;
                   break ;
         
              case SB_PAGEUP://翻页就是一个客户区大小
                   si.nPos -= cyClient ;
                   break ;
         
              case SB_PAGEDOWN:
                   si.nPos += cyClient ;
                   break ;
         
              case SB_THUMBTRACK:
                   si.nPos = HIWORD (wParam) ;
                   break ;
         
              default :
                   break ;
    		}
    
    		si.fMask	= SIF_POS;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		if(iVertPos != si.nPos)
    		{
    			//这里InvalidateRect、ScrollWindow,效果相同
    			InvalidateRect(hwnd, NULL, FALSE);
    			//ScrollWindow(hwnd, 0, iVertPos - si.nPos, NULL, NULL);
    		}
    		return 0;

    有过Java开发的朋友可能知道,在Java中添加一个滚动条是多么容易啊,几行代码就OK了,但我想说的是:真正的程序员是了解自己编写的程序整个的来龙去脉,那么,试问Java的滚动条,有谁知道它是怎么来的吗?呵呵,当然,我说这些不具有任何感情色彩,只是我觉得真刀真枪的做才会有意义,各位觉得呢^_^

    源代码下载

  • 相关阅读:
    王钿《淡逻辑设计的学习》
    格雷码
    perl中 数组 和 列表的区别!
    FPGA建立时间(setup time)&保持时间(hold time)&竞争和冒险&毛刺
    Verilog三段式状态机描述
    [转]Unit Testing 101: Are You Testing Your JavaScript?
    [JavaScript]Jasmine中如何选择只运行部分Cases
    Java编程中推荐的Singleton模式
    [转]如何在Intellij中使用JsTestDriver做Javascript的Unit Testing
    [Groovy]Test with Spock
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3226173.html
Copyright © 2020-2023  润新知