C#仿QQ皮肤-Label与ListBox 控件实现----寻求滚动条的解决方案
C#仿QQ皮肤-实现原理系列文章导航
http://www.cnblogs.com/sufei/archive/2010/03/10/1682847.html
大家还是先来看看效果吧
这次之所以一次写两个控件,其实主要是因为Label控件实在是太简单了没有必要放放一个文章里写,所以就一次性来了。
Label控件我就不再多说了,我直接把代码贴一下吧因为就几行代码,相信大家一眼就能看明白了。
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceCRD.WinUI.Misc
{
publicclassLabel:System.Windows.Forms.Label
{
publicLabel()
:base()
{
this.BackColor=System.Drawing.Color.Transparent;
}
}
}
ListBox实现
咱们从第一行代码就要吧看出来是继承自系统控件而来的。
所以本身就具备了系统的ListBox的一些特性。老方法我们先来看看WndProc方法的实现
{
IntPtrhDC=IntPtr.Zero;
Graphicsgdc=null;
switch(m.Msg)
{
case133:
hDC=Win32.GetWindowDC(m.HWnd);
gdc=Graphics.FromHdc(hDC);
Win32.SendMessage(this.Handle,WM_ERASEBKGND,hDC.ToInt32(),0);
SendPrintClientMsg();
Win32.SendMessage(this.Handle,WM_PAINT,0,0);
OverrideControlBorder(gdc);
m.Result=(IntPtr)1;
Win32.ReleaseDC(m.HWnd,hDC);
gdc.Dispose();
break;
caseWM_PAINT:
base.WndProc(refm);
hDC=Win32.GetWindowDC(m.HWnd);
gdc=Graphics.FromHdc(hDC);
OverrideControlBorder(gdc);
Win32.ReleaseDC(m.HWnd,hDC);
gdc.Dispose();
break;
default:
base.WndProc(refm);
break;
}
}
这边的实现方法基本上和之前的控件一个样,所以我就不再多说原理了,大家随便看一下前几次的文章就明白了。
下面我们来看一下怎么样换皮肤的事件
也就是说在换皮肤的时候我们应该做那些工作
{
Graphicsg=e.Graphics;
//绘制区域
Rectangler=e.Bounds;
Fontfn=null;
if(e.Index>=0)
{
if(e.State==DrawItemState.None)
{
//设置字体、字符串格式、对齐方式
fn=e.Font;
strings=(string)this.Items[e.Index];
StringFormatsf=newStringFormat();
sf.Alignment=StringAlignment.Near;
//根据不同的状态用不同的颜色表示
if(e.State==(DrawItemState.NoAccelerator|DrawItemState.NoFocusRect))
{
e.Graphics.FillRectangle(newSolidBrush(Color.Red),r);
e.Graphics.DrawString(s,fn,newSolidBrush(Color.Black),r,sf);
e.DrawFocusRectangle();
}
else
{
e.Graphics.FillRectangle(newSolidBrush(Color.White),r);
e.Graphics.DrawString(s,fn,newSolidBrush(Shared.FontColor),r,sf);
e.DrawFocusRectangle();
}
}
else
{
fn=e.Font;
StringFormatsf=newStringFormat();
sf.Alignment=StringAlignment.Near;
strings=(string)this.Items[e.Index];
e.Graphics.FillRectangle(newSolidBrush(Shared.ControlBackColor),r);
e.Graphics.DrawString(s,fn,newSolidBrush(Shared.FontColor),r,sf);
}
}
}
其实这些都不是今天要说的重点,这个控件的实现基础跟之前的一些控件基本上是一样的,像Textbox就和这个差不多,
唯一我想说的是滚动条的实现,不多说了下面开始吧
滚动条的实现
如上面的图片大家已经看到了,图片在我的源代码里都有,我在这里就不多说了,一起来看是怎么实现 的吧,先说说思路,
第一步,先制做一个自己的滚动条,随便做只要自己感觉漂亮就可以,第二步就是,利用Api把Listbox现有的滚动条用现在的滚动条代替,第三步,让现有的滚动条和系统的滚动条实现同步即可。
我实现滚动条的代码,大家也可以自己写这里只是一个参考吧
看一下效果
看一下代码吧,具体的素材大家到我源代码里面找吧,呵呵
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Drawing;
usingSystem.Data;
usingSystem.Text;
usingSystem.Windows.Forms;
usingSystem.Windows.Forms.Design;
usingSystem.Diagnostics;
namespaceCRD.WinUI.Misc
{
[Designer(typeof(ScrollbarControlDesigner))]
publicpartialclassCustomScrollbar:UserControl
{
protectedColormoChannelColor=Color.Empty;
protectedImagemoUpArrowImage=null;//上箭头
//protectedImagemoUpArrowImage_Over=null;
//protectedImagemoUpArrowImage_Down=null;
protectedImagemoDownArrowImage=null;//下箭头
//protectedImagemoDownArrowImage_Over=null;
//protectedImagemoDownArrowImage_Down=null;
protectedImagemoThumbArrowImage=null;
protectedImagemoThumbTopImage=null;
protectedImagemoThumbTopSpanImage=null;
protectedImagemoThumbBottomImage=null;
protectedImagemoThumbBottomSpanImage=null;
protectedImagemoThumbMiddleImage=null;
protectedintmoLargeChange=10;
protectedintmoSmallChange=1;
protectedintmoMinimum=0;
protectedintmoMaximum=100;
protectedintmoValue=0;
privateintnClickPoint;
protectedintmoThumbTop=0;
protectedboolmoAutoSize=false;
privateboolmoThumbDown=false;
privateboolmoThumbDragging=false;
publicneweventEventHandlerScroll=null;
publiceventEventHandlerValueChanged=null;
privateintGetThumbHeight()
{
intnTrackHeight=(this.Height-(UpArrowImage.Height+DownArrowImage.Height));
floatfThumbHeight=((float)LargeChange/(float)Maximum)*nTrackHeight;
intnThumbHeight=(int)fThumbHeight;
if(nThumbHeight>nTrackHeight)
{
nThumbHeight=nTrackHeight;
fThumbHeight=nTrackHeight;
}
if(nThumbHeight<56)
{
nThumbHeight=56;
fThumbHeight=56;
}
returnnThumbHeight;
}
publicCustomScrollbar()
{
InitializeComponent();
SetStyle(ControlStyles.ResizeRedraw,true);
SetStyle(ControlStyles.AllPaintingInWmPaint,true);
SetStyle(ControlStyles.DoubleBuffer,true);
moChannelColor=Color.FromArgb(51,166,3);
UpArrowImage=Bitmap.FromStream(Shared.AssemblyWinUI.GetManifestResourceStream("CRD.WinUI.Resources.Common.scroll.uparrow.png"),true,false);//BASSSkin.uparrow;//上箭头
DownArrowImage=Bitmap.FromStream(Shared.AssemblyWinUI.GetManifestResourceStream("CRD.WinUI.Resources.Common.scroll.downarrow.png"),true,false);//BASSSkin.downarrow;//下肩头
ThumbBottomImage=Bitmap.FromStream(Shared.AssemblyWinUI.GetManifestResourceStream("CRD.WinUI.Resources.Common.scroll.ThumbBottom.png"),true,false);//BASSSkin.ThumbBottom;
ThumbMiddleImage=Bitmap.FromStream(Shared.AssemblyWinUI.GetManifestResourceStream("CRD.WinUI.Resources.Common.scroll.ThumbMiddle.png"),true,false);//BASSSkin.ThumbMiddle;
this.Width=UpArrowImage.Width;//18px
base.MinimumSize=newSize(UpArrowImage.Width,UpArrowImage.Height+DownArrowImage.Height+GetThumbHeight());
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Behavior"),Description("LargeChange")]
publicintLargeChange
{
get{returnmoLargeChange;}
set
{
moLargeChange=value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Behavior"),Description("SmallChange")]
publicintSmallChange
{
get{returnmoSmallChange;}
set
{
moSmallChange=value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Behavior"),Description("Minimum")]
publicintMinimum
{
get{returnmoMinimum;}
set
{
moMinimum=value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Behavior"),Description("Maximum")]
publicintMaximum
{
get{returnmoMaximum;}
set
{
moMaximum=value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Behavior"),Description("Value")]
publicintValue
{
get{returnmoValue;}
set
{
moValue=value;
intnTrackHeight=(this.Height-(UpArrowImage.Height+DownArrowImage.Height));
floatfThumbHeight=((float)LargeChange/(float)Maximum)*nTrackHeight;
intnThumbHeight=(int)fThumbHeight;
if(nThumbHeight>nTrackHeight)
{
nThumbHeight=nTrackHeight;
fThumbHeight=nTrackHeight;
}
if(nThumbHeight<56)
{
nThumbHeight=56;
fThumbHeight=56;
}
//figureoutvalue
intnPixelRange=nTrackHeight-nThumbHeight;
intnRealRange=(Maximum-Minimum)-LargeChange;
floatfPerc=0.0f;
if(nRealRange!=0)
{
fPerc=(float)moValue/(float)nRealRange;
}
floatfTop=fPerc*nPixelRange;
moThumbTop=(int)fTop;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Skin"),Description("ChannelColor")]
publicColorChannelColor
{
get{returnmoChannelColor;}
set{moChannelColor=value;}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Skin"),Description("UpArrowGraphic")]
publicImageUpArrowImage
{
get{returnmoUpArrowImage;}
set{moUpArrowImage=value;}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Skin"),Description("UpArrowGraphic")]
publicImageDownArrowImage
{
get{returnmoDownArrowImage;}
set{moDownArrowImage=value;}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Skin"),Description("UpArrowGraphic")]
publicImageThumbBottomImage
{
get{returnmoThumbBottomImage;}
set{moThumbBottomImage=value;}
}
[EditorBrowsable(EditorBrowsableState.Always),Browsable(true),DefaultValue(false),Category("Skin"),Description("UpArrowGraphic")]
publicImageThumbMiddleImage
{
get{returnmoThumbMiddleImage;}
set{moThumbMiddleImage=value;}
}
protectedoverridevoidOnPaint(PaintEventArgse)
{
e.Graphics.InterpolationMode=System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
if(UpArrowImage!=null)
{
e.Graphics.DrawImage(UpArrowImage,newRectangle(newPoint(0,0),newSize(this.Width,UpArrowImage.Height)));
}
BrushoBrush=newSolidBrush(moChannelColor);
BrushoWhiteBrush=newSolidBrush(Color.FromArgb(255,255,255));
//函数名:rectangle
//功能:画一个矩形
//用法:voidfarrectangle(intleft,inttop,intright,intbottom);
//drawchannelleftandrightbordercolors
e.Graphics.FillRectangle(oWhiteBrush,newRectangle(0,UpArrowImage.Height,1,(this.Height-DownArrowImage.Height)));
e.Graphics.FillRectangle(oWhiteBrush,newRectangle(this.Width-1,UpArrowImage.Height,1,(this.Height-DownArrowImage.Height)));
//drawchannel
//e.Graphics.FillRectangle(oBrush,newRectangle(1,UpArrowImage.Height,this.Width-2,(this.Height-DownArrowImage.Height)));
e.Graphics.DrawImage(ThumbBottomImage,newRectangle(0,UpArrowImage.Height,this.Width,(this.Height-DownArrowImage.Height)));
//drawthumb
intnTrackHeight=(this.Height-(UpArrowImage.Height+DownArrowImage.Height));
floatfThumbHeight=((float)LargeChange/(float)Maximum)*nTrackHeight;
intnThumbHeight=(int)fThumbHeight;
if(nThumbHeight>nTrackHeight)
{
nThumbHeight=nTrackHeight;
fThumbHeight=nTrackHeight;
}
//MessageBox.Show(nThumbHeight.ToString());
if(nThumbHeight<56)
{
nThumbHeight=56;
fThumbHeight=56;
}
//Debug.WriteLine(nThumbHeight.ToString());
//floatfSpanHeight=(fThumbHeight-(ThumbMiddleImage.Height+ThumbTopImage.Height+ThumbBottomImage.Height))/2.0f;
//intnSpanHeight=(int)fSpanHeight;
intnTop=moThumbTop;//0
nTop+=UpArrowImage.Height;//9px
//drawtop画上面的按钮
//e.Graphics.DrawImage(ThumbTopImage,newRectangle(0,nTop,this.Width,ThumbTopImage.Height));
//nTop+=ThumbTopImage.Height;//10px
//drawtopspan
//Rectanglerect=newRectangle(1,nTop,this.Width-2,nSpanHeight);
//e.Graphics.DrawImage(ThumbTopSpanImage,1.0f,(float)nTop,(float)this.Width-2.0f,(float)fSpanHeight*2);
//nTop+=nSpanHeight;//11px
//drawmiddle
e.Graphics.DrawImage(ThumbMiddleImage,newRectangle(0,nTop,this.Width,ThumbMiddleImage.Height));
//nTop+=ThumbMiddleImage.Height;
//drawtopspan
//rect=newRectangle(1,nTop,this.Width-2,nSpanHeight*2);
//e.Graphics.DrawImage(ThumbBottomSpanImage,rect);
//nTop+=nSpanHeight;
//drawbottom
//e.Graphics.DrawImage(ThumbBottomImage,newRectangle(1,nTop,this.Width-2,nSpanHeight));
if(DownArrowImage!=null)
{
e.Graphics.DrawImage(DownArrowImage,newRectangle(newPoint(0,(this.Height-DownArrowImage.Height)),newSize(this.Width,DownArrowImage.Height)));
}
}
publicoverrideboolAutoSize
{
get
{
returnbase.AutoSize;
}
set
{
base.AutoSize=value;
if(base.AutoSize)
{
this.Width=moUpArrowImage.Width;
}
}
}
privatevoidInitializeComponent()
{
this.SuspendLayout();
//
//CustomScrollbar
//
this.Name="CustomScrollbar";
this.MouseDown+=newSystem.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseDown);
this.MouseMove+=newSystem.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseMove);
this.MouseUp+=newSystem.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseUp);
this.ResumeLayout(false);
}
privatevoidCustomScrollbar_MouseDown(objectsender,MouseEventArgse)
{
PointptPoint=this.PointToClient(Cursor.Position);
intnTrackHeight=(this.Height-(UpArrowImage.Height+DownArrowImage.Height));
floatfThumbHeight=((float)LargeChange/(float)Maximum)*nTrackHeight;
intnThumbHeight=(int)fThumbHeight;
if(nThumbHeight>nTrackHeight)
{
nThumbHeight=nTrackHeight;
fThumbHeight=nTrackHeight;
}
if(nThumbHeight<56)
{
nThumbHeight=56;
fThumbHeight=56;
}
intnTop=moThumbTop;
nTop+=UpArrowImage.Height;
Rectanglethumbrect=newRectangle(newPoint(1,nTop),newSize(ThumbMiddleImage.Width,nThumbHeight));
if(thumbrect.Contains(ptPoint))
{
//hitthethumb
nClickPoint=(ptPoint.Y-nTop);
//MessageBox.Show(Convert.ToString((ptPoint.Y-nTop)));
this.moThumbDown=true;
}
Rectangleuparrowrect=newRectangle(newPoint(1,0),newSize(UpArrowImage.Width,UpArrowImage.Height));
if(uparrowrect.Contains(ptPoint))
{
intnRealRange=(Maximum-Minimum)-LargeChange;
intnPixelRange=(nTrackHeight-nThumbHeight);
if(nRealRange>0)
{
if(nPixelRange>0)
{
if((moThumbTop-SmallChange)<0)
moThumbTop=0;
else
moThumbTop-=SmallChange;
//figureoutvalue
floatfPerc=(float)moThumbTop/(float)nPixelRange;
floatfValue=fPerc*(Maximum-LargeChange);
moValue=(int)fValue;
Debug.WriteLine(moValue.ToString());
if(ValueChanged!=null)
ValueChanged(this,newEventArgs());
if(Scroll!=null)
Scroll(this,newEventArgs());
Invalidate();
}
}
}
Rectangledownarrowrect=newRectangle(newPoint(1,UpArrowImage.Height+nTrackHeight),newSize(UpArrowImage.Width,UpArrowImage.Height));
if(downarrowrect.Contains(ptPoint))
{
intnRealRange=(Maximum-Minimum)-LargeChange;
intnPixelRange=(nTrackHeight-nThumbHeight);
if(nRealRange>0)
{
if(nPixelRange>0)
{
if((moThumbTop+SmallChange)>nPixelRange)
moThumbTop=nPixelRange;
else
moThumbTop+=SmallChange;
//figureoutvalue
floatfPerc=(float)moThumbTop/(float)nPixelRange;
floatfValue=fPerc*(Maximum-LargeChange);
moValue=(int)fValue;
Debug.WriteLine(moValue.ToString());
if(ValueChanged!=null)
ValueChanged(this,newEventArgs());
if(Scroll!=null)
Scroll(this,newEventArgs());
Invalidate();
}
}
}
}
privatevoidCustomScrollbar_MouseUp(objectsender,MouseEventArgse)
{
this.moThumbDown=false;
this.moThumbDragging=false;
}
privatevoidMoveThumb(inty)
{
intnRealRange=Maximum-Minimum;
intnTrackHeight=(this.Height-(UpArrowImage.Height+DownArrowImage.Height));
floatfThumbHeight=((float)LargeChange/(float)Maximum)*nTrackHeight;
intnThumbHeight=(int)fThumbHeight;
if(nThumbHeight>nTrackHeight)
{
nThumbHeight=nTrackHeight;
fThumbHeight=nTrackHeight;
}
if(nThumbHeight<56)
{
nThumbHeight=56;
fThumbHeight=56;
}
intnSpot=nClickPoint;
intnPixelRange=(nTrackHeight-nThumbHeight);
if(moThumbDown&&nRealRange>0)
{
if(nPixelRange>0)
{
intnNewThumbTop=y-(UpArrowImage.Height+nSpot);
if(nNewThumbTop<0)
{
moThumbTop=nNewThumbTop=0;
}
elseif(nNewThumbTop>nPixelRange)
{
moThumbTop=nNewThumbTop=nPixelRange;
}
else
{
moThumbTop=y-(UpArrowImage.Height+nSpot);
}
//figureoutvalue
floatfPerc=(float)moThumbTop/(float)nPixelRange;
floatfValue=fPerc*(Maximum-LargeChange);
moValue=(int)fValue;
Debug.WriteLine(moValue.ToString());
Application.DoEvents();
Invalidate();
}
}
}
privatevoidCustomScrollbar_MouseMove(objectsender,MouseEventArgse)
{
if(moThumbDown==true)
{
this.moThumbDragging=true;
}
if(this.moThumbDragging)
{
MoveThumb(e.Y);
}
if(ValueChanged!=null)
ValueChanged(this,newEventArgs());
if(Scroll!=null)
Scroll(this,newEventArgs());
}
}
internalclassScrollbarControlDesigner:System.Windows.Forms.Design.ControlDesigner
{
publicoverrideSelectionRulesSelectionRules
{
get
{
SelectionRulesselectionRules=base.SelectionRules;
PropertyDescriptorpropDescriptor=TypeDescriptor.GetProperties(this.Component)["AutoSize"];
if(propDescriptor!=null)
{
boolautoSize=(bool)propDescriptor.GetValue(this.Component);
if(autoSize)
{
selectionRules=SelectionRules.Visible|SelectionRules.Moveable|SelectionRules.BottomSizeable|SelectionRules.TopSizeable;
}
else
{
selectionRules=SelectionRules.Visible|SelectionRules.AllSizeable|SelectionRules.Moveable;
}
}
returnselectionRules;
}
}
}
}
关于一些Api的方法吧,我都定义出来了直接用就行了
{
[StructLayout(LayoutKind.Sequential)]
publicstructtagSCROLLINFO
{
publicuintcbSize;
publicuintfMask;
publicintnMin;
publicintnMax;
publicuintnPage;
publicintnPos;
publicintnTrackPos;
}
publicenumfnBar
{
SB_HORZ=0,
SB_VERT=1,
SB_CTL=2
}
publicenumfMask
{
SIF_ALL,
SIF_DISABLENOSCROLL=0X0010,
SIF_PAGE=0X0002,
SIF_POS=0X0004,
SIF_RANGE=0X0001,
SIF_TRACKPOS=0X0008
}
publicstaticintMakeLong(shortlowPart,shorthighPart)
{
return(int)(((ushort)lowPart)|(uint)(highPart<<16));
}
publicconstintSB_THUMBTRACK=5;
publicconstintWM_HSCROLL=0x114;
publicconstintWM_VSCROLL=0x115;
[DllImport("user32.dll",EntryPoint="GetScrollInfo")]
publicstaticexternboolGetScrollInfo(IntPtrhwnd,intfnBar,refSCROLLINFOlpsi);
[DllImport("user32.dll",EntryPoint="SetScrollInfo")]
publicstaticexternintSetScrollInfo(IntPtrhwnd,intfnBar,[In]refSCROLLINFOlpsi,boolfRedraw);
[DllImport("User32.dll",CharSet=CharSet.Auto,EntryPoint="SendMessage")]
staticexternIntPtrSendMessage(IntPtrhWnd,uintMsg,IntPtrwParam,IntPtrlParam);
[DllImport("user32.dll",SetLastError=true)]
publicstaticexternboolPostMessage(IntPtrhWnd,uintMsg,longwParam,intlParam);
}
最后一步控件同步效果
info.nPos=customScrollbar1.Value;
Win32API.SetScrollInfo(listBox1.Handle,(int)ScrollBarDirection.SB_VERT,refinfo,true);
Win32API.PostMessage(listBox1.Handle,Win32API.WM_VSCROLL,Win32API.MakeLong((short)Win32API.SB_THUMBTRACK,(short)(info.nPos)),0);
好了,现在我们拉一个控件到窗体上,就是我图1中的效果了。中间还有一些不好的地方,我争取改正,
也希望大家多多提建议,
我个人感觉这种方法不是什么很好的方法,如果是用C++来写的话会方便很多,但不知道c#是怎么写的,小弟确实不知道,还希望能得大家的帮助,一起来解决这个问题
其实我在网上也找了不少的资料,有些方法清空是抄别别人的,不过效果是实现 的,但总是感觉 不理想,不是自己想要的,也希望能通过这篇文章收集到一些好的解决方案,欢迎大家提供资源和解决方法,小弟在些谢过。
欢迎大家转载,如有转载请注明文章来自:http://sufei.cnblogs.com/
签名:做一番一生引以为豪的事业;在有生之年报答帮过我的人;并有能力帮助需要帮助的人;
QQ:361983679 Email:[email protected] MSN:[email protected]