Click here to Skip to main content
15,885,546 members
Please Sign up or sign in to vote.
4.00/5 (2 votes)
Hi,
I am quite new to using Visual C#. I have come across a problem where I need a slider (TrackBar) to have 3 thumbs, one to select min, one for max and one for the actual selection.

I have spent the last few hours on google trying to find a solution. I have found a few custom trackbar controls (with 1 slider) and a few range controls (with 2 sliders) but nothing for what I am after.

Can anyone guide me to how I can make such a control? Any information would be great at this stage!
Thanks,
Richard
Posted
Comments
RHodgett 17-Feb-11 7:22am    
Sorry my question was unclear, what I am looking for is 1 trackbar with three thumbs like:

|------0----0--0--------|

So the user can select their estimated minimum, projected and maximum values.

Thank you so much to everyone who has already answered!

[Description("Very basic slider control with selection range.")]
 public partial class SelectionRangeSlider : UserControl
 {
   /// <summary>
   /// Minimum value of the slider.
   /// </summary>
   [Description("Minimum value of the slider.")]
   public Single Min
   {
     get { return min; }
     set { min = value; Invalidate(); }
   }
   Single min = 0;
   /// <summary>
   /// Maximum value of the slider.
   /// </summary>
   [Description("Maximum value of the slider.")]
   public Single Max
   {
     get { return max; }
     set { max = value; Invalidate(); }
   }
   Single max = 100;
   /// <summary>
   /// Minimum value of the selection range.
   /// </summary>
   [Description("Minimum value of the selection range.")]
   public Single SelectedMin
   {
     get { return selectedMin; }
     set
     {
       selectedMin = value;
       if (SelectionChanged != null)
         SelectionChanged(this, null);
       Invalidate();
     }
   }
   Single selectedMin = 0;
   /// <summary>
   /// Maximum value of the selection range.
   /// </summary>
   [Description("Maximum value of the selection range.")]
   public Single SelectedMax
   {
     get { return selectedMax; }
     set
     {
       selectedMax = value;
       if (SelectionChanged != null)
         SelectionChanged(this, null);
       Invalidate();
     }
   }
   Single selectedMax = 100;
   /// <summary>
   /// Current value.
   /// </summary>
   [Description("Current value.")]
   public Single Value
   {
     get { return value; }
     set
     {
       this.value = value;
       if (ValueChanged != null)
         ValueChanged(this, null);
       Invalidate();
     }
   }
   Single value = 50;
   /// <summary>
   /// Fired when SelectedMin or SelectedMax changes.
   /// </summary>
   [Description("Fired when SelectedMin or SelectedMax changes.")]
   public event EventHandler SelectionChanged;
   /// <summary>
   /// Fired when Value changes.
   /// </summary>
   [Description("Fired when Value changes.")]
   public event EventHandler ValueChanged;

   public SelectionRangeSlider()
   {
     InitializeComponent();
     //avoid flickering
     SetStyle(ControlStyles.AllPaintingInWmPaint, true);
     SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
     Paint += new PaintEventHandler(SelectionRangeSlider_Paint);
     MouseDown += new MouseEventHandler(SelectionRangeSlider_MouseDown);
     MouseMove += new MouseEventHandler(SelectionRangeSlider_MouseMove);
   }

   Font drawFont = new Font("Arial", 16);
   SolidBrush drawBrush = new SolidBrush(Color.White);

   void SelectionRangeSlider_Paint(object sender, PaintEventArgs e)
   {
     //paint background in white
     e.Graphics.FillRectangle(Brushes.Transparent, ClientRectangle);
     //paint selection range in blue
     Rectangle selectionRect = new Rectangle((int)((selectedMin - Min) * Width / (Max - Min)), 0, (int)((selectedMax - selectedMin) * Width / (Max - Min)), Height);
     e.Graphics.FillRectangle(Brushes.Blue, selectionRect);
     //draw a black frame around our control
     e.Graphics.DrawRectangle(Pens.Black, 0, 0, Width - 1, Height - 1);

     e.Graphics.DrawString(SelectedMin.ToString("0.00"), drawFont, drawBrush, ClientRectangle.Left, ClientRectangle.Top);
     e.Graphics.DrawString(SelectedMax.ToString("0.00"), drawFont, drawBrush, ClientRectangle.Right - 85, ClientRectangle.Top);
     e.Graphics.DrawString(value.ToString("0.00"), drawFont, drawBrush, (ClientRectangle.Left + ClientRectangle.Right) / 2, ClientRectangle.Top);

   }

   void SelectionRangeSlider_MouseDown(object sender, MouseEventArgs e)
   {
     //check where the user clicked so we can decide which thumb to move
     Single pointedValue = Min + e.X * (Max - Min) / Width;
     Single distValue = Math.Abs(pointedValue - Value);
     Single distMin = Math.Abs(pointedValue - SelectedMin);
     Single distMax = Math.Abs(pointedValue - SelectedMax);
     Single minDist = Math.Min(distValue, Math.Min(distMin, distMax));
     if (minDist == distValue)
       movingMode = MovingMode.MovingValue;
     else if (minDist == distMin)
       movingMode = MovingMode.MovingMin;
     else
       movingMode = MovingMode.MovingMax;
     //call this to refreh the position of the selected thumb
     SelectionRangeSlider_MouseMove(sender, e);
   }

   void SelectionRangeSlider_MouseMove(object sender, MouseEventArgs e)
   {
     //if the left button is pushed, move the selected thumb
     if (e.Button != MouseButtons.Left)
       return;
     Single pointedValue = Min + e.X * (Max - Min) / Width;

     if (movingMode == MovingMode.MovingMax && pointedValue > max)
     {
       pointedValue = max;
     }

     if (movingMode == MovingMode.MovingMax && pointedValue < selectedMin + 5)
     {
       pointedValue = selectedMin + 5;
     }

     if (movingMode == MovingMode.MovingMin && pointedValue < min)
     {
       pointedValue = min;
     }

     if (movingMode == MovingMode.MovingMin && pointedValue > selectedMax - 5)
     {
       pointedValue = selectedMax - 5;
     }

     if (movingMode == MovingMode.MovingValue)
       Value = (Single)Math.Round(pointedValue, 2);
     else if (movingMode == MovingMode.MovingMin)
       SelectedMin = (Single)Math.Round(pointedValue, 2);
     else if (movingMode == MovingMode.MovingMax)
       SelectedMax = (Single)Math.Round(pointedValue, 2);
   }

   /// <summary>
   /// To know which thumb is moving
   /// </summary>
   enum MovingMode { MovingValue, MovingMin, MovingMax }
   MovingMode movingMode;
 }
 
