用sdk建立完善的滚动

// 要把滚动功能做得尽善尽美真不是容易
// 一个完善的滚动功能,应该可以让用户用键盘和鼠标调整滚动条的位置
// 而且滑块的大小不应该是固定的
// 这需要调用SetScrollInfo和GetScrollInfo来处理
// WM_VSCROLL和WM_HSCROLL消息
// 同时还要处理WM_KEYDOWN消息和WM_MOUSEWHEEL消息
// 还要在WM_SIZE消息中更新滚动条的信息
// 总结:
// 1.在窗口大小改变时,即处理WM_SIZE消息时更新滚动条的信息以改变滚动条滑块的大小
// 而这会同时改变位置的范围.这须要调用SetScrollInfo
// 2.处理WM_VSCROLL和WM_HSCROLL,根据消息参数改变滚动条的位置
// 这其中要多次调用SetScrollInfo和GetScrollInfo
// 3.处理WM_KEYDOWN和WM_MOUSEWHEEL完善功能

// 以下的程序在窗口中显示了显示器的内容
// 由于窗口没有显示器大,所以需要滚动条


#define  _WIN32_WINNT 0x0500         // 为了使用WM_MOUSEWHEEL
#include 
< windows.h >

// 过程函数的返回值都是LRESULT,类型都是CALLBACK
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
int  WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpszCmdLine, int  nCmdShow)
{
    HWND hwnd;        
// 主窗口句柄,在CreateWindow中赋值
    MSG msg;         // 消息变量,在GetMessage中使用
    WNDCLASS wndclass;         // 窗口类
    TCHAR *  szAppName  =  TEXT( " CBViewer " );     // 类名和窗口名
    wndclass.hbrBackground  =  (HBRUSH)GetStockObject(WHITE_BRUSH);     // 窗口背景
    wndclass.hCursor  =  LoadCursor(NULL,IDC_ARROW);         // 鼠标
    wndclass.hIcon  =  LoadIcon(NULL,IDI_APPLICATION);     // 图标
    wndclass.lpszClassName  =  szAppName;         // 类名
    wndclass.cbClsExtra  =   0 ;         // 类的额外参数
    wndclass.cbWndExtra  =   0 ;         // 窗口的额外参数.用于基于同一窗口类的窗口各自区分.
                                    
// 在自定义对话框类时必须指定为DLGWINDOWEXTRA的大小
    wndclass.lpszMenuName  =  NULL;     // 菜单名.可以用作子窗口的id
    wndclass.style  =  CS_HREDRAW  |  CS_VREDRAW;     // 窗口风格
    wndclass.lpfnWndProc  =  WndProc;         // 窗口过程
    wndclass.hInstance  =  hInstance;         // 包含窗口过程的实例句柄

    
if ! RegisterClass( & wndclass) )         // 注册窗口类
         return   0 ;
    hwnd 
=  CreateWindow(                 // 创建窗口
                szAppName,                 // 窗口类名
                szAppName,                 // 窗口标题
                WS_OVERLAPPEDWINDOW,     // 窗口风格
                CW_USEDEFAULT,             // 初始的x坐标
                CW_USEDEFAULT,             // 初始的y坐标
                CW_USEDEFAULT,             // 初始的宽度
                CW_USEDEFAULT,             // 初始的高度
                NULL,                     // 父窗口
                NULL,                     // 菜单
                hInstance,                 // 和窗口相关的实例句柄
                NULL                     // 额外参数
                );
    ShowWindow( hwnd,nCmdShow );        
// 显示窗口
    UpdateWindow( hwnd );                 // 更新窗口
     while ( GetMessage( & msg,NULL, 0 , 0 ) )     // 消息循环
    {
        TranslateMessage(
& msg);             // 将WM_XXXKEYXXX消息翻译为WM_CHAR消息
        DispatchMessage( & msg);             // 传递消息到窗口过程
    }    
    
