纯c++实现之滚动窗口

来源:互联网 发布:茱莉亚音乐学院 知乎 编辑:程序博客网 时间:2024/06/04 18:54

别在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                  200HWND hwnd_screen = NULL;//屏幕句柄,这里的屏幕既是我们创建的顶级窗口HWND hwnd_canvas = NULL;//画布句柄HINSTANCEGhinstance = NULL;//程序实例//注意:以下提到的“屏幕”指的都是我们创建的模拟屏幕,也就是顶级窗口,而不是我们计算机的屏幕intcanvas_width = 2000;//画布长度int canvas_height = 1500;//画布宽度intscreen_width = 0;//屏幕长度intscreen_height = 0;//屏幕宽度intposition_width = 0;//当前位置的横坐标intposition_height = 0;//当前位置的纵坐标inthb_pos = 0;//竖直方向滚动条当前位置intvb_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);//根据恒等式1vb_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);}}


原创粉丝点击