查了很多资料,都找不到直接重写ListBox滚动条的方法,只能曲线救国,先自己重绘一个带皮肤的滚动条,然后让它取代ListBox现有的滚动条. 老习惯,先传个效果图,你觉得感兴趣就继续看下去,不喜欢的话就此打住,懒得耽误你宝
贵的时间,嘿嘿
注意,此图中的滚动条宽度明显小于ListBox本身滚动条的宽度,我目前只顾着实现功能了,毕竟,宽度调整相当简单哈。
下面简单介绍下重绘系统滚动条的详细步骤:
1.在项目中添加新项--用户控件,我们命名为CustomScrollbar.cs
2.准备几张图片添加进项目资源作为滚动条重绘时要用的背景,我用的图片如下:
uparrow.png资源名称为uparrow ,滚动条的上箭头
ThumbBottom.png资源名称为ThumbBottom ,滚动条中间滑道的背景
ThumbMiddle.png资源名称为ThumbMiddle ,滚动条的中间的拖动块
downarrow.png资源名称为downarrow ,滚动条的下箭头
3.然后就是利用上面图片做背景重画滚动条背景了,直接给出CustomScrollbar.cs的代码吧 Code using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Diagnostics; namespace Winamp { [Designer(typeof(ScrollbarControlDesigner))] public partial class CustomScrollbar : UserControl {
protected Color moChannelColor = Color.Empty; protected Image moUpArrowImage = null;//上箭头 //protected Image moUpArrowImage_Over = null; //protected Image moUpArrowImage_Down = null; protected Image moDownArrowImage = null;//下箭头 //protected Image moDownArrowImage_Over = null; //protected Image moDownArrowImage_Down = null; protected Image moThumbArrowImage = null;
protected Image moThumbTopImage = null; protected Image moThumbTopSpanImage = null; protected Image moThumbBottomImage = null; protected Image moThumbBottomSpanImage = null; protected Image moThumbMiddleImage = null;
protected int moLargeChange = 10; protected int moSmallChange = 1; protected int moMinimum = 0; protected int moMaximum = 100; protected int moValue = 0; private int nClickPoint;
protected int moThumbTop = 0;
protected bool moAutoSize = false;
private bool moThumbDown = false; private bool moThumbDragging = false;
public new event EventHandler Scroll = null; public event EventHandler ValueChanged = null;
private int GetThumbHeight() { 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; } if (nThumbHeight < 56) { nThumbHeight = 56; fThumbHeight = 56; }
return nThumbHeight; }
public CustomScrollbar() {
InitializeComponent(); SetStyle(ControlStyles.ResizeRedraw, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.DoubleBuffer, true);
moChannelColor = Color.FromArgb(51, 166, 3); UpArrowImage = BASSSkin.uparrow;//上箭头 DownArrowImage = BASSSkin.downarrow;//下肩头
ThumbBottomImage = BASSSkin.ThumbBottom;
ThumbMiddleImage = BASSSkin.ThumbMiddle;
this.Width = UpArrowImage.Width;//18px base.MinimumSize = new Size(UpArrowImage.Width, UpArrowImage.Height + DownArrowImage.Height + GetThumbHeight()); }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("LargeChange")] public int LargeChange { get { return moLargeChange; } set { moLargeChange = value; Invalidate(); } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("SmallChange")] public int SmallChange { get { return moSmallChange; } set { moSmallChange = value; Invalidate(); } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("Minimum")] public int Minimum { get { return moMinimum; } set { moMinimum = value; Invalidate(); } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("Maximum")] public int Maximum { get { return moMaximum; } set { moMaximum = value; Invalidate(); } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("Value")] public int Value { get { return moValue; } set { moValue = value;
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; } if (nThumbHeight < 56) { nThumbHeight = 56; fThumbHeight = 56; }
//figure out value int nPixelRange = nTrackHeight - nThumbHeight; int nRealRange = (Maximum - Minimum) - LargeChange; float fPerc = 0.0f; if (nRealRange != 0) { fPerc = (float)moValue / (float)nRealRange;
}
float fTop = fPerc * nPixelRange; moThumbTop = (int)fTop;
Invalidate(); } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Skin"), Description("Channel Color")] public Color ChannelColor { get { return moChannelColor; } set { moChannelColor = value; } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Skin"), Description("Up Arrow Graphic")] public Image UpArrowImage { get { return moUpArrowImage; } set { moUpArrowImage = value; } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Skin"), Description("Up Arrow Graphic")] public Image DownArrowImage { get { return moDownArrowImage; } set { moDownArrowImage = value; } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Skin"), Description("Up Arrow Graphic")] public Image ThumbBottomImage { get { return moThumbBottomImage; } set { moThumbBottomImage = value; } }
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Skin"), Description("Up Arrow Graphic")] public Image ThumbMiddleImage { get { return moThumbMiddleImage; } set { moThumbMiddleImage = value; } }
protected override void OnPaint(PaintEventArgs e) {
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.FromArgb(255, 255, 255)); // 函数名: rectangle //功 能: 画一个矩形 //用 法: void far rectangle(int left, int top, int right, int bottom);
//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)));
//draw channel //e.Graphics.FillRectangle(oBrush, new Rectangle(1, UpArrowImage.Height, this.Width-2, (this.Height-DownArrowImage.Height))); e.Graphics.DrawImage(ThumbBottomImage, new Rectangle(0, UpArrowImage.Height, this.Width, (this.Height - DownArrowImage.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; } //MessageBox.Show(nThumbHeight.ToString()); if (nThumbHeight < 56) { nThumbHeight = 56; fThumbHeight = 56; }
//Debug.WriteLine(nThumbHeight.ToString());
//float fSpanHeight = (fThumbHeight - (ThumbMiddleImage.Height + ThumbTopImage.Height + ThumbBottomImage.Height)) / 2.0f; //int nSpanHeight = (int)fSpanHeight;
int nTop = moThumbTop;//0 nTop += UpArrowImage.Height;//9px
//draw top画上面的按钮 //e.Graphics.DrawImage(ThumbTopImage, new Rectangle(0, nTop, this.Width, ThumbTopImage.Height));
//nTop += ThumbTopImage.Height;//10px //draw top span //Rectangle rect = new Rectangle(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 //draw middle e.Graphics.DrawImage(ThumbMiddleImage, new Rectangle(0, nTop, this.Width, ThumbMiddleImage.Height));
//nTop += ThumbMiddleImage.Height; //draw top span //rect = new Rectangle(1, nTop, this.Width - 2, nSpanHeight*2); //e.Graphics.DrawImage(ThumbBottomSpanImage, rect);
//nTop += nSpanHeight; //draw bottom //e.Graphics.DrawImage(ThumbBottomImage, new Rectangle(1, nTop, this.Width - 2, nSpanHeight));
if (DownArrowImage != null) { e.Graphics.DrawImage(DownArrowImage, new Rectangle(new Point(0, (this.Height - DownArrowImage.Height)), new Size(this.Width, DownArrowImage.Height))); }
}
public override bool AutoSize { get { return base.AutoSize; } set { base.AutoSize = value; if (base.AutoSize) { this.Width = moUpArrowImage.Width; } } }
private void InitializeComponent() { this.SuspendLayout(); // // CustomScrollbar // this.Name = "CustomScrollbar"; this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseDown); this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseMove); this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseUp); this.ResumeLayout(false);
}
private void CustomScrollbar_MouseDown(object sender, MouseEventArgs e) { Point ptPoint = this.PointToClient(Cursor.Position); 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; } if (nThumbHeight < 56) { nThumbHeight = 56; fThumbHeight = 56; }
int nTop = moThumbTop; nTop += UpArrowImage.Height;
Rectangle thumbrect = new Rectangle(new Point(1, nTop), new Size(ThumbMiddleImage.Width, nThumbHeight)); if (thumbrect.Contains(ptPoint)) {
//hit the thumb nClickPoint = (ptPoint.Y - nTop); //MessageBox.Show(Convert.ToString((ptPoint.Y - nTop))); this.moThumbDown = true; }
Rectangle uparrowrect = new Rectangle(new Point(1, 0), new Size(UpArrowImage.Width, UpArrowImage.Height)); if (uparrowrect.Contains(ptPoint)) {
int nRealRange = (Maximum - Minimum) - LargeChange; int nPixelRange = (nTrackHeight - nThumbHeight); if (nRealRange > 0) { if (nPixelRange > 0) { if ((moThumbTop - SmallChange) < 0) moThumbTop = 0; else moThumbTop -= SmallChange;
//figure out value float fPerc = (float)moThumbTop / (float)nPixelRange; float fValue = fPerc * (Maximum - LargeChange);
moValue = (int)fValue; Debug.WriteLine(moValue.ToString());
if (ValueChanged != null) ValueChanged(this, new EventArgs());
if (Scroll != null) Scroll(this, new EventArgs());
Invalidate(); } } }
Rectangle downarrowrect = new Rectangle(new Point(1, UpArrowImage.Height + nTrackHeight), new Size(UpArrowImage.Width, UpArrowImage.Height)); if (downarrowrect.Contains(ptPoint)) { int nRealRange = (Maximum - Minimum) - LargeChange; int nPixelRange = (nTrackHeight - nThumbHeight); if (nRealRange > 0) { if (nPixelRange > 0) { if ((moThumbTop + SmallChange) > nPixelRange) moThumbTop = nPixelRange; else moThumbTop += SmallChange;
//figure out value float fPerc = (float)moThumbTop / (float)nPixelRange; float fValue = fPerc * (Maximum - LargeChange);
moValue = (int)fValue; Debug.WriteLine(moValue.ToString());
if (ValueChanged != null) ValueChanged(this, new EventArgs()); if (Scroll != null) Scroll(this, new EventArgs());
Invalidate(); } } } }
private void CustomScrollbar_MouseUp(object sender, MouseEventArgs e) { this.moThumbDown = false; this.moThumbDragging = false; }
private void MoveThumb(int y) { int nRealRange = Maximum - Minimum; 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; } if (nThumbHeight < 56) { nThumbHeight = 56; fThumbHeight = 56; }
int nSpot = nClickPoint;
int nPixelRange = (nTrackHeight - nThumbHeight); if (moThumbDown && nRealRange > 0) { if (nPixelRange > 0) { int nNewThumbTop = y - (UpArrowImage.Height + nSpot);
if (nNewThumbTop < 0) { moThumbTop = nNewThumbTop = 0; } else if (nNewThumbTop > nPixelRange) { moThumbTop = nNewThumbTop = nPixelRange; } else { moThumbTop = y - (UpArrowImage.Height + nSpot); }
//figure out value float fPerc = (float)moThumbTop / (float)nPixelRange; float fValue = fPerc * (Maximum - LargeChange); moValue = (int)fValue; Debug.WriteLine(moValue.ToString());
Application.DoEvents();
Invalidate(); } } }
private void CustomScrollbar_MouseMove(object sender, MouseEventArgs e) { if (moThumbDown == true) { this.moThumbDragging = true; }
if (this.moThumbDragging) {
MoveThumb(e.Y); }
if (ValueChanged != null) ValueChanged(this, new EventArgs());
if (Scroll != null) Scroll(this, new EventArgs()); }
}
internal class ScrollbarControlDesigner : System.Windows.Forms.Design.ControlDesigner {
public override SelectionRules SelectionRules { get { SelectionRules selectionRules = base.SelectionRules; PropertyDescriptor propDescriptor = TypeDescriptor.GetProperties(this.Component)["AutoSize"]; if (propDescriptor != null) { bool autoSize = (bool)propDescriptor.GetValue(this.Component); if (autoSize) { selectionRules = SelectionRules.Visible | SelectionRules.Moveable | SelectionRules.BottomSizeable | SelectionRules.TopSizeable; } else { selectionRules = SelectionRules.Visible | SelectionRules.AllSizeable | SelectionRules.Moveable; } } return selectionRules; } } } } 目前只想简单实现滚动条中上箭头/下箭头/滑道/拖动块的重写,所以以上代码中OnPaint函数里的部分内容被我注释了,好了,这个滚动条控件已经做好了,一个控件而已,你应该会使用它,我就不罗嗦了。
接下来就是怎么用它来控制ListBox的内容滚动的问题了,这需要调用API函数来实现,同时又不能设置ListBox无滚动条,因为ListBox没有滚动条也就没有滚动的事件可捕获,那就达不到滚动的效果了。
在你的窗体里拖一个listbox控件和一个上边我们制作好的用户控件,分明命名为listBox和customScrollbar1,
然后往listBox中随便多弄写内容,使之出现滚动条即可。调整customScrollbar1的位置使之覆盖在listBox的滚动条上,呵呵,这方法不错吧?
然后我们定义一下Win32API,代码如下:
Code public class Win32API { [StructLayout(LayoutKind.Sequential)] public struct tagSCROLLINFO { public uint cbSize; public uint fMask; public int nMin; public int nMax; public uint nPage; public int nPos; public int nTrackPos; } public enum fnBar { SB_HORZ = 0, SB_VERT = 1, SB_CTL = 2 } public enum fMask { SIF_ALL, SIF_DISABLENOSCROLL = 0X0010, SIF_PAGE = 0X0002, SIF_POS = 0X0004, SIF_RANGE = 0X0001, SIF_TRACKPOS = 0X0008 }
public static int MakeLong(short lowPart, short highPart) { return (int)(((ushort)lowPart) | (uint)(highPart << 16)); } public const int SB_THUMBTRACK = 5; public const int WM_HSCROLL = 0x114; public const int WM_VSCROLL = 0x115; [DllImport("user32.dll", EntryPoint = "GetScrollInfo")] public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi); [DllImport("user32.dll", EntryPoint = "SetScrollInfo")] public static extern int SetScrollInfo(IntPtr hwnd, int fnBar, [In] ref SCROLLINFO lpsi, bool fRedraw);
[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")] static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] public static extern bool PostMessage(IntPtr hWnd, uint Msg, long wParam,int lParam); }
public struct SCROLLINFO { public uint cbSize; public uint fMask; public int nMin; public int nMax; public uint nPage; public int nPos; public int nTrackPos; } enum ScrollInfoMask { SIF_RANGE = 0x1, SIF_PAGE = 0x2, SIF_POS = 0x4, SIF_DISABLENOSCROLL = 0x8, SIF_TRACKPOS = 0x10, SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS } enum ScrollBarDirection { SB_HORZ = 0, SB_VERT = 1, SB_CTL = 2, SB_BOTH = 3 }
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(listBox.Handle, (int)ScrollBarDirection.SB_VERT, ref si);
return si;
}
} //当鼠标滚动时,设置该滚动条
private void SetImageListScroll()
{
SCROLLINFO info = tvImageListScrollInfo;
if (info.nMax > 0)
{
int pos = info.nPos - 1;
if (pos >= 0)
{
customScrollbar1.Value = pos;
}
}
} 定义customScrollbar1的滚动事件函数customScrollbar1_Scroll,实现在滚动条滚动的同时发消息给listBox使之同步滚动
Code private void customScrollbar1_Scroll(object sender, EventArgs m) { //当滚动条滚动时,通知控件也跟着滚动吧。。。
SCROLLINFO info = tvImageListScrollInfo;
info.nPos = customScrollbar1.Value;
Win32API.SetScrollInfo(listBox.Handle, (int)ScrollBarDirection.SB_VERT, ref info, true);
Win32API.PostMessage(listBox.Handle, Win32API.WM_VSCROLL, Win32API.MakeLong((short)Win32API.SB_THUMBTRACK, (short)(info.nPos)), 0);
}