namespace CustomScrollBar { using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; /// /// The scrollbar renderer class. /// internal static class ScrollBarExRenderer { #region fields /// /// The colors of the thumb in the 3 states. /// private static Color[,] thumbColors = new Color[3, 8]; /// /// The arrow colors in the three states. /// private static Color[,] arrowColors = new Color[3, 8]; /// /// The arrow border colors. /// private static Color[] arrowBorderColors = new Color[4]; /// /// The background colors. /// private static Color[] backgroundColors = new Color[5]; /// /// The track colors. /// private static Color[] trackColors = new Color[2]; #endregion #region constructor /// /// Initializes static members of the class. /// static ScrollBarExRenderer() { // hot state thumbColors[0, 0] = Color.FromArgb(96, 111, 148); // border color thumbColors[0, 1] = Color.FromArgb(232, 233, 233); // left/top start color thumbColors[0, 2] = Color.FromArgb(230, 233, 241); // left/top end color thumbColors[0, 3] = Color.FromArgb(233, 237, 242); // right/bottom line color thumbColors[0, 4] = Color.FromArgb(209, 218, 228); // right/bottom start color thumbColors[0, 5] = Color.FromArgb(218, 227, 235); // right/bottom end color thumbColors[0, 6] = Color.FromArgb(190, 202, 219); // right/bottom middle color thumbColors[0, 7] = Color.FromArgb(96, 11, 148); // left/top line color // over state thumbColors[1, 0] = Color.FromArgb(60, 110, 176); thumbColors[1, 1] = Color.FromArgb(187, 204, 228); thumbColors[1, 2] = Color.FromArgb(205, 227, 254); thumbColors[1, 3] = Color.FromArgb(252, 253, 255); thumbColors[1, 4] = Color.FromArgb(170, 207, 247); thumbColors[1, 5] = Color.FromArgb(219, 232, 251); thumbColors[1, 6] = Color.FromArgb(190, 202, 219); thumbColors[1, 7] = Color.FromArgb(233, 233, 235); // pressed state thumbColors[2, 0] = Color.FromArgb(23, 73, 138); thumbColors[2, 1] = Color.FromArgb(154, 184, 225); thumbColors[2, 2] = Color.FromArgb(166, 202, 250); thumbColors[2, 3] = Color.FromArgb(221, 235, 251); thumbColors[2, 4] = Color.FromArgb(110, 166, 240); thumbColors[2, 5] = Color.FromArgb(194, 218, 248); thumbColors[2, 6] = Color.FromArgb(190, 202, 219); thumbColors[2, 7] = Color.FromArgb(194, 211, 231); /* picture of colors and indices *(0,0) * ----------------------------------------------- * | | * | |-----------------------------------------| | * | | (2) | | * | | |-------------------------------------| | | * | | | (0) | | | * | | | | | | * | | | | | | * | |3| (1) |3| | * | |6| (4) |6| | * | | | | | | * | | | (5) | | | * | | |-------------------------------------| | | * | | (12) | | * | |-----------------------------------------| | * | | * ----------------------------------------------- (15,17) */ // hot state arrowColors[0, 0] = Color.FromArgb(223, 236, 252); arrowColors[0, 1] = Color.FromArgb(207, 225, 248); arrowColors[0, 2] = Color.FromArgb(245, 249, 255); arrowColors[0, 3] = Color.FromArgb(237, 244, 252); arrowColors[0, 4] = Color.FromArgb(244, 249, 255); arrowColors[0, 5] = Color.FromArgb(244, 249, 255); arrowColors[0, 6] = Color.FromArgb(251, 253, 255); arrowColors[0, 7] = Color.FromArgb(251, 253, 255); // over state arrowColors[1, 0] = Color.FromArgb(205, 222, 243); arrowColors[1, 1] = Color.FromArgb(186, 208, 235); arrowColors[1, 2] = Color.FromArgb(238, 244, 252); arrowColors[1, 3] = Color.FromArgb(229, 237, 247); arrowColors[1, 4] = Color.FromArgb(223, 234, 247); arrowColors[1, 5] = Color.FromArgb(241, 246, 254); arrowColors[1, 6] = Color.FromArgb(243, 247, 252); arrowColors[1, 7] = Color.FromArgb(250, 252, 255); // pressed state arrowColors[2, 0] = Color.FromArgb(215, 220, 225); arrowColors[2, 1] = Color.FromArgb(195, 202, 210); arrowColors[2, 2] = Color.FromArgb(242, 244, 245); arrowColors[2, 3] = Color.FromArgb(232, 235, 238); arrowColors[2, 4] = Color.FromArgb(226, 228, 230); arrowColors[2, 5] = Color.FromArgb(230, 233, 236); arrowColors[2, 6] = Color.FromArgb(244, 245, 245); arrowColors[2, 7] = Color.FromArgb(245, 247, 248); // background colors backgroundColors[0] = Color.FromArgb(235, 237, 239); backgroundColors[1] = Color.FromArgb(252, 252, 252); backgroundColors[2] = Color.FromArgb(247, 247, 247); backgroundColors[3] = Color.FromArgb(238, 238, 238); backgroundColors[4] = Color.FromArgb(240, 240, 240); // track colors trackColors[0] = Color.FromArgb(204, 204, 204); trackColors[1] = Color.FromArgb(220, 220, 220); // arrow border colors arrowBorderColors[0] = Color.FromArgb(135, 146, 160); arrowBorderColors[1] = Color.FromArgb(140, 151, 165); arrowBorderColors[2] = Color.FromArgb(128, 139, 153); arrowBorderColors[3] = Color.FromArgb(99, 110, 125); } #endregion #region methods #region public methods /// /// Draws the background. /// /// The used to paint. /// The rectangle in which to paint. /// The . public static void DrawBackground( Graphics g, Rectangle rect, ScrollBarOrientation orientation) { if (g == null) { throw new ArgumentNullException("g"); } if (rect.IsEmpty || g.IsVisibleClipEmpty || !g.VisibleClipBounds.IntersectsWith(rect)) { return; } if (orientation == ScrollBarOrientation.Vertical) { DrawBackgroundVertical(g, rect); } else { DrawBackgroundHorizontal(g, rect); } } /// /// Draws the channel ( or track ). /// /// The used to paint. /// The rectangle in which to paint. /// The scrollbar state. /// The . public static void DrawTrack( Graphics g, Rectangle rect, ScrollBarState state, ScrollBarOrientation orientation) { if (g == null) { throw new ArgumentNullException("g"); } if (rect.Width <= 0 || rect.Height <= 0 || state != ScrollBarState.Pressed || g.IsVisibleClipEmpty || !g.VisibleClipBounds.IntersectsWith(rect)) { return; } if (orientation == ScrollBarOrientation.Vertical) { DrawTrackVertical(g, rect); } else { DrawTrackHorizontal(g, rect); } } /// /// Draws the thumb. /// /// The used to paint. /// The rectangle in which to paint. /// The of the thumb. /// The . public static void DrawThumb( Graphics g, Rectangle rect, ScrollBarState state, ScrollBarOrientation orientation) { if (g == null) { throw new ArgumentNullException("g"); } if (rect.IsEmpty || g.IsVisibleClipEmpty || !g.VisibleClipBounds.IntersectsWith(rect) || state == ScrollBarState.Disabled) { return; } if (orientation == ScrollBarOrientation.Vertical) { DrawThumbVertical(g, rect, state); } else { DrawThumbHorizontal(g, rect, state); } } /// /// Draws the grip of the thumb. /// /// The used to paint. /// The rectangle in which to paint. /// The . public static void DrawThumbGrip( Graphics g, Rectangle rect, ScrollBarOrientation orientation) { if (g == null) { throw new ArgumentNullException("g"); } if (rect.IsEmpty || g.IsVisibleClipEmpty || !g.VisibleClipBounds.IntersectsWith(rect)) { return; } // get grip image using (Image gripImage = Properties.ScrollBarResources.GripNormal) { // adjust rectangle and rotate grip image if necessary Rectangle r = AdjustThumbGrip(rect, orientation, gripImage); // adjust alpha channel of grip image using (ImageAttributes attr = new ImageAttributes()) { attr.SetColorMatrix( new ColorMatrix(new float[][] { new[] { 1F, 0, 0, 0, 0 }, new[] { 0, 1F, 0, 0, 0 }, new[] { 0, 0, 1F, 0, 0 }, new[] { 0, 0, 0, .8F, 0 }, new[] { 0, 0, 0, 0, 1F } }), ColorMatrixFlag.Default, ColorAdjustType.Bitmap ); // draw grip image g.DrawImage(gripImage, r, 0, 0, r.Width, r.Height, GraphicsUnit.Pixel, attr); } } } /// /// Draws an arrow button. /// /// The used to paint. /// The rectangle in which to paint. /// The of the arrow button. /// true for an up arrow, false otherwise. /// The . public static void DrawArrowButton( Graphics g, Rectangle rect, ScrollBarArrowButtonState state, bool arrowUp, ScrollBarOrientation orientation) { if (g == null) { throw new ArgumentNullException("g"); } if (rect.IsEmpty || g.IsVisibleClipEmpty || !g.VisibleClipBounds.IntersectsWith(rect)) { return; } if (orientation == ScrollBarOrientation.Vertical) { DrawArrowButtonVertical(g, rect, state, arrowUp); } else { DrawArrowButtonHorizontal(g, rect, state, arrowUp); } } #endregion #region private methods /// /// Draws the background. /// /// The used to paint. /// The rectangle in which to paint. private static void DrawBackgroundVertical(Graphics g, Rectangle rect) { using (Pen p = new Pen(backgroundColors[0])) { g.DrawLine(p, rect.Left + 1, rect.Top + 1, rect.Left + 1, rect.Bottom - 1); g.DrawLine(p, rect.Right - 2, rect.Top + 1, rect.Right - 2, rect.Bottom - 1); } using (Pen p = new Pen(backgroundColors[1])) { g.DrawLine(p, rect.Left + 2, rect.Top + 1, rect.Left + 2, rect.Bottom - 1); } Rectangle firstRect = new Rectangle( rect.Left + 3, rect.Top, 8, rect.Height); Rectangle secondRect = new Rectangle( firstRect.Right - 1, firstRect.Top, 7, firstRect.Height); using (LinearGradientBrush brush = new LinearGradientBrush( firstRect, backgroundColors[2], backgroundColors[3], LinearGradientMode.Horizontal)) { g.FillRectangle(brush, firstRect); } using (LinearGradientBrush brush = new LinearGradientBrush( secondRect, backgroundColors[3], backgroundColors[4], LinearGradientMode.Horizontal)) { g.FillRectangle(brush, secondRect); } } /// /// Draws the background. /// /// The used to paint. /// The rectangle in which to paint. private static void DrawBackgroundHorizontal(Graphics g, Rectangle rect) { using (Pen p = new Pen(backgroundColors[0])) { g.DrawLine(p, rect.Left + 1, rect.Top + 1, rect.Right - 1, rect.Top + 1); g.DrawLine(p, rect.Left + 1, rect.Bottom - 2, rect.Right - 1, rect.Bottom - 2); } using (Pen p = new Pen(backgroundColors[1])) { g.DrawLine(p, rect.Left + 1, rect.Top + 2, rect.Right - 1, rect.Top + 2); } Rectangle firstRect = new Rectangle( rect.Left, rect.Top + 3, rect.Width, 8); Rectangle secondRect = new Rectangle( firstRect.Left, firstRect.Bottom - 1, firstRect.Width, 7); using (LinearGradientBrush brush = new LinearGradientBrush( firstRect, backgroundColors[2], backgroundColors[3], LinearGradientMode.Vertical)) { g.FillRectangle(brush, firstRect); } using (LinearGradientBrush brush = new LinearGradientBrush( secondRect, backgroundColors[3], backgroundColors[4], LinearGradientMode.Vertical)) { g.FillRectangle(brush, secondRect); } } /// /// Draws the channel ( or track ). /// /// The used to paint. /// The rectangle in which to paint. private static void DrawTrackVertical(Graphics g, Rectangle rect) { Rectangle innerRect = new Rectangle(rect.Left + 1, rect.Top, 15, rect.Height); using (LinearGradientBrush brush = new LinearGradientBrush( innerRect, trackColors[0], trackColors[1], LinearGradientMode.Horizontal)) { g.FillRectangle(brush, innerRect); } } /// /// Draws the channel ( or track ). /// /// The used to paint. /// The rectangle in which to paint. private static void DrawTrackHorizontal(Graphics g, Rectangle rect) { Rectangle innerRect = new Rectangle(rect.Left, rect.Top + 1, rect.Width, 15); using (LinearGradientBrush brush = new LinearGradientBrush( innerRect, trackColors[0], trackColors[1], LinearGradientMode.Vertical)) { g.FillRectangle(brush, innerRect); } } /// /// Adjusts the thumb grip according to the specified . /// /// The rectangle to adjust. /// The scrollbar orientation. /// The grip image. /// The adjusted rectangle. /// Also rotates the grip image if necessary. private static Rectangle AdjustThumbGrip( Rectangle rect, ScrollBarOrientation orientation, Image gripImage) { Rectangle r = rect; r.Inflate(-1, -1); if (orientation == ScrollBarOrientation.Vertical) { r.X += 3; r.Y += (r.Height / 2) - (gripImage.Height / 2); } else { gripImage.RotateFlip(RotateFlipType.Rotate90FlipNone); r.X += (r.Width / 2) - (gripImage.Width / 2); r.Y += 3; } r.Width = gripImage.Width; r.Height = gripImage.Height; return r; } /// /// Draws the thumb. /// /// The used to paint. /// The rectangle in which to paint. /// The of the thumb. private static void DrawThumbVertical( Graphics g, Rectangle rect, ScrollBarState state) { int index = 0; switch (state) { case ScrollBarState.Hot: { index = 1; break; } case ScrollBarState.Pressed: { index = 2; break; } } Rectangle innerRect = rect; innerRect.Inflate(-1, -1); Rectangle r = innerRect; r.Width = 6; r.Height++; // draw left gradient using (LinearGradientBrush brush = new LinearGradientBrush( r, thumbColors[index, 1], thumbColors[index, 2], LinearGradientMode.Horizontal)) { g.FillRectangle(brush, r); } r.X = r.Right; // draw right gradient if (index == 0) { using (LinearGradientBrush brush = new LinearGradientBrush( r, thumbColors[index, 4], thumbColors[index, 5], LinearGradientMode.Horizontal)) { brush.InterpolationColors = new ColorBlend(3) { Colors = new[] { thumbColors[index, 4], thumbColors[index, 6], thumbColors[index, 5] }, Positions = new[] { 0f, .5f, 1f } }; g.FillRectangle(brush, r); } } else { using (LinearGradientBrush brush = new LinearGradientBrush( r, thumbColors[index, 4], thumbColors[index, 5], LinearGradientMode.Horizontal)) { g.FillRectangle(brush, r); } // draw left line using (Pen p = new Pen(thumbColors[index, 7])) { g.DrawLine(p, innerRect.X, innerRect.Y, innerRect.X, innerRect.Bottom); } } // draw right line using (Pen p = new Pen(thumbColors[index, 3])) { g.DrawLine(p, innerRect.Right, innerRect.Y, innerRect.Right, innerRect.Bottom); } g.SmoothingMode = SmoothingMode.AntiAlias; // draw border using (Pen p = new Pen(thumbColors[index, 0])) { using (GraphicsPath path = CreateRoundPath(rect, 2f, 2f)) { g.DrawPath(p, path); } } g.SmoothingMode = SmoothingMode.None; } /// /// Draws the thumb. /// /// The used to paint. /// The rectangle in which to paint. /// The of the thumb. private static void DrawThumbHorizontal( Graphics g, Rectangle rect, ScrollBarState state) { int index = 0; switch (state) { case ScrollBarState.Hot: { index = 1; break; } case ScrollBarState.Pressed: { index = 2; break; } } Rectangle innerRect = rect; innerRect.Inflate(-1, -1); Rectangle r = innerRect; r.Height = 6; r.Width++; // draw left gradient using (LinearGradientBrush brush = new LinearGradientBrush( r, thumbColors[index, 1], thumbColors[index, 2], LinearGradientMode.Vertical)) { g.FillRectangle(brush, r); } r.Y = r.Bottom; // draw right gradient if (index == 0) { using (LinearGradientBrush brush = new LinearGradientBrush( r, thumbColors[index, 4], thumbColors[index, 5], LinearGradientMode.Vertical)) { brush.InterpolationColors = new ColorBlend(3) { Colors = new[] { thumbColors[index, 4], thumbColors[index, 6], thumbColors[index, 5] }, Positions = new[] { 0f, .5f, 1f } }; g.FillRectangle(brush, r); } } else { using (LinearGradientBrush brush = new LinearGradientBrush( r, thumbColors[index, 4], thumbColors[index, 5], LinearGradientMode.Vertical)) { g.FillRectangle(brush, r); } // draw left line using (Pen p = new Pen(thumbColors[index, 7])) { g.DrawLine(p, innerRect.X, innerRect.Y, innerRect.Right, innerRect.Y); } } // draw right line using (Pen p = new Pen(thumbColors[index, 3])) { g.DrawLine(p, innerRect.X, innerRect.Bottom, innerRect.Right, innerRect.Bottom); } g.SmoothingMode = SmoothingMode.AntiAlias; // draw border using (Pen p = new Pen(thumbColors[index, 0])) { using (GraphicsPath path = CreateRoundPath(rect, 2f, 2f)) { g.DrawPath(p, path); } } g.SmoothingMode = SmoothingMode.None; } /// /// Draws an arrow button. /// /// The used to paint. /// The rectangle in which to paint. /// The of the arrow button. /// true for an up arrow, false otherwise. private static void DrawArrowButtonVertical( Graphics g, Rectangle rect, ScrollBarArrowButtonState state, bool arrowUp) { using (Image arrowImage = GetArrowDownButtonImage(state)) { if (arrowUp) { arrowImage.RotateFlip(RotateFlipType.Rotate180FlipNone); } g.DrawImage(arrowImage, rect); } } /// /// Draws an arrow button. /// /// The used to paint. /// The rectangle in which to paint. /// The of the arrow button. /// true for an up arrow, false otherwise. private static void DrawArrowButtonHorizontal( Graphics g, Rectangle rect, ScrollBarArrowButtonState state, bool arrowUp) { using (Image arrowImage = GetArrowDownButtonImage(state)) { if (arrowUp) { arrowImage.RotateFlip(RotateFlipType.Rotate90FlipNone); } else { arrowImage.RotateFlip(RotateFlipType.Rotate270FlipNone); } g.DrawImage(arrowImage, rect); } } /// /// Draws the arrow down button for the scrollbar. /// /// The button state. /// The arrow down button as . private static Image GetArrowDownButtonImage( ScrollBarArrowButtonState state) { Rectangle rect = new Rectangle(0, 0, 15, 17); Bitmap bitmap = new Bitmap(15, 17, PixelFormat.Format32bppArgb); bitmap.SetResolution(72f, 72f); using (Graphics g = Graphics.FromImage(bitmap)) { g.SmoothingMode = SmoothingMode.None; g.InterpolationMode = InterpolationMode.Low; int index = -1; switch (state) { case ScrollBarArrowButtonState.UpHot: case ScrollBarArrowButtonState.DownHot: { index = 1; break; } case ScrollBarArrowButtonState.UpActive: case ScrollBarArrowButtonState.DownActive: { index = 0; break; } case ScrollBarArrowButtonState.UpPressed: case ScrollBarArrowButtonState.DownPressed: { index = 2; break; } } if (index != -1) { using (Pen p1 = new Pen(arrowBorderColors[0]), p2 = new Pen(arrowBorderColors[1])) { g.DrawLine(p1, rect.X, rect.Y, rect.Right - 1, rect.Y); g.DrawLine(p2, rect.X, rect.Bottom - 1, rect.Right - 1, rect.Bottom - 1); } rect.Inflate(0, -1); using (LinearGradientBrush brush = new LinearGradientBrush( rect, arrowBorderColors[2], arrowBorderColors[1], LinearGradientMode.Vertical)) { ColorBlend blend = new ColorBlend(3) { Positions = new[] { 0f, .5f, 1f }, Colors = new[] { arrowBorderColors[2], arrowBorderColors[3], arrowBorderColors[0] } }; brush.InterpolationColors = blend; using (Pen p = new Pen(brush)) { g.DrawLine(p, rect.X, rect.Y, rect.X, rect.Bottom - 1); g.DrawLine(p, rect.Right - 1, rect.Y, rect.Right - 1, rect.Bottom - 1); } } rect.Inflate(0, 1); Rectangle upper = rect; upper.Inflate(-1, 0); upper.Y++; upper.Height = 7; using (LinearGradientBrush brush = new LinearGradientBrush( upper, arrowColors[index, 2], arrowColors[index, 3], LinearGradientMode.Vertical)) { g.FillRectangle(brush, upper); } upper.Inflate(-1, 0); upper.Height = 6; using (LinearGradientBrush brush = new LinearGradientBrush( upper, arrowColors[index, 0], arrowColors[index, 1], LinearGradientMode.Vertical)) { g.FillRectangle(brush, upper); } Rectangle lower = rect; lower.Inflate(-1, 0); lower.Y = 8; lower.Height = 8; using (LinearGradientBrush brush = new LinearGradientBrush( lower, arrowColors[index, 6], arrowColors[index, 7], LinearGradientMode.Vertical)) { g.FillRectangle(brush, lower); } lower.Inflate(-1, 0); using (LinearGradientBrush brush = new LinearGradientBrush( lower, arrowColors[index, 4], arrowColors[index, 5], LinearGradientMode.Vertical)) { g.FillRectangle(brush, lower); } } using (Image arrowIcon = Properties.ScrollBarResources.ScrollBarArrowDown) { if (state == ScrollBarArrowButtonState.DownDisabled || state == ScrollBarArrowButtonState.UpDisabled) { System.Windows.Forms.ControlPaint.DrawImageDisabled( g, arrowIcon, 3, 6, Color.Transparent); } else { g.DrawImage(arrowIcon, 3, 6); } } } return bitmap; } /// /// Creates a rounded rectangle. /// /// The rectangle to create the rounded rectangle from. /// The x-radius. /// The y-radius. /// A object representing the rounded rectangle. private static GraphicsPath CreateRoundPath( Rectangle r, float radiusX, float radiusY) { // create new graphics path object GraphicsPath path = new GraphicsPath(); // calculate radius of edges PointF d = new PointF(Math.Min(radiusX * 2, r.Width), Math.Min(radiusY * 2, r.Height)); // make sure radius is valid d.X = Math.Max(1, d.X); d.Y = Math.Max(1, d.Y); // add top left arc path.AddArc(r.X, r.Y, d.X, d.Y, 180, 90); // add top right arc path.AddArc(r.Right - d.X, r.Y, d.X, d.Y, 270, 90); // add bottom right arc path.AddArc(r.Right - d.X, r.Bottom - d.Y, d.X, d.Y, 0, 90); // add bottom left arc path.AddArc(r.X, r.Bottom - d.Y, d.X, d.Y, 90, 90); // close path path.CloseFigure(); return path; } #endregion #endregion } }