1. 程式人生 > >VC/MFC中如何替換自帶滾動條控制元件的圖片

VC/MFC中如何替換自帶滾動條控制元件的圖片

Sample Image - skinscrollbar_demo.gif

Introduction

This is my first article. At first, I must express my thanks to CodeProject and all the selfless people.

I have tried to look for a sample to 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 gave it a try and I succeeded!

Two Main Components

In my code, you will find two main components:

  1. CSkinScrollBar (derived from CScrollBar)
  2. CSkinScrollWnd (derived from CWnd)

CSkinScrollBar offers an owner draw scrollbar. What I have done is handle mouse input and paint message simply, and I do not intend to describe it in detail. If you are interested in it, you can look into my code.

CSkinScrollWnd is the Code's Core

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

With the help of my code, you just need to add a line of code in your code. For example, assume you have atreectrl in a window and you want to replace it's scrollbar. At first, you give it a namem_ctrlTree. The next step is when it gets initialized, add a line like this:

SkinWndScroll(&m_ctrlTree,hBmpScroll)

How To Test My Project?

There are 4 types of controls in the interface, including listbox,treectrl, editctrl, richeditctrl respectively. Clickinglist_addstring button will fill listctrl and you will see a left scrollbar. Clickingtree_addnode button will fill treectrl and you may see two ownerdraw scrollbars replace its internal scrollbar. Input text in two editboxes to see whether it works.

How To Prepare Your Scrollbar Bitmap?

Both vertical and horizontal scrollbars require 4 image segments. They are arrow-up/arrow-left, slide, thumb and arrow-down/arrow-right. Each of them includes 3 states: normal, hover, press. (It is possible to extend support for state easily. Because I'm not good at image processing, the sample bitmap came from a software's resource.) Beside those segments, the bitmap includes two angle segments located at bitmap's right.

Sample image

Now I Want to Show You the Problems I Have Encountered

  1. When I began this code, I 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. Although my scrollbar window's z-order is higher, when mouse moves to scrollbar area, the internal scrollbar will render immediately. I have to add a new window as a frame to the target window.
  2. At first, I did not intend to support leftscrollbar style, and my code worked well. Finally, I decided to support it. But what makes me depressed is that it does not work any more. After spending a lot of time on debugging, I found it's wrong to move the window in the subclass callback function. So I defined a user message, and posted the message to the message queue.

How To Apply It to a ListCtrl?

Thanks to kangcorn for finding the problem.

When applying it to ListCtrl, dragging the thumb box will have no effect. I tried my best to deal with it. Now I show you an alternate method.

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

For example:

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 suggestions will be welcome.

History

  • 2007-03-07
    • Fixed a bug of skinscrollwnd.cpp in which a wrong compare was done. Thanks to tHeWiZaRdOfDoS for reporting it to me.
  • 2007.1.23
    • Tested the project carefully and made some optimizations
  • 2007.1.22
    • Fixed a scrollbar's zero div bug, modified scrollbar's auto scroll codes
  • 2006.12.22
    • Modified code to apply it to combo ctrl
  • 2006.7.26
    • Showed a method for applying it to ListCtrl, etc.
  • 2006.7.12
    • Fixed a scrollbar's bug
  • 2006.7.9
    • Finished a primary frame

相關推薦

VC/MFC如何替換滾動控制元件圖片

Introduction This is my first article. At first, I must express my thanks to CodeProject and all the selfless people. I have tried to look for a sample to

C#關於滾動控制元件滾動跳動問題

     今天偶爾發現那個panel的垂直滾動條如果不做任何處理,每次獲得焦點後位置老是變動。當拖動到某個位置,有另一個視窗擋住後,再顯示出來它的滾動條就不在原來的位置了?這個怎麼解決呢? 例如,視窗1的panel滾動條一開始拖動到最底端,然後開啟另一個程式視窗2擋住;然後

對話方塊滾動的使用

.h: CButton* m_pBtns; afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta

Easyui 去除jquery-easui tab頁div滾動

滾動 需要 bsp http alt right 需求 mage css 去除jquery-easui tab頁div自帶滾動條 by:授客 QQ:1033553122 測試環境 jquery-easyui-1.5.3 需求場景 打開tab頁面時,自動

【UE4】 第04講 隱藏新建工程的操作控制元件

  (版權宣告,禁止轉載)        UE4.14在新建移動平臺的工程之後,啟動會顯示預設的兩個白色圓圈操作控制元件,一個控制預設CameraActor的方向,一個控制移動。當你要自行實現功能的時候,就需要把它們隱藏掉了。 &nb

c#通過.net的chart控制元件繪製餅圖pie chart

