别在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,
800,
600,
NULL,NULL,hInstance,NULL);
if(hwnd_screen == NULL) {
MessageBox(NULL, "Screen Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
//========================================建立螢幕end==================================================
//你也可以把建立畫布的過程放到螢幕的WM_CREATE事件中,放這裡是使讀者思路能清晰些
//========================================建立畫布begin==================================================
wc;
wc.lpszClassName = "Canvas";
wc.lpfnWndProc = CanvasProc;
wc.hInstance = Ghinstance;//這裡可以直接使用全局變量了
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
MessageBox(NULL, "Canvas Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
}
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,//這裡可以直接使用全局變量了
);
if(hwnd_canvas == NULL) {
MessageBox(NULL, "Canvas Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
}
//========================================建立畫布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 : {
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: {
}
//回寫滾動條滾動塊的位置,時視覺上正常
si.fMask=SIF_POS;
SetScrollInfo(hwnd_screen, SB_HORZ, &si, true);
//豎直方向滾動條事件,與上面相似不解釋了
case WM_VSCROLL : {
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);
SetScrollInfo(hwnd_screen, SB_VERT, &si, true);
//滑鼠滾輪
/* 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) {
}
}
break;
}
*/
case WM_CLOSE: {
DestroyWindow(hwnd);
case WM_DESTROY: {
PostQuitMessage(0);
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
return 0;
//視窗的事件
LRESULT CALLBACK CanvasProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
//從畫布左上角到右下角畫一條線,以便觀察滾動效果
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);
}
}
本文轉自莫水千流部落格園部落格,原文連結:http://www.cnblogs.com/zhoug2020/p/6076185.html,如需轉載請自行聯系原作者