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。。。。)
介绍第二种方法
首先参考codeproject的一条滚动条,就是采取这个方案,使用USERCONTROL画了一条出来了http://www.codeproject.com/KB/miscctrl/customscrollbar.aspx
但他比较细致,比较麻烦,需要画好多个地方,而本人就在其也改改,只需三张图片,向上按钮,向下按钮,还有中间的拇指按钮。中间的通道自定一种色彩就可以了。
修改之后的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")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
[DllImport("user32.dll")]
public static extern int SetScrollInfo(IntPtr hwnd, int fnBar, [In] ref SCROLLINFO
lpsi, bool 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_VERT, ref 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_VERT, ref info, true);
Win32API.PostMessage(tvImageList.Handle, Win32API.WM_VSCROLL, Win32API.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的
http://www.codeproject.com/KB/vb/VbNetScrolling.aspx
http://www.codeproject.com/KB/vb/APIScroll.aspx
http://www.codesky.net/article/doc/200309/200309145113524.htm
这里还有一篇介结滚动条的几个比较主要的属性的,像那些MAXCHANGE
http://www.codeproject.com/KB/miscctrl/understandingScrollbars.aspx
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
以上等等,就是一条自定义滚动条的相关知识了,哦,还差隐藏控件的滚动条。。。这个暂时实现不了,不过直接把你的覆盖下去就可以了哦。