return  msg.wParam;                     // 返回
}
LRESULT WINAPI WndProc( HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam )
{
    
static   int   nDeltaPerLine, nAccumDelta ;  // 在WM_MOUSEWHEEL中使用
     static  HDC hdcMem;                          // 用作BitBlt的源DC
     static  HBITMAP  hbmp;                      // 桌面位图
     static   int  cxScreen, cyScreen;              // 屏幕大小
     switch ( message )
    {
    
case  WM_CREATE:    
        {        
            HWND hwndDesktop 
=  GetDesktopWindow();  // 桌面句柄
            HDC hdcDesktop  =  GetWindowDC(hwndDesktop);  // 桌面DC
            cxScreen   =  GetSystemMetrics(SM_CXSCREEN);  // 桌面大小
            cyScreen  =  GetSystemMetrics(SM_CYSCREEN);
            hbmp 
=  CreateCompatibleBitmap(                // 与桌面DC兼容的位图
                hdcDesktop,
                cxScreen,
                cyScreen);
            hdcMem 
=  CreateCompatibleDC(hdcDesktop);   // 与桌面DC兼容的内存DC
            SelectObject(hdcMem, hbmp);
            
// 将桌面内容保存到内存位图中, 以在WM_PAINT中画到客户窗口
            BitBlt(hdcMem,  0 0 , cxScreen, cyScreen, hdcDesktop,  0 0 , SRCCOPY);
            ReleaseDC(hwndDesktop, hdcDesktop);
        }
    
case  WM_SETTINGCHANGE:
        {
            ULONG  ulScrollLines ;
            SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 
0 & ulScrollLines,  0 ) ;
            
            
//  ulScrollLines usually equals 3 or 0 (for no scrolling)
            
//  WHEEL_DELTA equals 120, so nDeltaPerLine will be 40
            
            
if  (ulScrollLines)
            {
                nDeltaPerLine 
=  WHEEL_DELTA  /  ulScrollLines ;
            }
            
else
            {
                nDeltaPerLine 
=   0  ;
            }
            
return   0  ;
        }
    
case  WM_SIZE:
        {
            SCROLLINFO sif;
            
// 设置垂直滚动条的信息
            sif.cbSize  =   sizeof (sif);
            sif.fMask 
=  SIF_RANGE  |  SIF_PAGE;
            sif.nMax 
=  GetSystemMetrics(SM_CYSCREEN);
            sif.nMin 
=   0 ;
            sif.nPage 
=  HIWORD(lParam);
            SetScrollInfo(hwnd, SB_VERT, 
& sif, TRUE);
            
// 设置水平滚动条的信息
            sif.cbSize  =   sizeof (sif);
            sif.fMask 
=  SIF_RANGE  |  SIF_PAGE;
            sif.nMax 
=  GetSystemMetrics(SM_CXSCREEN);
            sif.nMin 
=   0 ;
            sif.nPage 
=  LOWORD(lParam);
            SetScrollInfo(hwnd, SB_HORZ, 
& sif, TRUE);

            
return   0 ;
        }
    
case  WM_VSCROLL:
        {
            
int  nVertPos;
            SCROLLINFO sif;

            sif.cbSize 
=   sizeof (sif);
            sif.fMask 
=  SIF_ALL;
            GetScrollInfo(hwnd, SB_VERT, 
& sif);
            
// 保存滚动条的当前位置.如果处理完消息后,滚动条的位置没变,我们就不用更新窗口了
            nVertPos  =  sif.nPos;

            
switch ( LOWORD(wParam) )
            {
            
case  SB_LINEUP:
                sif.nPos 
-=  sif.nPage / 10 ;
                
break ;
            
case  SB_LINEDOWN:
                sif.nPos 
+=  sif.nPage / 10 ;
                
break ;
            
case  SB_PAGEUP:
                sif.nPos 
-=  sif.nPage;
                
break ;
            
case  SB_PAGEDOWN:
                sif.nPos 
+=  sif.nPage;
                
break ;
            
case  SB_THUMBTRACK:
                sif.nPos 
=  HIWORD(wParam);
                
break ;
            }
            sif.cbSize 
=   sizeof (sif);
            sif.fMask 
=  SIF_POS;
            
// 设置滚动条的信息,然后再重新取得它的信息.因为windows会另外自动调整
            
// 滚动条的信息,而不是直接使用你设置的信息
            
// 例如对于sif.nMax,windows把它设置为sif.nMax = sif.nMax-sif.nPage+1
            SetScrollInfo(hwnd, SB_VERT,  & sif, TRUE);
            GetScrollInfo(hwnd, SB_VERT, 
& sif);

            
if ( nVertPos  !=  sif.nPos )
            {
                ScrollWindow(hwnd, 
0 , nVertPos  -  sif.nPos, NULL, NULL);
                UpdateWindow(hwnd);
            }
            
return   0 ;
        }
    
case  WM_HSCROLL:     // 水平滚动条几乎和垂直的一样处理,只是消息名称变了一点
        {
            
int  nHorzPos;
            SCROLLINFO sif;

            sif.cbSize 
=   sizeof (sif);
            sif.fMask 
=  SIF_ALL;
            GetScrollInfo(hwnd, SB_HORZ, 
& sif);
            
// 保存滚动条的当前位置.如果处理完消息后,滚动条的位置没变,我们就不用更新窗口了
            nHorzPos  =  sif.nPos;

            
switch ( LOWORD(wParam) )
            {
            
case  SB_LINELEFT:
                sif.nPos 
-=  sif.nPage / 10 ;
                
break ;
            
case  SB_LINERIGHT:
                sif.nPos 
+=  sif.nPage / 10 ;
                
break ;
            
case  SB_PAGELEFT:
                sif.nPos 
-=  sif.nPage;
                
break ;
            
case  SB_PAGERIGHT:
                sif.nPos 
+=  sif.nPage;
                
break ;
            
case  SB_THUMBTRACK:
                sif.nPos 
=  HIWORD(wParam);
                
break ;
            }
            sif.cbSize 
=   sizeof (sif);
            
// 设置滚动条的信息,然后再重新取得它的信息.因为windows会另外自动调整
            
// 滚动条的信息,而不是直接使用你设置的信息
            
// 例如对于sif.nMax,windows把它设置为sif.nMax = sif.nMax-sif.nPage+1
            SetScrollInfo(hwnd, SB_HORZ,  & sif, TRUE);
            GetScrollInfo(hwnd, SB_HORZ, 
& sif);

            
if ( nHorzPos  !=  sif.nPos )
            {
                ScrollWindow(hwnd, nHorzPos 
-  sif.nPos,  0 , NULL, NULL);
                UpdateWindow(hwnd);
            }
            
return   0 ;
        }
    
case  WM_KEYDOWN:
        {
            
switch (wParam)
            {
        
// 模拟滚动信息.这样处理的话,当我们的程序逻辑改变的时候,只要修改一个地方的代码即可
             case  VK_LEFT: 
                SendMessage(hwnd, WM_HSCROLL, SB_LINELEFT, (LPARAM)NULL);
                
break ;
            
case  VK_RIGHT:
                SendMessage(hwnd, WM_HSCROLL, SB_LINERIGHT, (LPARAM)NULL);
                
break ;
            
case  VK_UP:
                SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, (LPARAM)NULL);
                
break ;
            
case  VK_DOWN:
                SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)NULL);
                