Share this answer
 
v3
Comments
RHodgett 17-Feb-11 7:54am    
Oliver, I am getting the error: "Cannot access nonstatic member of outer type 'DevelopTrackBar.SelectionRangeSlider' via nested type 'DevelopTrackBar.SelectionRangeSlider.SelectionRangeSlider'" -any ideas?

Thank you so much for writing the code for me, if I can get it to work I will be so grateful!
Olivier Levrey 17-Feb-11 9:37am    
Did you do nested class? That is: did you declare a class inside another class? Have a look to this link to learn about this error:
http://www.codeproject.com/KB/cs/nested_csclasses.aspx

I didn't write nested class in the code I copied, so you probably copied it by mistake inside another class. If you want to use nested classes, check that all the variables you access from a class are declared inside that class.
RHodgett 17-Feb-11 11:08am    
That solved it! Mate, that is awesome, thank you so much! I will modify your code slightly but it does exactly what I need it to!
Olivier Levrey 17-Feb-11 11:56am    
I'm glad I could help. ;)
Le@rner 13-Sep-17 0:41am    
Can u please provide me sample for this i also want trackbar with 3 thumb.
I don't quite understand why you need three TrackBars since if the slider is at one end it is set to Min the other end is Max and wherever the slider is is the value.

Still, if you are convinced then create a UserControl, put three Trackbars on it, surface whichever properties you want and Bob's your uncle.
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 16-Feb-11 13:50pm    
Basically, you're right, it all equivalent to just one slide, but, in principle, you can make it easy to move withing the currently allowed range, and harder to change the range "are your sure"?
So I understand to purpose, but yes, plain vanilla -- boring and redundant.
--SA
William Winner 16-Feb-11 17:22pm    
He doesn't say that he needs three trackbars...he says he wants one with three things to slide. Basically a range slider where you can also set a current value.
Espen Harlinn 16-Feb-11 17:42pm    
May not have been bulls eye, but decidedly not a 2 - hence my 5, something you deserved anyway :)
RHodgett 17-Feb-11 7:31am    
Hi Henry, I think there is a little confusion over my question, sorry! What I am after is 1 Trackbar with three thumbs to slide as William stated :)
Sergey Alexandrovich Kryukov 19-Feb-11 16:27pm    
I up-voted this answer. Someone voted "2" all around except one (accepted) answer.
I disagree with this vote, even though your answer has one confusion: you talk about 3 TrackBars, but the idea was 3 thumbs, which makes more sense as I mentioned in my comment above.
Nevertheless, your criticism against the idea also makes sense.
--SA
Absolutely (well, I would need exact spec); and this is not very hard to do.

