![]() |
Desktop Development »
Miscellaneous »
Miscellaneous Controls
Intermediate
A variation on the default TrackBarBy Guinness4StrengthMulti-Color and multi-directional TrackBar. |
C#, Windows, .NET1.0, .NET1.1VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

I was looking for a better looking TrackBar control, when I stumbled upon Alan Zhao's ColorProgressBar code and saw how simple it would be to create my own TrackBar control. So, I came up with the ColorTrackBar. First, let's get some basic syntax down for naming the parts or areas of this control.
ColorTrackBar which will move to set the value. BarBorderColor - Sets/Gets the color to be used when drawing the bar's border.
BarColor - Sets/Gets the color of the control's Bar.
TrackerBorderColor - Sets/Gets the Tracker's border color.
TrackerColor - Sets/Gets the Tracker's color.
Minimum - Sets/Gets the lowest possible number that the ColorTrackBar control can return.
Maximum - Sets/Gets the highest value the control can return.
Value - Sets/Gets the current position of the Tracker relative to the maximum and minimum values.
MaximumValueSide - This property allows the user to decide which direction on the control will increase the Value property, the opposite will obviously decrease Value. Possible values are Left, Right, Top, and Bottom.
BarOrientation - Select either Horizontal or Vertical.
ControlCornerStyle - Select either Square or Rounded corners.
TrackerSize - Specify the width or height of the tracker depending on the BarOrientation selection. Note: if you have selected Rounded corners, you cannot set the Tracker size. Scroll - This event is fired when the position of the Tracker is changed.
ValueChanged - This event fires when the numeric value of the Value property is changed. ColorTrackBar control will now be in your ToolBox, just drag the control onto your form.
There are probably three areas of interest within the source code for this control, the first being drawing the rounded corners:
protected GraphicsPath DrawRoundedCorners(Rectangle Rect,
Color BorderColor,Graphics g)
{
GraphicsPath gPath = new GraphicsPath();
try
{
Pen LinePen = new Pen(BorderColor,borderWidth+1);
switch(barOrientation)
{
case Orientations.Horizontal:
Rectangle LeftRect,RightRect;
LeftRect=new Rectangle(Rect.X,Rect.Y+1,
Rect.Height-1,Rect.Height-2);
RightRect = new Rectangle(Rect.X+
(Rect.Width-Rect.Height),Rect.Y+1,
Rect.Height-1,Rect.Height-2);
//build shape
gPath.AddArc(LeftRect,90,180);
gPath.AddLine(LeftRect.X+LeftRect.Width/2+2,
LeftRect.Top+1,RightRect.X+(RightRect.Width/2)-1,
RightRect.Top+1);
gPath.AddArc(RightRect,270,180);
gPath.AddLine(RightRect.X+(RightRect.Width/2),
RightRect.Bottom, LeftRect.X+(LeftRect.Width/2),
LeftRect.Bottom);
gPath.CloseFigure();
g.DrawPath(LinePen,gPath);
break;
case Orientations.Vertical:
Rectangle TopRect,BotRect;
TopRect=new Rectangle(Rect.X+1,Rect.Y,
Rect.Width-2,Rect.Width-1);
BotRect = new Rectangle(Rect.X+1,Rect.Y+
(Rect.Height-Rect.Width),
Rect.Width-2,Rect.Width-1);
//build shape
gPath.AddArc(TopRect,180,180);
gPath.AddLine(TopRect.Right,
TopRect.Y+TopRect.Height/2,
BotRect.Right,
BotRect.Y+BotRect.Height/2+1);
gPath.AddArc(BotRect,0,180);
gPath.AddLine(BotRect.Left+1,
BotRect.Y+BotRect.Height/2-1,
TopRect.Left+1,TopRect.Y+TopRect.Height/2+2);
gPath.CloseFigure();
g.DrawPath(LinePen,gPath);
break;
default:
break;
}
}
catch(Exception Err)
{
throw new Exception("DrawRoundedCornersException: "
+Err.Message);
}
return gPath;
}
The rounded corners are created by drawing two half-circles based on the control's height. First, I calculated two sub-rectangles from the ClientRectangle in which to draw the circles, then just connected the open ends of the half-circles for the rounded control.
The second area of interest would be the painting of the rounded control, this is where I followed Alan Zhao's lead, using the GradientBrush to give it a 3-D appearance.
protected void PaintPath(GraphicsPath PaintPath,
Color PathColor,Graphics g)
{
Region FirstRegion,SecondRegion;
FirstRegion = new Region(PaintPath);
SecondRegion= new Region(PaintPath);
//
// Fill background
//
SolidBrush bgBrush = new SolidBrush(ControlPaint.Dark(PathColor));
g.FillRegion(bgBrush, new Region(PaintPath));
bgBrush.Dispose();
//
// The gradient brush
//
LinearGradientBrush brush;
Rectangle FirstRect,SecondRect;
Rectangle RegionRect = Rectangle.Truncate(PaintPath.GetBounds());
switch(barOrientation)
{
case Orientations.Horizontal:
FirstRect= new Rectangle(RegionRect.X,RegionRect.Y,
RegionRect.Width, RegionRect.Height / 2);
SecondRect=new Rectangle(RegionRect.X,
RegionRect.Height / 2, RegionRect.Width,
RegionRect.Height / 2);
//only get the bar region
FirstRegion.Intersect(FirstRect);
SecondRegion.Intersect(SecondRect);
// Paint upper half
brush = new LinearGradientBrush(
new Point(FirstRect.Width/2,FirstRect.Top),
new Point(FirstRect.Width/2,FirstRect.Bottom),
ControlPaint.Dark(PathColor),
PathColor);
g.FillRegion(brush, FirstRegion);
brush.Dispose();
// Paint lower half
// (SecondRect.Y - 1 because there would be
// a dark line in the middle of the bar)
brush = new LinearGradientBrush(
new Point(SecondRect.Width/2,
SecondRect.Top-1),
new Point(SecondRect.Width/2,
SecondRect.Bottom),
PathColor,
ControlPaint.Dark(PathColor));
g.FillRegion(brush, SecondRegion);
brush.Dispose();
break;
case Orientations.Vertical:
FirstRect= new Rectangle(RegionRect.X,RegionRect.Y,
RegionRect.Width/2, RegionRect.Height);
SecondRect=new Rectangle(RegionRect.Width / 2,
RegionRect.Y, RegionRect.Width/2,
RegionRect.Height);
//only get the bar region
FirstRegion.Intersect(FirstRect);
SecondRegion.Intersect(SecondRect);
// Paint left half
brush = new LinearGradientBrush(
new Point(FirstRect.Left, FirstRect.Height/2),
new Point(FirstRect.Right,FirstRect.Height/2),
ControlPaint.Dark(PathColor),
PathColor);
g.FillRegion(brush, FirstRegion);
brush.Dispose();
// Paint right half
// (SecondRect.X - 1 because there would be a dark line
// in the middle of the bar)
brush = new LinearGradientBrush(
new Point(SecondRect.Left - 1,SecondRect.Height/2),
new Point(SecondRect.Right,SecondRect.Height/2),
PathColor,
ControlPaint.Dark(PathColor));
g.FillRegion(brush, SecondRegion);
brush.Dispose();
break;
default:
break;
}
}
First, I used GraphicsPath.GetBounds to get a RectangleF object which I then truncate into a regular Rectangle object. From there, I calculate the upper/lower or left/right halves of the control. In order to "filter" out the corners of the rectangle, I then get the Region that intersects both the GraphicPath's rectangle and the GraphicsPath shape (the rounded corners). I then just fill the two halves with background color.
Finally, I think the movement of the Tracker needs some explaining. I have never created any control that moves like this one, so forgive my kludgey solution. All the important code for the movement is in the WndProc() method.
I handle three messages:
WM_LBUTTONDOWN (0x0201)
WM_LBUTTONUP (0x0202)
WM_MOUSEMOVE (0x0200) if(m.Msg==0x0201)
{
Point CurPoint=new Point(LowWord((uint)m.LParam),
HighWord((uint)m.LParam));
if(trackRect.Contains(CurPoint))
{
if(!leftbuttonDown)
{
leftbuttonDown=true;
switch(this.barOrientation)
{
case Orientations.Horizontal:
mousestartPos= CurPoint.X-trackRect.X;
break;
case Orientations.Vertical:
mousestartPos= CurPoint.Y-trackRect.Y;
break;
}
}
}
else
{
int OffSet=0;
switch(this.barOrientation)
{
case Orientations.Horizontal:
if(trackRect.Right+(CurPoint.X-trackRect.X
-(trackRect.Width/2))>=this.Width)
OffSet=this.Width-trackRect.Right-1;
else if(trackRect.Left+(CurPoint.X-
trackRect.X-(trackRect.Width/2))<=0)
OffSet=(trackRect.Left-1)*-1;
else
OffSet=CurPoint.X-trackRect.X-(trackRect.Width/2);
trackRect.Offset(OffSet,0);
trackerValue=(int)( ((trackRect.X-1) *
(barMaximum-barMinimum))/(this.Width-trackSize-2));
if(maxSide==Poles.Left)
trackerValue=(trackerValue-(barMaximum-barMinimum))*-1;
break;
case Orientations.Vertical:
if(trackRect.Bottom+(CurPoint.Y-trackRect.Y-
(trackRect.Height/2))>=this.Height)
OffSet=this.Height-trackRect.Bottom-1;
else if(trackRect.Top+(CurPoint.Y-
trackRect.Y-(trackRect.Height/2))<=0)
OffSet=(trackRect.Top-1)*-1;
else
OffSet=CurPoint.Y-trackRect.Y-(trackRect.Height/2);
trackRect.Offset(0,OffSet);
trackerValue=(int)( ((trackRect.Y-1) *
(barMaximum-barMinimum))/(this.Height-trackSize-2));
if(maxSide==Poles.Top)
trackerValue=(trackerValue-(barMaximum-barMinimum))*-1;
break;
default:
break;
}
trackerValue+=barMinimum;
this.Invalidate();
if(OffSet!=0)
{
OnScroll();
OnValueChanged();
}
}
this.Focus();
}
//WM_MOUSEMOVE
if(m.Msg==0x0200)
{
int OldValue=trackerValue;
Point CurPoint=new Point(LowWord((uint)m.LParam),
HighWord((uint)m.LParam));
if(leftbuttonDown && ClientRectangle.Contains(CurPoint))
{
int OffSet=0;
try
{
switch(this.barOrientation)
{
case Orientations.Horizontal:
if(trackRect.Right+(CurPoint.X-trackRect.X
-mousestartPos)>=this.Width)
OffSet=this.Width-trackRect.Right-1;
else if(trackRect.Left+(CurPoint.X-
trackRect.X-mousestartPos)<=0)
OffSet=(trackRect.Left-1)*-1;
else
OffSet=CurPoint.X-trackRect.X-mousestartPos;
trackRect.Offset(OffSet,0);
trackerValue=(int)( ((trackRect.X-1) *
(barMaximum-barMinimum))/(this.Width-trackSize-2));
if(maxSide==Poles.Left)
trackerValue=(trackerValue-(barMaximum-barMinimum))*-1;
break;
case Orientations.Vertical:
if(trackRect.Bottom+(CurPoint.Y-
trackRect.Y-mousestartPos)>=this.Height)
OffSet=this.Height-trackRect.Bottom-1;
else if(trackRect.Top+(CurPoint.Y-
trackRect.Y-mousestartPos)<=0)
OffSet=(trackRect.Top-1)*-1;
else
OffSet=CurPoint.Y-trackRect.Y-mousestartPos;
trackRect.Offset(0,OffSet);
trackerValue=(int)( ((trackRect.Y-1) *
(barMaximum-barMinimum))/(this.Height-trackSize-2));
if(maxSide==Poles.Top)
trackerValue=(trackerValue-(barMaximum-barMinimum))*-1;
break;
}
}
catch(Exception){}
finally
{
//force redraw
trackerValue+=barMinimum;
this.Invalidate();
if(OffSet!=0)
{
OnScroll();
OnValueChanged();
}
}
}
}
Basically, if the user clicks inside the Tracker's region, I save that initial point in mousestartPos, which I then use to calculate the offset of the Tracker's rectangle or region when the user drags the Tracker. If the initial point is not in the Tracker's region, but still within the Bar's region, I jump the Tracker's center to the left click position.
When I receive the MOVE message, I check to see that the left button is down with my leftbuttonDown state variable, and offset the Tracker rectangle based on the initial start position, the current mouse position, and the current Tracker rectangle. Then, I calculate the Value (trackerValue) based on the new Tracker rectangle.
if(m.Msg==0x0202)
{
leftbuttonDown=false;
}
When the left button up message is received, I simply set my state variable back to false to stop any movement, and reset allows the mousestartPos to be reset next time the user clicks on the control.
I removed many of the standard Control events from the designer using the ControlDesigner class. These can be restored very easily by editing the ColorTrackBar source code.
I hope others find this control useful, please send me any suggestions or comments.
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 17 Aug 2004 Editor: Smitha Vijayan |
Copyright 2004 by Guinness4Strength Everything else Copyright © CodeProject, 1999-2010 Web18 | Advertise on the Code Project |