当前位置: 首页 > news >正文

Win32_7由浅入深——滚动条

滚动条在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的滚动条,有谁知道它是怎么来的吗?呵呵,当然,我说这些不具有任何感情色彩,只是我觉得真刀真枪的做才会有意义,各位觉得呢^_^

源代码下载

相关文章:

  • 简单实现web服务器负载均衡
  • Android编程之ActivityManager: Segmentation fault
  • C# 网络编程之网页简单下载实现
  • mac 下对 iterm 终端 设置代理
  • 如何理解c和c++的复杂类型声明
  • silverlight中datagrid数据到处excel
  • Android编程之如何取得View的当前位置
  • Android4.0内存Dex数据动态加载技术
  • C# 网络编程之网页自动登录 (一).使用WebBrower控件模仿登录
  • 文章收录1
  • windows服务器应对高并发和DDOS攻击
  • 公开课可下载资源汇总
  • 将solr3.5整合到Tomcat6.x中
  • 自己写Lucene分词器原理篇——ChineseAnalyzer简单讲解
  • 一个Java程序员应该掌握的10项技能
  • 【技术性】Search知识
  • 345-反转字符串中的元音字母
  • ECMAScript入门(七)--Module语法
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • JS变量作用域
  • laravel 用artisan创建自己的模板
  • Lucene解析 - 基本概念
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • react-native 安卓真机环境搭建
  • Sass Day-01
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • Windows Containers 大冒险: 容器网络
  • 大主子表关联的性能优化方法
  • 高程读书笔记 第六章 面向对象程序设计
  • 官方解决所有 npm 全局安装权限问题
  • 蓝海存储开关机注意事项总结
  • 排序(1):冒泡排序
  • 实战|智能家居行业移动应用性能分析
  • 使用 @font-face
  • 小程序测试方案初探
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • #define 用法
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #微信小程序:微信小程序常见的配置传旨
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (0)Nginx 功能特性
  • (06)金属布线——为半导体注入生命的连接
  • (07)Hive——窗口函数详解
  • (2)MFC+openGL单文档框架glFrame
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (WSI分类)WSI分类文献小综述 2024
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法