However, think it is very easy to confuse the use with this kind of control, so if you want to do it, better make it very good, visually different from the regular TrackBar but nicely blending in style with other controls. So your major concern should be design; implementation here is way easier.

You only need to stop your search and simply concentrate on available help on System.Windows.Forms.Control class. Derive you control from this class, for rendering overload Control.OnPaint; always use System.Drawing.Graphics from the argument of OnPaint (please, never created Graphics — a common mistake!).

When you change anything, your changes in rendering will be triggered by Control.Invalidate; use one of the overloaded methods with parameters to invalidate only a part of control, is you want only a partial re-draw.

Apparently, override OnMouseDown, OnMouseUp, (optionally) OnMouseEnter, OnMouseLeave, OnMouseMove, OnMouseWheel, OnKeyUp, OnKeyDown. You also may need to override OnGotFocus, OnLostFocus to render focused state differently (a most for controls if Control.CanFocus == true).

—SA
 
Share this answer
 
Comments
Espen Harlinn 16-Feb-11 14:46pm    
Good answer, my 5
Sergey Alexandrovich Kryukov 16-Feb-11 15:31pm    
Thank you, hope it's doable. I agree with Henry's concerns though.
--SA
Espen Harlinn 16-Feb-11 15:41pm    
I think it makes good sense for for a minvalue - value - maxvalue ui element - he mentions three thumbs, not three trackbars as Henry seems to assume :)
Henry Minute 16-Feb-11 17:17pm    
True.

Although he also mentions finding controls with more than one 'slider' which he earlier equated to trackbars. So his message is a little mixed.
Espen Harlinn 16-Feb-11 17:35pm    
Seems this was unjustified: "Also seems like he’s an unappreciative fellow – gimme code, the complete code, and nothing but the code, or I’ll downvote kind of guy"
Somebody else found something wrong with the answers - a commnet would have been nice ...
This has more than you actually need:
Custom C# Gradient Editor in Windows.Forms[^].

Since you can add more gradient stops - which are similar to the trackbars - you should be able to reuse the logic of this control to achieve what you need.

Regards
Espen Harlinn
 
Share this answer
 
Comments
RHodgett 17-Feb-11 7:34am    
Espen, I have downloaded the project and had a play. The added gradient stops is just what I need (with 3 permanent ones). I will look into modifying this if I cannot get my own control to work.
Thank you!
Sergey Alexandrovich Kryukov 18-Feb-11 19:15pm    
Oh, I got your point.
Someone voted "2". I disagree, this must be a manifestation of laziness.
My vote's 5.
--SA
Espen Harlinn 19-Feb-11 9:52am    
Thank you SAKryukov!
Sergey Alexandrovich Kryukov 19-Feb-11 16:21pm    
By the way, somebody voted "2" all around.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900