break ;
            
case  VK_PRIOR:
                SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)NULL);
                
break ;
            
case  VK_NEXT:
                SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)NULL);
                
break ;
            }
            
return   0 ;
        }
    
case  WM_MOUSEWHEEL:  // 用鼠标滚轮也可以调整滚动条的位置
        {
            
if  (nDeltaPerLine  ==   0 )
            {
                
break  ;
            }
            nAccumDelta 
+=  ( short ) HIWORD (wParam) ;      //  120 or -120
            
            
while  (nAccumDelta  >=  nDeltaPerLine)
            { 
                
// 当滚动滑轮同时按下CTRL, 我们调整水平滚动条的位置
                
// 否则调整垂直滚动条的位置
                
// 在这里,和处理键盘消息一样,还是用模拟滚动消息的方法
                SendMessage (hwnd, 
                    LOWORD(wParam) 
&  MK_CONTROL  ?  WM_HSCROLL : WM_VSCROLL,
                    LOWORD(wParam) 
&  MK_CONTROL  ?  SB_LINELEFT : SB_LINEUP,
                    
0 );
                nAccumDelta 
-=  nDeltaPerLine ;
            }
            
            
while  (nAccumDelta  <=   - nDeltaPerLine)
            {
                SendMessage (hwnd,
                    LOWORD(wParam) 
&  MK_CONTROL  ?  WM_HSCROLL : WM_VSCROLL,
                    LOWORD(wParam) 
&  MK_CONTROL  ?  SB_LINEDOWN : SB_LINEDOWN,
                    
0 ) ;
                nAccumDelta 
+=  nDeltaPerLine ;
            }
            
            
return   0  ;
        }
    
case  WM_PAINT:
        {
            HDC hdc;
            PAINTSTRUCT ps;
            SCROLLINFO sif;
            
int  nVertPos, nHorzPos;
            
// 取得滚动信息以决定我们的图画到哪到位置
            sif.cbSize  =   sizeof (sif);
            sif.fMask 
=  SIF_POS;
            GetScrollInfo(hwnd, SB_VERT, 
& sif);
            nVertPos 
=  sif.nPos;
            
// 取得滚动信息以决定我们的图画到哪到位置
            sif.cbSize  =   sizeof (sif);
            sif.fMask 
=  SIF_POS;
            GetScrollInfo(hwnd, SB_HORZ, 
& sif);
            nHorzPos 
=  sif.nPos;
            
// 画图
            hdc  =  BeginPaint(hwnd,  & ps);
            BitBlt(hdc, 
- nHorzPos,  - nVertPos, cxScreen, cyScreen, hdcMem,  0 0 , SRCCOPY);
            EndPaint(hwnd, 
& ps);
            
return   0 ;
        }
    
case  WM_DESTROY:
        DeleteDC(hdcMem);    
// 别忘了做清理工作!!!
        DeleteObject(hbmp);
        PostQuitMessage(
0 );
        
return   0 ;
    }
    
return  DefWindowProc( hwnd,message,wParam,lParam );
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值