• 用customdraw滚动条控件替换窗口的内部滚动条


    Sample Image - skinscrollbar_demo.gif 介绍 这是我的第一篇文章。首先,我要感谢CodeProject和所有无私的人们。 我试图寻找一个示例来演示如何皮肤窗口的内部滚动条,但不幸的是,我失败了。几天前,我得到了灵感:为了给窗口的内部滚动条皮肤,可以将窗口的滚动条隐藏在一个框架窗口下面,该框架窗口的大小比窗口小,但却是窗口的父窗口。 我试了一下,成功了! 两个主要的组件 在我的代码,你会发现两个主要组件: CSkinScrollBar(派生自CScrollBar) CSkinScrollBar提供了一个所有者绘制滚动条。我所做的只是简单地处理鼠标输入和绘制消息,我不打算详细描述它。如果你感兴趣,你可以看看我的代码。 CSkinScrollWnd是代码的核心 隐藏,收缩,复制Code

    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 its 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: use 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 information 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); 在我的代码的帮助下,您只需要在您的代码中添加一行代码。例如,假设您在窗口中有一个treectrl,并且您想替换它的滚动条。首先,你给它一个名字m_ctrlTree。下一步是当它被初始化时,添加如下一行: 隐藏,复制Code

    SkinWndScroll(&m_ctrlTree,hBmpScroll)

    如何测试我的项目? 界面中有4种控件,分别是listbox、treectrl、editctrl、richeditctrl。单击list_addstring按钮将填充listctrl,您将看到一个左边的滚动条。点击tree_addnode按钮将填充treectrl,您可能会看到两个ownerdraw滚动条替换了它的内部滚动条。在两个编辑框中输入文本,以查看它是否工作。 如何准备你的滚动条位图? 垂直和水平滚动条都需要4个图像片段。它们是向上/向左,滑动,拇指和向下/向右。每一个都包括3种状态:正常状态,悬停状态,按下状态。(很容易扩展对国家的支持。因为我不擅长图像处理,所以这个位图样本来自于一个软件的资源。)除了这些片段,位图还包括位于位图右侧的两个角度片段。 Sample image 现在我想向你们展示我遇到的问题 当我开始这段代码时,我尝试使用scrollbarctrl来覆盖窗口的内部滚动条。在我看来,只有滚动条窗口的Z阶较高,它才能正常工作。但事实上,这是行不通的。虽然我的滚动条窗口的z顺序较高,但当鼠标移动到滚动条区域时,内部的滚动条将立即呈现。我必须添加一个新窗口作为一个框架到目标窗口。一开始,我并没有打算支持leftscrollbar样式,所以我的代码运行得很好。最后,我决定支持它。但让我沮丧的是,它不再工作。在花了很多时间调试后,我发现它是错误的移动窗口的子类回调函数。因此,我定义了一个用户消息,并将该消息发布到消息队列。 如何应用它到一个列表ctrl ? 感谢康康发现了这个问题。 当将其应用到ListCtrl时,拖动拇指框将不起作用。我尽了我最大的努力去处理它。现在我向你们展示另一种方法。 我从CListCtrl派生了一个新类,并使用sbcode = SB_THUMBTRACK来处理WM_VSCROLL/WM_HSCROLL。 例如: 隐藏,收缩,复制Code

    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);
    }

    好的,就这样。希望对你有帮助。欢迎提出任何建议。 历史 2007-03-07 修正了skinscrollwnd.cpp中的错误比较。谢谢巫师们向我报告这件事。 2007.1.23 仔细测试了项目并进行了一些优化 2007.1.22 修正了一个滚动条的零div错误,修改了滚动条的自动滚动代码 2006.12.22 修改代码以将其应用于组合ctrl 2006.7.26 展示了一个方法,应用它到ListCtrl等。 2006.7.12 修正了一个滚动条的错误 2006.7.9 完成一个主框架 本文转载于:http://www.diyabc.com/frontweb/news6970.html

  • 相关阅读:
    PHP——语句和时间函数
    数据库——环境初建改端口和密码(转)
    PHP——初学,基础
    数据库——常用函数
    数据库——触发器(转)
    数据库——浅谈数据库中的存储过程(转)
    JavaScrip——简单练习(抓错误信息,for循环,日期)
    数据库——练习题答案
    django 创建APP 后找不到 templates文件夹下的Html文件。
    POSTGRESQL 9.1 FATAL: password authentication failed for user "postgres"
  • 原文地址:https://www.cnblogs.com/Dincat/p/13467462.html
Copyright © 2020-2023  润新知