zoukankan      html  css  js  c++  java
  • replace a window's internal scrollbar with a customdraw scrollbar control

    Sample Image - skinscrollbar_demo.gif

    Introduction

    It's my first article. At first I must express my thank to codeproject and all of selflessness people.

    I have tried to look for a sample show me how to skin a window's internal scrollbar, but,unfortunately, I failed. Some days ago, I got inspiration: In order to skin a window's internal scrollbar, it may be possible to hide a window's scrollbar below a frame window whose size is smaller than the window but is the window's parent.

    I have a try and I success!

    In my code you will find two main components:

     CSkinScrollBar( drived from CScrollBar) and CSkinScrollWnd(drived from CWnd)

    CSkinScrollBar offers a owner draw scrollbar. What I have done is to handle mouse input and paint messge simply, and I'm not intent to descript it in detail. If you are interesting with it, you can look into my code.

    CSkinScrollWnd is the code's core.

    Collapse
    BOOL CSkinScrollWnd::SkinWindow(CWnd *pWnd,HBITMAP hBmpScroll)
    {//create a frame windows set
     ASSERT(m_hWnd==NULL);
     m_hBmpScroll=hBmpScroll;
    
    //calc scrollbar wid/hei according to the input bitmap handle
     BITMAP bm;
     GetObject(hBmpScroll,sizeof(bm),&bm);
     m_nScrollWid=bm.bmWidth/9;
    
     CWnd *pParent=pWnd->GetParent();
     ASSERT(pParent);
     RECT rcFrm,rcWnd;
     pWnd->GetWindowRect(&rcFrm);
     pParent->ScreenToClient(&rcFrm);
     rcWnd=rcFrm;
     OffsetRect(&rcWnd,-rcWnd.left,-rcWnd.top);
     UINT uID=pWnd->GetDlgCtrlID();
    
    //remove original window's border style and add it to frame window
     DWORD dwStyle=pWnd->GetStyle();
     DWORD dwFrmStyle=WS_CHILD|SS_NOTIFY;
     DWORD dwFrmStyleEx=0;
     if(dwStyle&WS_VISIBLE) dwFrmStyle|=WS_VISIBLE;
     if(dwStyle&WS_BORDER)
     {
      dwFrmStyle|=WS_BORDER;
      pWnd->ModifyStyle(WS_BORDER,0);
      int nBorder=::GetSystemMetrics(SM_CXBORDER);
      rcWnd.right-=nBorder*2;
      rcWnd.bottom-=nBorder*2;
     }
     DWORD dwExStyle=pWnd->GetExStyle();
     if(dwExStyle&WS_EX_CLIENTEDGE)
     {
      pWnd->ModifyStyleEx(WS_EX_CLIENTEDGE,0);
      int nBorder=::GetSystemMetrics(SM_CXEDGE);
      rcWnd.right-=nBorder*2;
      rcWnd.bottom-=nBorder*2;
      dwFrmStyleEx|=WS_EX_CLIENTEDGE;
     }
    
    //create frame window at original window's rectangle and set it's ID equal to original window's ID.
    
     this->CreateEx(dwFrmStyleEx,AfxRegisterWndClass(NULL),"SkinScrollBarFrame",dwFrmStyle,rcFrm,pParent,uID);
    
    //create a limit window. it will clip target window's scrollbar. 
    
    m_wndLimit.Create(NULL,"LIMIT",WS_CHILD|WS_VISIBLE,CRect(0,0,0,0),this,200);
    
    //create my scrollbar ctrl
     m_sbHorz.Create(WS_CHILD,CRect(0,0,0,0),this,100);
     m_sbVert.Create(WS_CHILD|SBS_VERT,CRect(0,0,0,0),this,101);
     m_sbHorz.SetBitmap(m_hBmpScroll);
     m_sbVert.SetBitmap(m_hBmpScroll);
    
    //change target's parent to limit window
    
     pWnd->SetParent(&m_wndLimit);
    
    //attach CSkinScrollWnd data to target window's userdata. 
    
    //remark: user this code ,obviously, you will never try to use userdata!!
     SetWindowLong(pWnd->m_hWnd,GWL_USERDATA,(LONG)this);
    
    //subclass target window's wndproc
     m_funOldProc=(WNDPROC)SetWindowLong(pWnd->m_hWnd,GWL_WNDPROC,(LONG)HookWndProc);
    
     pWnd->MoveWindow(&rcWnd);
    
    //set a timer. it will update scrollbar's infomation at times.
    
    //I have tried to hook some messages so as to update scrollinfo timely. For example, WM_ERESEBKGND and WM_PAINT. But with spy++'s aid, I found if the window's client area need not update, my hook proc would hook nothing except some control-depending interfaces. 
     SetTimer(TIMER_UPDATE,500,NULL);
     return TRUE;
    }
    static LRESULT CALLBACK
    HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
    {//my hook function
     CSkinScrollWnd *pSkin=(CSkinScrollWnd*)GetWindowLong(hwnd,GWL_USERDATA);
     LRESULT lr=::CallWindowProc(pSkin->m_funOldProc,hwnd,msg,wp,lp);
     if(pSkin->m_bOp) return lr;
     if(msg==WM_ERASEBKGND)
     {//update scroll info
       SCROLLINFO si;
       DWORD dwStyle=::GetWindowLong(hwnd,GWL_STYLE);
       if(dwStyle&WS_VSCROLL)
       {
        memset(&si,0,sizeof(si));
        si.cbSize=sizeof(si);
        si.fMask=SIF_ALL;
        ::GetScrollInfo(hwnd,SB_VERT,&si);
        pSkin->m_sbVert.SetScrollInfo(&si);
        pSkin->m_sbVert.EnableWindow(si.nMax>=si.nPage);
       }
       if(dwStyle&WS_HSCROLL)
       {
        memset(&si,0,sizeof(si));
        si.cbSize=sizeof(si);
        si.fMask=SIF_ALL;
        ::GetScrollInfo(hwnd,SB_HORZ,&si);
        pSkin->m_sbHorz.SetScrollInfo(&si);
        pSkin->m_sbHorz.EnableWindow(si.nMax>=si.nPage);
       }
     }else if(msg==WM_NCCALCSIZE && wp)
     {//recalculate scroll bar display area.
       LPNCCALCSIZE_PARAMS pNcCalcSizeParam=(LPNCCALCSIZE_PARAMS)lp;
       DWORD dwStyle=::GetWindowLong(hwnd,GWL_STYLE);
       DWORD dwExStyle=::GetWindowLong(hwnd,GWL_EXSTYLE);
       BOOL  bLeftScroll=dwExStyle&WS_EX_LEFTSCROLLBAR;
       int nWid=::GetSystemMetrics(SM_CXVSCROLL);
       if(dwStyle&WS_VSCROLL) 
       {
        if(bLeftScroll)
         pNcCalcSizeParam->rgrc[0].left-=nWid-pSkin->m_nScrollWid;
        else
         pNcCalcSizeParam->rgrc[0].right+=nWid-pSkin->m_nScrollWid;
       }
       if(dwStyle&WS_HSCROLL) pNcCalcSizeParam->rgrc[0].bottom+=nWid-pSkin->m_nScrollWid;
       
       RECT rc,rcVert,rcHorz;
       ::GetWindowRect(hwnd,&rc);
       ::OffsetRect(&rc,-rc.left,-rc.top);
       
       nWid=pSkin->m_nScrollWid;
       if(bLeftScroll)
       {
        int nLeft=pNcCalcSizeParam->rgrc[0].left;
        int nBottom=pNcCalcSizeParam->rgrc[0].bottom;
        rcVert.right=nLeft;
        rcVert.left=nLeft-nWid;
        rcVert.top=0;
        rcVert.bottom=nBottom;
        rcHorz.left=nLeft;
        rcHorz.right=pNcCalcSizeParam->rgrc[0].right;
        rcHorz.top=nBottom;
        rcHorz.bottom=nBottom+nWid;
       }else
       {
        int nRight=pNcCalcSizeParam->rgrc[0].right;
        int nBottom=pNcCalcSizeParam->rgrc[0].bottom;
        rcVert.left=nRight;
        rcVert.right=nRight+nWid;
        rcVert.top=0;
        rcVert.bottom=nBottom;
        rcHorz.left=0;
        rcHorz.right=nRight;
        rcHorz.top=nBottom;
        rcHorz.bottom=nBottom+nWid;
       }
       if(dwStyle&WS_VSCROLL && dwStyle&WS_HSCROLL)
       {
        pSkin->m_nAngleType=bLeftScroll?1:2;
       }else
       {
        pSkin->m_nAngleType=0;
       }
       if(dwStyle&WS_VSCROLL)
       {
        pSkin->m_sbVert.MoveWindow(&rcVert);
        pSkin->m_sbVert.ShowWindow(SW_SHOW);
       }else
       {
        pSkin->m_sbVert.ShowWindow(SW_HIDE);
       }
       if(dwStyle&WS_HSCROLL)
       {
        pSkin->m_sbHorz.MoveWindow(&rcHorz);
        pSkin->m_sbHorz.ShowWindow(SW_SHOW);
       }else
       {
        pSkin->m_sbHorz.ShowWindow(SW_HIDE);
       }
       pSkin->PostMessage(UM_DESTMOVE,dwStyle&WS_VSCROLL,bLeftScroll);
     }
     return lr;
    }

    //the only global function

    //param[in] CWnd *pWnd: target window

    //param[in] HBITMAP hBmpScroll: bitmap handle used by scrollbar control.

    //return CSkinScrollWnd*:the frame pointer

    CSkinScrollWnd* SkinWndScroll(CWnd *pWnd,HBITMAP hBmpScroll);

    With my code's help, you just need add a line of code in your code. For example, assume you have a treectrl in a window and you want to replace it's scrollbar, at first ,you give it a name m_ctrlTree,the next step is when it got initialized ,add a line like this "SkinWndScroll(&m_ctrlTree,hBmpScroll)".

    How to test my project?

    there are 4 types of control in the interface, including listbox,treectrl,editctrl,richeditctrl respectively. Clicking list_addstring button will fill listctrl and you will see a left scrollbar. Clicking tree_addnode button will fill treectrl and you may see two ownerdraw scrollbar replace it's internal scrollbar. input text in two of editboxes to see whether it works.

    How to prepare your scrollbar bitmap?

    both vertical and horizontal scrollbar require 4 image segments. they are arrow-up/arrow-left,slide,thumb and arrow-down/arrow-right.each of them include 3 state:normal,hover,press(It is possible to extent to surport 4 state easily.because I'm not good at image process,the sample bitmap if came from a software's resource. ) Beside those segments,the bitmap include two angle segments locating at bitmap's right.

    Sample image

    Now I want to show you the problems I have encounted.

    1. when I begin this code, I have tried to use a scrollbarctrl to cover the window's internal scrollbar. In my mind, only if my scrollbar window's Z order is higher it will work well. But in fact, It does not work. Althrough my scrollbar window's z-order is higher, when mouse moves to scrollbar area, the internal scrollbar will rander immediately. I have to add a new window as a frame to the target window.

    2. At first , I does not intent to surport leftscrollbar style, and my code work well. Finally, I deside to surport it. But what makes me depression is it don't work any more. For a long time of debugging, I found it's wrong to move window in subclass callback function. So I define a user message,and post the message to message queue.

    How to apply it to a ListCtrl?

    thank kangcorn for finding the problem.

    when apply it to ListCtrl,traging thumb box will have none effect. I try all my best to deal with it. now I show a alt method.

    I derive a new class from CListCtrl and handle WM_VSCROLL /WM_HSCROLL with a sbcode equal to SB_THUMBTRACK.

    for example:

    Collapse
    LRESULT CListCtrlEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
    {
     if(message==WM_VSCROLL||message==WM_HSCROLL)
     {
      WORD sbCode=LOWORD(wParam);
      if(sbCode==SB_THUMBTRACK
       ||sbCode==SB_THUMBPOSITION)
      {
       SCROLLINFO siv={0};
       siv.cbSize=sizeof(SCROLLINFO);
       siv.fMask=SIF_ALL;
       SCROLLINFO sih=siv;
       int nPos=HIWORD(wParam);
       CRect rcClient;
       GetClientRect(&rcClient);
       GetScrollInfo(SB_VERT,&siv);
       GetScrollInfo(SB_HORZ,&sih);
       SIZE sizeAll;
       if(sih.nPage==0) 
        sizeAll.cx=rcClient.right;
       else
        sizeAll.cx=rcClient.right*(sih.nMax+1)/sih.nPage ;
       if(siv.nPage==0)
        sizeAll.cy=rcClient.bottom;
       else
        sizeAll.cy=rcClient.bottom*(siv.nMax+1)/siv.nPage ;
       
       SIZE size={0,0};
       if(WM_VSCROLL==message)
       {
        size.cx=sizeAll.cx*sih.nPos/(sih.nMax+1);
        size.cy=sizeAll.cy*(nPos-siv.nPos)/(siv.nMax+1);
       }else
       {
        size.cx=sizeAll.cx*(nPos-sih.nPos)/(sih.nMax+1);
        size.cy=sizeAll.cy*siv.nPos/(siv.nMax+1);
       }
       Scroll(size);
       return 1;
      }
     }
     return CListCtrl::WindowProc(message, wParam, lParam);
    }
    

    Ok , that's all. Hope it will be helpful to you. Any suggestion will be wellcome.
    //website:http://www.codeproject.com/useritems/skinscrollbar.asp

     
  • 相关阅读:
    msf提权命令/meterpreter下的几个命令
    ms17010漏洞复现-2003
    复现IIS6.0远程命令执行漏洞
    代码审计之Finecms任意文件下载漏洞
    逻辑运算符
    RIP动态路由的配置
    跟着百度学习php之ThinkPHP的运行流程-2
    静态路由配置
    跟着百度学习php之ThinkPHP的运行流程-1
    外挂是怎么写的?
  • 原文地址:https://www.cnblogs.com/rainbowzc/p/2422292.html
Copyright © 2011-2022 走看看