1. 程式人生 > >Winform:關於滾動條美化

Winform:關於滾動條美化

先來點直觀的效果,這是偶新做的一個UI中的一條滾動條,這條滾動條可獨立應用於各種有下拉框的控制元件,不簡單吧。。。。嘿嘿。

換了一家新公司馬上就接到兩個UI的製作,好久沒做這種UI美化了,嘿嘿!很有感覺哦。。。。

裡面有這麼一條滾動條花了比較多時間,不過最後做出來了,真好。

但這個UI在偶的直接上司來了之後被否定掉了,記錄一下吧。嘿嘿

滾動條美化有兩種方案

1、截獲Windows訊息,然後進行重繪滾動條等操作。。

CODEPROJECT在有一些C++的程式碼記錄著。

而某牛人在http://www.tctl.com.cn/accp/1521/1522/242362.html上也記錄著這麼一段話:” 我提供基本的思路:子類換(SubClass)是必須的(截獲訊息並處理),HookAPI也是必須的。由於ListView中的滾動條是內建的,而不是 單獨的ScrollBar,所以處理比較麻煩。首先是處理訊息,其次是APIHook。首先說訊息處理。我們要處理的訊息又 WM_PAINT,WM_NCPAINT,WM_HSCROLL,WM_VSCROLL,WM_HITTEST,WM_NCMOUSEMOVE等。在 WM_NCPAINT的時候,系統需要繪製滾動條,這時候,如果訊息的引數wParam是1是,表示一個需要繪製整個區域,那麼,我們必須從中Clip掉 滾動條的區域,然後傳給原來的訊息處理函式。WM_HSCROLL,WM_VSCROLL中,我們需要改變其樣式,將滾動條樣式去掉,欺騙 Windows,然後呼叫原來的視窗處理過程。WM_HITTEST中,需要處理的是滑鼠的點選位置,這樣的自己判斷。WM_NCMOUSEMOVE中需 要處理的是滾動條的拖動,需要自己計算滾動條的位置。然後傳送訊息 SendMessage(AHandle,MW_HSCROLL,MAKEWPARAM(SB_THUMBTRACK,m_iPos),0);這些訊息中 都需要自己繪製滾動條。”(看得懂他的意思,卻比較難實現,這種熟知API才行)

http://blog.csdn.net/superarhow/archive/2006/07/27/984338.aspx這裡有對上面那個說法的驗證,感動啊。。。。。。。。不過是Delphi程式碼。

2、新增自己的滾動條,覆蓋ListView本身產生的那個滾動條,然後把相應的事件聯絡起來。即自已做一條滾動條,再把控制元件的滾動條隱藏掉,通過這條滾動條再去控制元件控制元件的滾動條,KUGOO的滾動條即是採取這種方案,聽說QQ也是,用SPA++看一下就知道了,是兩個不同的視窗控制代碼。(其實這種好理解,但也是得熟知API。。。。)

介紹第二種方法

但他比較細緻,比較麻煩,需要畫好多個地方,而本人就在其也改改,只需三張圖片,向上按鈕,向下按鈕,還有中間的拇指按鈕。中間的通道自定一種色彩就可以了。

修改之後的ONPAINT函式

e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

畫上面的按鈕

if (UpArrowImage != null)

{

e.Graphics.DrawImage(UpArrowImage, new Rectangle(new Point(0, 0), new Size(this.Width, UpArrowImage.Height)));

}

Brush oBrush = new SolidBrush(moChannelColor);