原文地址:http://www.veryhuo.com/a/view/52954.html 需要實現的目標是:   1.將資料繫結到pie的後臺資料中,自動生成餅圖。   2.生成的餅圖有詳細文字的說明。   具體的實現步驟:   >>前臺介面的設

如何在fragment獲取定義view的控制元件id

今天在CSDN上下載了一個關於廣告輪播的demo,上面的控制元件是自定義view的控制元件。當我把它放到專案中犯了難,因為他直接在activity中定義, 而我需要將其放入fragment中,所以報了一堆錯誤還有空指標,主要的問題及解決可見程式碼,需要注意的問題是,不能直接

用微軟的FileUpload控制元件上傳檔案報錯

解決方法:在WebConfig 中 增加<system.web>     <httpRuntime useFullyQualifiedRedirectUrl="true" maxRequestLength="104857" executionTimeout=

C# WinForm開發系列之c# 通過.net的chart控制元件繪製餅圖,柱形圖和折線圖的基礎使用和擴充套件

一.需要實現的目標是: 1.將資料繫結到pie的後臺資料中,自動生成餅圖。 2.生成的餅圖有詳細文字的說明。 1.設定chart1的屬性Legends中預設的Legend1的Enable為false; 如圖1所示: 圖1 2.設定Series的ChartT

在VS2010/MFC如何對對話方塊新增控制元件

先說一下自己用的工具Visual Studio 2010 Ultimate 英文版(裝有Visual Assist X)。這裡簡單提一下Visual Assist X的安裝方法在選單欄選擇”Tools”,然後是Extension Manager,在Extension Manager左邊欄裡面選擇Onli

unityGUI之控制元件的焦點獲取

using UnityEngine; using System.Collections; public class SetConFocus : MonoBehaviour {     public s

C# 根據BackgroundWoker非同步模型和ProgressBar控制元件,定義進度控制元件

### 前言 > 程式開發過程中,難免會有的業務邏輯,或者演算法之類產生讓人能夠感知的耗時操作,例如迴圈中對複雜邏輯處理;獲取資料庫百萬乃至千萬級資料;http請求的時候等...... > 使用者在使用UI操作並不知道程式的內部處理,從而誤操作導致程式無響應,關閉程式等待影響體驗的情況,因此,在

在word2010新增滾動的文字框

由於檔案內容過長,為了加強文章的可讀性,可以新增一個帶滾動條的文字框,既能使文章看起來乾淨整潔,同時也極大的提高了文章的可讀性。  我這裡對在word2010中文字框帶滾動條作個介紹: 1. 開啟word後,點選“檔案”選單,在下列列表中找到“選項”一欄,在彈出的視窗中找到‘自定義

jsp實現滾動的table表格

<div style="width:700px; height:225px; overflow:auto;">  <table border="1" width="100%" cellpadding="0" cellspacing="0">   &l

[轉]關於VC++ MFC的空閑Idle處理機制!

normal 函數 系統 true check track cor idle 行處理 關鍵詞:   先根據空閑標誌以及消息隊列是否為空這兩個條件判斷當前線程是否處於空閑狀態(這個“空閑”的含義同操作系統的含義不同,是MFC自己所謂的“空閑”),如果是,就調用CW

CSS3定義滾動樣式 -webkit-scrollbar

play 偽類 thumb area 沒有 :link 現在 自定義滾動條 box ::-webkit-scrollbar {/*隱藏滾輪*/display: none;} 前言 webkit支持擁有overflow屬性的區域,列表框,下拉菜單,textarea的滾動條自定

每天一個JS 小demo之定義滾動。主要知識點:事件應用

prevent 數據 滾動 sca listener 視頻 希望 特效 poi <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>D

VC/MFC的CComboBox控件使用詳解

作用 沒有 opp com 開始 getc 其他 index ear CComboBox控件詳解 CComboBox控件又稱作組合框控件,其有三種形態可供選擇,1.簡單組合框(Simple)2.下拉組合框(Drop-down)3.下拉列表式組合框(Drop-down lis

移動端網頁定義滾動

max view andro move .html class height transform inner <!DOCTYPE html> <html> <head> <meta charset="utf-8" />

CSS3定義滾動樣式 之 -webkit-scrollbar

selection 單獨 窗口 請求 方塊 利用 源碼 bsp 進行 有沒有覺得瀏覽器自帶的原始滾動條很不美觀,同時也有看到很多網站的自定義滾動條顯得高端,就連chrome32.0開發板都拋棄了原始的滾動條,美觀多了。那webkit瀏覽器是如何自定義滾動條的呢? 前言