zoukankan      html  css  js  c++  java
  • 纯c++实现之滚动窗口

    别在MFC了,先分析下,上图


    我们以左上角为坐标原点,用position_width和position_height来保存当前显示坐标。

    根据msdn说明,滚动条默认情况下的值在0~100之间。

    根据图可以知道positon_width的活动范围是0到canvas_width-screen-width,另一边类似。

    所以有恒等式1:position_width/(canvas_width-screen_width) = hb_pos/100,其中hb_pos是水平方向滚动条当前值。

    滚动块长度公式2:screen_width/canvas_width = 滚动块长度/滚动条可滚动区域长度,滚动条可滚动区域长度大概是screen_width-40差不多,可以设置大写留余地。


    下面直接上完整代码,可以运行的,只实现了拖动滚动块事件,其他事件自己补充吧

    #include <windows.h>
    
    #define  IDC_CANVAS                  200
    
    HWND hwnd_screen = NULL;//屏幕句柄,这里的屏幕既是我们创建的顶级窗口
    HWND hwnd_canvas = NULL;//画布句柄
    HINSTANCE	Ghinstance = NULL;//程序实例
    
    //注意:以下提到的“屏幕”指的都是我们创建的模拟屏幕,也就是顶级窗口,而不是我们计算机的屏幕
    
    int			canvas_width 		= 2000;//画布长度
    int 		canvas_height 		= 1500;//画布宽度
    
    int			screen_width 		= 0;//屏幕长度
    int			screen_height 		= 0;//屏幕宽度
    
    int			position_width 		= 0;//当前位置的横坐标
    int			position_height 	= 0;//当前位置的纵坐标
    
    int			hb_pos 				= 0;//竖直方向滚动条当前位置
    int			vb_pos 				= 0;//水平方向滚动条当前位置
    
    
    LRESULT CALLBACK ScreenProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);//屏幕事件处理函数
    LRESULT CALLBACK CanvasProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);//画布事件处理函数
    
    
    //入口函数
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    
    //========================================创建屏幕begin==================================================	
    	WNDCLASSEX wc;
    	MSG Msg;
    
    	memset(&wc,0,sizeof(wc));
    	wc.cbSize		 = sizeof(WNDCLASSEX);
    	wc.lpfnWndProc	 = ScreenProc;
    	wc.hInstance	 = hInstance;
    	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
    	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    	wc.lpszClassName = "WindowClass";
    	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
    
    	if(!RegisterClassEx(&wc)) {
    		MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
    		return 0;
    	}
    	
    	//程序实例和屏幕句柄放到全局变量里
    	Ghinstance = hInstance;
    	hwnd_screen = CreateWindowEx(WS_EX_CLIENTEDGE,"WindowClass","Caption",WS_VISIBLE|WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		800,
    		600,
    		NULL,NULL,hInstance,NULL);
    
    	if(hwnd_screen == NULL) {
    		MessageBox(NULL, "Screen Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
    		return 0;
    	}
    	
    //========================================创建屏幕end==================================================	 
    	
    	
    //你也可以把创建画布的过程放到屏幕的WM_CREATE事件中,放这里是使读者思路能清晰些	
    //========================================创建画布begin==================================================
    	wc;
    
    	memset(&wc,0,sizeof(wc));
    	wc.cbSize		 = sizeof(WNDCLASSEX);
    	wc.lpszClassName = "Canvas";
    	wc.lpfnWndProc	 = CanvasProc;
    	wc.hInstance	 = Ghinstance;//这里可以直接使用全局变量了
    	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
    	wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
    	
    	if(!RegisterClassEx(&wc)) {
    		MessageBox(NULL, "Canvas Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
    		return 0;
    	}				
    	
    	hwnd_canvas= CreateWindow(
        		"Canvas",
        		"",
        		WS_CHILD | WS_VISIBLE | WS_BORDER,
        		0, 0, canvas_width, canvas_height,
        		hwnd_screen,//这里可以直接使用全局变量了,注意,如果是放屏幕的WM_CREATE里面,这时候是还不能使用这个全局变量的,WM_CREATE事件结束后CreateWindow方法才会返回创建窗口的句柄
        		(HMENU)IDC_CANVAS,
        		Ghinstance,//这里可以直接使用全局变量了
        		0
        		);
    	
    	if(hwnd_canvas == NULL) {
    		MessageBox(NULL, "Canvas Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
    		return 0;
    	}	
    
    //========================================创建画布end==================================================
    	
    	//该显示的显示
        ShowWindow(hwnd_screen, nCmdShow);
        UpdateWindow(hwnd_screen);
        
    	//该显示的显示    
    	ShowWindow(hwnd_canvas, SW_SHOW);
    	UpdateWindow(hwnd_canvas);    
        
         
    	//消息循环
    	while(GetMessage(&Msg, NULL, 0, 0) > 0) {
    		TranslateMessage(&Msg);
    		DispatchMessage(&Msg);
    	}
    	return Msg.wParam;
    }
    
    //屏幕的事件
    LRESULT CALLBACK ScreenProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    	
    	//只需要处理WM_SIZE、WM_HSCROLL、WM_VSCROLL三个消息
    	switch(Message) {
    	
    		//窗口大小改变时,更新全局变量中的屏幕大小,更新滚动条上滚动块的位置
    		case WM_SIZE: {
    			
    			//更新屏幕大小begin-----------------------------
    			screen_width = LOWORD (lParam);
    			screen_height = HIWORD (lParam);
    			//更新屏幕大小end-----------------------------
    			
    			//更新滚动条上滚动块的位置begin----------------------
    			hb_pos = position_width * 100 / (canvas_width - screen_width);//根据恒等式1
    			vb_pos = position_height * 100 / (canvas_height - screen_height);
    			
    			SCROLLINFO  si;
    			si.cbSize=sizeof(SCROLLINFO);			
    			si.fMask=SIF_POS;			
    						
    			si.nPos = vb_pos;
    			SetScrollInfo(hwnd_screen,SB_VERT,&si,true);
    						
    			si.nPos = hb_pos;
    			SetScrollInfo(hwnd_screen,SB_HORZ,&si,true);			
    			//更新滚动条上滚动块的位置end----------------------
    			
    			
    			//其实还应该更新滚动条上滚动块的长度,这里先忽略吧
    			//int hb_length = GValue::s_width * (GValue::s_width - 40) / GValue::c_width;//根据恒等式2
    			//int vb_length = GValue::s_height * (GValue::s_height - 40) / GValue::c_height;
    			
    			break;
    		}
    		
    		
    		//水平方向滚动条事件
    		case WM_HSCROLL : {		
    			
    			SCROLLINFO  si;
    			si.cbSize=sizeof(SCROLLINFO);
    			si.fMask=SIF_ALL;
    			GetScrollInfo(hwnd_screen,SB_HORZ,&si);//先拿滚动条信息
    			
    			switch(LOWORD(wParam)){//这里只处理按下滚动条拖动的事件,其他滚动条事件自己实现吧
    			
    				//分析可知按住滚动条拖动过程中,需要修改当前位置、然后基于当前位置移动画布,最后修改滚动条位置(你不修改的话视觉效果上会弹回去的)、
    				case SB_THUMBTRACK: {										
    					position_width = si.nTrackPos * (canvas_width - screen_width) / 100;//更改当前位置		
    					MoveWindow(hwnd_canvas, 0 - position_width, 0 - position_height, canvas_width, canvas_height, true);//移动画布
    					
    					si.nPos=si.nTrackPos;
    					break;
    				}
    				
    				//TODO 滚动条的其他事件				
    				
    				default: {
    					break;
    				}
    			}
    			
    			//回写滚动条滚动块的位置,时视觉上正常
    			si.fMask=SIF_POS;
    			SetScrollInfo(hwnd_screen, SB_HORZ, &si, true);			
    			
    			break;
    		}
    		
    		//竖直方向滚动条事件,与上面相似不解释了
    		case WM_VSCROLL : {
    			
    			SCROLLINFO  si;
    			si.cbSize=sizeof(SCROLLINFO);
    			si.fMask=SIF_ALL;
    			GetScrollInfo(hwnd_screen, SB_VERT, &si);
    			
    			switch(LOWORD(wParam)){
    				case SB_THUMBTRACK: {
    					
    					position_height = si.nTrackPos * (canvas_height - screen_height) / 100;
    					MoveWindow(hwnd_canvas, 0 - position_width, 0 - position_height, canvas_width, canvas_height, true);
    					
    					si.nPos=si.nTrackPos;
    					break;
    				}
    				
    				default: {
    					break;
    				}
    			}
    			
    			si.fMask=SIF_POS;
    			SetScrollInfo(hwnd_screen, SB_VERT, &si, true);			
    			
    			break;
    		}
    		
    		//鼠标滚轮
    /*		case WM_MOUSEWHEEL : {
    			
    			//向下
    			if(HIWORD(wParam) < 0) {
    				vb_pos = vb_pos + 10;
    				if(vb_pos > 100) {
    					vb_pos = 0;
    				}				
    			} else {
    				vb_pos = vb_pos - 10;
    				if(vb_pos < 0) {
    					vb_pos = 0;
    				}
    			}
    			
    			break;
    		}
    */		
    			
    		case WM_CLOSE: {
    			DestroyWindow(hwnd);
    			break;
    		}
    		
    		case WM_DESTROY: {
    			PostQuitMessage(0);
    			break;
    		}
    		
    		default:
    			return DefWindowProc(hwnd, Message, wParam, lParam);
    	}
    	return 0;
    }
    
    //窗口的事件
    LRESULT CALLBACK CanvasProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    	
    	switch(Message) {
    		
    		//从画布左上角到右下角画一条线,以便观察滚动效果
    		case WM_PAINT: {
    			PAINTSTRUCT ps;
    			HDC hdc;	
    			
    			RECT rc;
    			GetClientRect(hwnd_canvas, &rc);
    
    			hdc = BeginPaint(hwnd_canvas, &ps);
    			
    			MoveToEx(hdc, 0 , 0 , NULL);
    			LineTo(hdc, rc.right, rc.bottom);
    			
    			EndPaint(hwnd_canvas, &ps);
    			
    			break;
    		}	
    	
    		case WM_CLOSE: {
    			DestroyWindow(hwnd);
    			break;
    		}
    		
    		case WM_DESTROY: {
    			PostQuitMessage(0);
    			break;
    		}
    		
    		default:
    			return DefWindowProc(hwnd, Message, wParam, lParam);
    	}
    }		
    


  • 相关阅读:
    解决docx4j 变量替换 由于变量存在样式式或空白字符 导致替换失败问题
    redis批量删除key 远程批量删除key
    idea 集成sonarLint检查代码bugs
    mac jmeter 的使用
    tomcat配置管理员-走后门
    终端mysql Operation not permitted错误解决方案
    update使用inner join
    hibernate 三种状态的转换
    数据库中间表插入乱序
    解决https证书验证不通过的问题
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3275686.html
Copyright © 2011-2022 走看看