Brush oWhiteBrush = new SolidBrush(Color.FromA#ffffff);

畫兩條邊框

//draw channel left and right border colors

e.Graphics.FillRectangle(oWhiteBrush, new Rectangle(0, UpArrowImage.Height, 1, (this.Height - DownArrowImage.Height)));

e.Graphics.FillRectangle(oWhiteBrush, new Rectangle(this.Width - 1, UpArrowImage.Height, 1, (this.Height - DownArrowImage.Height-UpArrowImage.Height)));

畫通道

//draw channel

e.Graphics.FillRectangle(oBrush, new Rectangle(0, UpArrowImage.Height, this.Width, (this.Height - DownArrowImage.Height - UpArrowImage.Height)));

//draw thumb

int nTrackHeight = (this.Height - (UpArrowImage.Height + DownArrowImage.Height));

float fThumbHeight = ((float)LargeChange / (float)Maximum) * nTrackHeight;

int nThumbHeight = (int)fThumbHeight;

if (nThumbHeight > nTrackHeight)

{

nThumbHeight = nTrackHeight;

fThumbHeight = nTrackHeight;

}

float fSpanHeight = (fThumbHeight - (ThumbMiddleImage.Height + ThumbTopImage.Height + ThumbBottomImage.Height)) / 2.0f;

int nSpanHeight = (int)fSpanHeight;

int nTop = moThumbTop;

nTop += UpArrowImage.Height;

e.Graphics.DrawImage(ThumbMiddleImage, new Rectangle(0, nTop, this.Width , nThumbHeight));

nTop += ThumbMiddleImage.Height;

if (DownArrowImage != null)

{

e.Graphics.DrawImage(DownArrowImage, new Rectangle(new Point(0, (this.Height - DownArrowImage.Height)), new Size(this.Width, DownArrowImage.Height)));

}

其他的參考作者的,就畫出了一條滾動條了。。。。。

接著,怎麼去控制樹型控制元件的SCROLLBAR呢。。。。

這裡需要用到幾個API

[DllImport("user32.dll")]

[returnMarshalAs(UnmanagedType.Bool)]

public static extern bool GetScrollInfo(IntPtr hwndint fnBarref SCROLLINFO lpsi);

[DllImport("user32.dll")]

public static extern int SetScrollInfo(IntPtr hwndint fnBar, [Inref SCROLLINFO

lpsibool fRedraw);

直接給程式碼算了

獲取其資訊的

public SCROLLINFO tvImageListScrollInfo

{

get

{

SCROLLINFO si = new SCROLLINFO();

si.cbSize = (uint)Marshal.SizeOf(si);

si.fMask = (int)(ScrollInfoMask.SIF_DISABLENOSCROLL | ScrollInfoMask.SIF_ALL);

Win32API.GetScrollInfo(tvImageList.Handle, (int)ScrollBarDirection.SB_VERTref si);

return si;

}

}

設定可見與否

private void SetImageListScrollVisible()

{

SCROLLINFO info = tvImageListScrollInfo;

tvscroll.Minimum = info.nMin;

tvscroll.Maximum = Convert.ToInt32(info.nMax + 1);

tvscroll.LargeChange = (int)info.nPage;

tvscroll.SmallChange = 7; //這裡是根據每一項的大小而定的

if (tvImageListScrollInfo.nMax > 0)

{

tvscroll.Visible = true;

}

else

{

tvscroll.Visible = false;

}

}

當滑鼠滾動時,設定該滾動條

private void SetImageListScroll()

{

SCROLLINFO info = tvImageListScrollInfo;

if (info.nMax > 0)

{

int pos = info.nPos - 1;

if (pos >= 0)

{

tvscroll.Value = pos;

}

}

}

當滾動條滾動時,通知控制元件也跟著滾動吧。。。

SCROLLINFO info = tvImageListScrollInfo;

info.nPos = curValue;

Win32API.SetScrollInfo(tvImageList.Handle, (int)ScrollBarDirection.SB_VERTref infotrue);

Win32API.PostMessage(tvImageList.HandleWin32API.WM_VSCROLLWin32API.MakeLong((short)Win32API.SB_THUMBTRACK, (short)(info.nPos)), 0);

等等,也就完成了一個自定義的樹型控制元件了咧。。。。。。。。

寫得好沒激情呀,可能太久沒寫的關係吧。。。。

介結幾個API吧

GetScrollInfo-函式功能

該函式找到滾動條的引數,包括滾動條位置的最小值、最大值,頁面大小,滾動按鈕的 位置,

函式原型:BOOL GetScrolllnfo(HWND hWnd,int fnBar,LPSCROLLINFO lpsi);

引數:

hWnd:滾動條控制或有標準滾動條的窗體控制代碼,由fnBar引數確定。

fnBar:指定待找回滾動條引數的型別,此引數可以為如下值,其值含義:

SB_CTL:找回滾動條控制引數。其中引數hwnd一定是處理滾動條控制的控制代碼。

SB_HORZ:找回所指定窗體的標準水平滾動條引數。

SB_VERT:找回所指定窗體的標準垂直滾動條引數。

lpsi:指向SCROLLINFO結構。在呼叫Getscrolllofo函式之前,設定SCROLLINFO結構中cbSize成員以標識結構 大小,設定成員fMask以說明待找回的滾動條引數。在執行之前,函式複製結構中適當的成員所指定的引數。

成員fMask可以是如下值:

SIF_PAGE:複製滾動頁碼到由lpsi指向的SCROLLINFO結構的nPage成員中。

SIF_POS:複製滾動位置到由lpsi指向的SCROLLINFO結構的nPos成員中。

GetScrollInfo-相關資料

SIF_RANGE:複製滾動範圍到由lpsi指向的SCROLLINFO結構的nMin和nMax成員中。

SIF_TRACKPOS:複製當前滾動盒跟蹤位置到由nTrackPos指向的SCROLLINFO結構的 nPage成員中。

返回值:如果函式找到任何一個值,那麼返回值為非零;如果函式沒有找到任何值,那麼返回值為零;

注意:Getscrolllnfo函式儘管WM_HSCROLL和WM_VSCROLL指出了滾動條位置訊息,卻僅提供了16位資料,而函式 SetScrollnfo和GetScrollnfo則提供了32位的滾動條資料。因而,當應用程式在處理WM_HSCROLL或 WM_VSCROLL時,要獲得32位滾動條位置的資料時, 則要呼叫Getscrolllnfo函式。     在WM_HSCROLL或WM_VSCROLL訊息中SB_THUMBTRACK通告過程中,為了獲得32位的滾動盒位置,需要呼叫 GetScrolllnfo函式以得到結構SCROLLINFO成員fMask中的SCROLLINFO值。函式返回在結構SCROLLINFO成員nTrackPos中指出的滾動盒跟蹤位置的值。這將允許當用戶移動滾動盒時能得到其位置。

SetScrollInfo

函式功能:該函式設定滾動條引數,包括滾動位置的最大值和最小值,頁面大小,滾動按鈕的位置。如被請求,函式也可以重畫滾動條。

函式原型:int SetScrollInfo(HWND hWnd;int fnBar,LPSCROLLINFO lpsi,BOOL fRedraw);

引數:

hWnd:滾動條控制或帶標準滾動條的窗體控制代碼,由fnBar引數決定。

fnBar:指定被設定引數的滾動條的型別。這個引數可以是下面值,含義如下:

SB_CTL:設定滾動條控制。而引數hwnd必須是滾動條控制的控制代碼。

SB_HORZ:設定所給定的窗體上標準水平滾動條引數。

SB_VERT:設定所給定的窗體上標準垂直滾動條引數。

IPBI:指向SCROLLINFO結構。在呼叫SetScrognfo之前,設定 SCROLLINFO結構中cbSize成員以標識結構大小,設定成員fMask以說明待設定的滾動條引數,並且在適當的成員中制定新的引數值。成員 fMask可以為下面所列複合值,含義如下:

SIF_DfSABLENOSCROLL:如果滾動條的新引數使其為沒必要,則使滾動條無效而不再移動它。

SIF_PAGE:設定滾動頁碼值到由Ipsi指向的SCROLLINFO結構的nPage成員中。

SIF_POS:設定滾動位置值到由lpsi指向的SCROLLINFO結構的nPos成員中。

SIF_RANGE:設定滾動範圍值到由lpsl指向的SCROLLINFO結構的nMin和nMax成員中。

fRedraw:指定滾動條是否重畫以反映滾動條的變化。如果這個引數為TRUE,滾動條將被重畫,否則不被重畫。

返回值:返回值是滾動盒的當前位置。

注意:SetScrolllnfo函式執行任務是檢查SCROLLINFO結構中由成員 nPage和nPos值的範圍。成員uPage值必須從0到nMax- nMin+1,成員nPos必須是在nMin和nMax-nMax-max(nPage C1,0)之間的指定值。如果任何一個值超過了這個範圍,函式將在指定範圍內為它設定一個值。

在Windows CE 2.0中,如果在引數lpsi中指定一個NULL指標,Setscrohnfo則返回0,而不返回滾動盒的當前位置。

控制控制元件上移下移還可以這樣:

PostMessageA(treeView1.Handle, WM_VSCROLL,1, 0);

PostMessageA(treeView1.Handle, WM_VSCROLL,0, 0);

傳送一條移一次。。。。。。。。這是比較標準的WINDOWS方式,但有時老控制不準。。。主要是移得太快的話有時有些訊息老丟了就不準確了。

如果對這個控制滾動條還不是很理解,還可以考慮這裡,但是是VB。NET的

這裡還有一篇介結滾動條的幾個比較主要的屬性的,像那些MAXCHANGE

Setting these two properties adjusts the scrollbar so that the thumb correctly reflects the viewable "page" proportional to the entire document breadth or width. The following equation illustrates the relationship between LargeChange and Maximum to the thumb length and scrollbar length (I'm using length here to reflect one dimension, width or height):

Collapse

LargeChange     Thumb Length

----------- = ------------

Maximum       Track Length

以上等等,就是一條自定義滾動條的相關知識了,哦,還差隱藏控制元件的滾動條。。。這個暫時實現不了,不過直接把你的覆蓋下去就可以了哦。