Share via


How to make a double trackbar control in VB 2010

Question

Monday, February 9, 2015 5:12 PM

I'm suing VB 2010 Express and I need to create a trackbar control with double trackbars.

I can't find "Doubletrackbar" control from Toolbox. I searched online and found one post toking about it (http://www.vbforums.com/showthread.php?620394-WIP-Double-TrackBar). Unfortunately, I can't use the code in the post.   Anyone can help?

Thank you very much.

All replies (29)

Wednesday, February 11, 2015 12:01 AM ✅Answered | 1 vote

Hi,

 Well, unfortunately you are going to have to leave the Minimum and the ValueLeft set to 0 and build it that way. Then after you build it and add one to the form you can set the ValueLeft to the value you want in the Properties on the [Design] tab, i set it to 80 in the example below. Do Not change the Minimum property from 0 or it screws up and does not draw the thumb buttons correctly.

 You will have to keep the range from 0 to 300 and just do the calculations you want in the forms code by adding 100 to each of the ValueLeft and ValueRight properties and then divide that by 100. That will give you the values 1.00 to 4.00. Below is the code i used in the form to calculate the values and display them.

 Like i said before, this control was poorly designed and was never completed. Unless it is pretty much completely rebuilt, it is just not going to have very reliable results or predictable behavior.

Public Class Form1
    Private Lval As Double
    Private Rval As Double

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Lval = (DoubleTrackBar1.ValueLeft + 100) / 100
        Rval = (DoubleTrackBar1.ValueRight + 100) / 100
        Label1.Text = "Left = " & Lval.ToString
        Label2.Text = "Right = " & Rval.ToString
    End Sub

    Private Sub DoubleTrackBar1_LeftValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DoubleTrackBar1.LeftValueChanged
        Lval = (DoubleTrackBar1.ValueLeft + 100) / 100
        Label1.Text = "Left = " & Lval.ToString
    End Sub

    Private Sub DoubleTrackBar1_RightValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DoubleTrackBar1.RightValueChanged
        Rval = (DoubleTrackBar1.ValueRight + 100) / 100
        Label2.Text = "Right = " & Rval.ToString
    End Sub
End Class

 

If you say it can`t be done then i`ll try it


Wednesday, February 11, 2015 12:30 AM ✅Answered

Thank you very much, IronRazerz. 

I'll do as you suggested.


Monday, February 9, 2015 6:51 PM

Suing or using?

La vida loca


Monday, February 9, 2015 7:09 PM

So, what is the problem you are having with using the control from that link? Is there something it does not do that you need to do?

If you say it can`t be done then i`ll try it


Monday, February 9, 2015 7:22 PM

I'm suing VB 2010 Express and I need to create a trackbar control with double trackbars.

I can't find "Doubletrackbar" control from Toolbox. I searched online and found one post toking about it (http://www.vbforums.com/showthread.php?620394-WIP-Double-TrackBar). Unfortunately, I can't use the code in the post.   Anyone can help?

Thank you very much.

I think you mean a "Range Control", not a doubletrackbar:

If you can't work one for yourself or from something you find, you might want to consider third-party stuff like the one I showed which is from DevExpress.

Still lost in code, just at a little higher level.

:-)


Monday, February 9, 2015 10:14 PM

sorry, it's "using".


Monday, February 9, 2015 10:25 PM

I downloaded the code from the link but I can't get it to run in Visual Basic Express 2010. It creates many errors when I compile it. I don't know how to link it to the form. 

I can only find "Trackbar" from Toobox. I can't find anything like "Double Trackbar".

Below is the code from the link.

Public Class DoubleTrackBar
    Inherits Control

    Public Sub New()
        Me.DoubleBuffered = True
        Me.SetDefaults()
    End Sub

    Private Sub SetDefaults()
        Me.Orientation = Windows.Forms.Orientation.Horizontal
        Me.SmallChange = 1
        Me.Maximum = 10
        Me.Minimum = 0
        Me.ValueLeft = 0
        Me.ValueRight = 7
    End Sub

#Region " Private Fields "

    Private leftThumbState As VisualStyles.TrackBarThumbState
    Private rightThumbState As VisualStyles.TrackBarThumbState

    Private draggingLeft, draggingRight As Boolean
#End Region

#Region " Enums "

    Public Enum Thumbs
        None = 0
        Left = 1
        Right = 2
    End Enum

#End Region

#Region " Properties "

    Private _SelectedThumb As Thumbs
    ''' <summary>
    ''' Gets the thumb that had focus last.
    ''' </summary>
    ''' <returns>The thumb that had focus last.</returns>
    <Description("The thumb that had focus last.")> _
    Public Property SelectedThumb() As Thumbs
        Get
            Return _SelectedThumb
        End Get
        Private Set(value As Thumbs)
            _SelectedThumb = value
        End Set
    End Property
    
    Private _ValueLeft As Integer
    ''' <summary>
    ''' Gets or sets the position of the left slider.
    ''' </summary>
    ''' <returns>The position of the left slider.</returns>
    <Description("The position of the left slider.")> _
    Public Property ValueLeft() As Integer
        Get
            Return _ValueLeft
        End Get
        Set(ByVal value As Integer)
            If value < Me.Minimum OrElse value > Me.Maximum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueLeft'. 'ValueLeft' should be between 'Minimum' and 'Maximum'.", value.ToString()), "ValueLeft")
            End If
            If value > Me.ValueRight Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueLeft'. 'ValueLeft' should be less than or equal to 'ValueRight'.", value.ToString()), "ValueLeft")
            End If
            _ValueLeft = value

            Me.OnValueChanged(EventArgs.Empty)
            Me.OnLeftValueChanged(EventArgs.Empty)

            Me.Invalidate()
        End Set
    End Property

    Private _ValueRight As Integer
    ''' <summary>
    ''' Gets or sets the position of the right slider.
    ''' </summary>
    ''' <returns>The position of the right slider.</returns>
    <Description("The position of the right slider.")> _
    Public Property ValueRight() As Integer
        Get
            Return _ValueRight
        End Get
        Set(ByVal value As Integer)
            If value < Me.Minimum OrElse value > Me.Maximum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueRight'. 'ValueRight' should be between 'Minimum' and 'Maximum'.", value.ToString()), "ValueRight")
            End If
            If value < Me.ValueLeft Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueRight'. 'ValueRight' should be greater than or equal to 'ValueLeft'.", value.ToString()), "ValueLeft")
            End If
            _ValueRight = value

            Me.OnValueChanged(EventArgs.Empty)
            Me.OnRightValueChanged(EventArgs.Empty)

            Me.Invalidate()
        End Set
    End Property

    Private _Minimum As Integer
    ''' <summary>
    ''' Gets or sets the minimum value.
    ''' </summary>
    ''' <returns>The minimum value.</returns>
    <Description("The minimum value.")> _
    Public Property Minimum() As Integer
        Get
            Return _Minimum
        End Get
        Set(ByVal value As Integer)
            If value >= Me.Maximum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'Minimum'. 'Minimum' should be less than 'Maximum'.", value.ToString()), "Minimum")
            End If
            _Minimum = value
            Me.Invalidate()
        End Set
    End Property

    Private _Maximum As Integer
    ''' <summary>
    ''' Gets or sets the maximum value.
    ''' </summary>
    ''' <returns>The maximum value.</returns>
    <Description("The maximum value.")> _
    Public Property Maximum() As Integer
        Get
            Return _Maximum
        End Get
        Set(ByVal value As Integer)
            If value <= Me.Minimum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'Maximum'. 'Maximum' should be greater than 'Minimum'.", value.ToString()), "Maximum")
            End If
            _Maximum = value
            Me.Invalidate()
        End Set
    End Property

    Private _Orientation As Orientation
    ''' <summary>
    ''' Gets or sets the orientation of the control.
    ''' </summary>
    ''' <returns>The orientation of the control.</returns>
    <Description("The orientation of the control.")> _
    Public Property Orientation() As Orientation
        Get
            Return _Orientation
        End Get
        Set(ByVal value As Orientation)
            _Orientation = value
        End Set
    End Property

    Private _SmallChange As Integer
    ''' <summary>
    ''' Gets or sets the amount of positions the closest slider moves when the control is clicked.
    ''' </summary>
    ''' <returns>The amount of positions the closest slider moves when the control is clicked.</returns>
    <Description("The amount of positions the closest slider moves when the control is clicked.")> _
    Public Property SmallChange() As Integer
        Get
            Return _SmallChange
        End Get
        Set(ByVal value As Integer)
            _SmallChange = value
        End Set
    End Property

    Private ReadOnly Property RelativeValueLeft As Double
        Get
            Dim diff = Me.Maximum - Me.Minimum
            Return If(diff = 0, Me.ValueLeft, Me.ValueLeft / diff)
        End Get
    End Property

    Private ReadOnly Property RelativeValueRight As Double
        Get
            Dim diff = Me.Maximum - Me.Minimum
            Return If(diff = 0, Me.ValueLeft, Me.ValueRight / diff)
        End Get
    End Property

#End Region

#Region " Methods "

    Public Sub IncrementLeft()
        Dim newValue = Math.Min(Me.ValueLeft + 1, Me.Maximum)
        If Me.IsValidValueLeft(newValue) Then
            Me.ValueLeft = newValue
        End If
        Me.Invalidate()
    End Sub

    Public Sub IncrementRight()
        Dim newValue = Math.Min(Me.ValueRight + 1, Me.Maximum)
        If Me.IsValidValueRight(newValue) Then
            Me.ValueRight = newValue
        End If
        Me.Invalidate()
    End Sub

    Public Sub DecrementLeft()
        Dim newValue = Math.Max(Me.ValueLeft - 1, Me.Minimum)
        If Me.IsValidValueLeft(newValue) Then
            Me.ValueLeft = newValue
        End If
        Me.Invalidate()
    End Sub

    Public Sub DecrementRight()
        Dim newValue = Math.Max(Me.ValueRight - 1, Me.Minimum)
        If Me.IsValidValueRight(newValue) Then
            Me.ValueRight = newValue
        End If
        Me.Invalidate()
    End Sub

    Private Function IsValidValueLeft(ByVal value As Integer) As Boolean
        Return (value >= Me.Minimum AndAlso value <= Me.Maximum AndAlso value < Me.ValueRight)
    End Function

    Private Function IsValidValueRight(ByVal value As Integer) As Boolean
        Return (value >= Me.Minimum AndAlso value <= Me.Maximum AndAlso value > Me.ValueLeft)
    End Function

    Private Function GetLeftThumbRectangle(Optional ByVal g As Graphics = Nothing) As Rectangle
        Dim shouldDispose = (g Is Nothing)
        If shouldDispose Then g = Me.CreateGraphics()

        Dim rect = Me.GetThumbRectangle(Me.RelativeValueLeft, g)
        If shouldDispose Then g.Dispose()

        Return rect
    End Function

    Private Function GetRightThumbRectangle(Optional ByVal g As Graphics = Nothing) As Rectangle
        Dim shouldDispose = (g Is Nothing)
        If shouldDispose Then g = Me.CreateGraphics()

        Dim rect = Me.GetThumbRectangle(Me.RelativeValueRight, g)
        If shouldDispose Then g.Dispose()

        Return rect
    End Function

    Private Function GetThumbRectangle(ByVal relativeValue As Double, ByVal g As Graphics) As Rectangle
        Dim size = TrackBarRenderer.GetBottomPointingThumbSize(g, VisualStyles.TrackBarThumbState.Normal)
        Dim border = CInt(size.Width / 2)
        Dim w = Me.GetTrackRectangle(border).Width
        Dim x = CInt(Math.Abs(Me.Minimum) / (Me.Maximum - Me.Minimum) * w + relativeValue * w)
        
        Dim y = CInt((Me.Height - size.Height) / 2)
        Return New Rectangle(New Point(x, y), size)
    End Function

    Private Function GetTrackRectangle(ByVal border As Integer) As Rectangle
        'TODO: Select Case for hor/ver
        Return New Rectangle(border, CInt(Me.Height / 2) - 3, Me.Width - 2 * border - 1, 4)
    End Function

    Private Function GetClosestSlider(ByVal point As Point) As Thumbs
        Dim leftThumbRect = Me.GetLeftThumbRectangle()
        Dim rightThumbRect = Me.GetRightThumbRectangle()
        If Me.Orientation = Windows.Forms.Orientation.Horizontal Then
            If Math.Abs(leftThumbRect.X - point.X) > Math.Abs(rightThumbRect.X - point.X) _
            AndAlso Math.Abs(leftThumbRect.Right - point.X) > Math.Abs(rightThumbRect.Right - point.X) Then
                Return Thumbs.Right
            Else
                Return Thumbs.Left
            End If
        Else
            If Math.Abs(leftThumbRect.Y - point.Y) > Math.Abs(rightThumbRect.Y - point.Y) _
            AndAlso Math.Abs(leftThumbRect.Bottom - point.Y) > Math.Abs(rightThumbRect.Bottom - point.Y) Then
                Return Thumbs.Right
            Else
                Return Thumbs.Left
            End If
        End If
    End Function

    Private Sub SetThumbState(ByVal location As Point, ByVal newState As VisualStyles.TrackBarThumbState)
        Dim leftThumbRect = Me.GetLeftThumbRectangle()
        Dim rightThumbRect = Me.GetRightThumbRectangle()

        If leftThumbRect.Contains(location) Then
            leftThumbState = newState
        Else
            If Me.SelectedThumb = Thumbs.Left Then
                leftThumbState = VisualStyles.TrackBarThumbState.Hot
            Else
                leftThumbState = VisualStyles.TrackBarThumbState.Normal
            End If
        End If

        If rightThumbRect.Contains(location) Then
            rightThumbState = newState
        Else
            If Me.SelectedThumb = Thumbs.Right Then
                rightThumbState = VisualStyles.TrackBarThumbState.Hot
            Else
                rightThumbState = VisualStyles.TrackBarThumbState.Normal
            End If
        End If
    End Sub

    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseMove(e)
        Me.SetThumbState(e.Location, VisualStyles.TrackBarThumbState.Hot)

        Dim offset = CInt(e.Location.X / (Me.Width) * (Me.Maximum - Me.Minimum))
        Dim newValue = Me.Minimum + offset
        If draggingLeft Then
            If Me.IsValidValueLeft(newValue) Then Me.ValueLeft = newValue
        ElseIf draggingRight Then
            If Me.IsValidValueRight(newValue) Then Me.ValueRight = newValue
        End If

        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseDown(e)
        Me.Focus()
        Me.SetThumbState(e.Location, VisualStyles.TrackBarThumbState.Pressed)

        draggingLeft = (leftThumbState = VisualStyles.TrackBarThumbState.Pressed)
        If Not draggingLeft Then draggingRight = (rightThumbState = VisualStyles.TrackBarThumbState.Pressed)

        If draggingLeft Then
            Me.SelectedThumb = Thumbs.Left
        ElseIf draggingRight Then
            Me.SelectedThumb = Thumbs.Right
        End If

        If Not draggingLeft AndAlso Not draggingRight Then
            If Me.GetClosestSlider(e.Location) = 1 Then
                If e.X < Me.GetLeftThumbRectangle().X Then
                    Me.DecrementLeft()
                Else
                    Me.IncrementLeft()
                End If
                Me.SelectedThumb = Thumbs.Left
            Else
                If e.X < Me.GetRightThumbRectangle().X Then
                    Me.DecrementRight()
                Else
                    Me.IncrementRight()
                End If
                Me.SelectedThumb = Thumbs.Right
            End If
        End If

        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseUp(e)
        draggingLeft = False
        draggingRight = False
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseWheel(e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseWheel(e)

        If e.Delta = 0 Then Return

        If Me.SelectedThumb = 1 Then
            If e.Delta > 0 Then
                Me.IncrementLeft()
            Else
                Me.DecrementLeft()
            End If
        ElseIf Me.SelectedThumb = 2 Then
            If e.Delta > 0 Then
                Me.IncrementRight()
            Else
                Me.DecrementRight()
            End If
        End If
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Dim thumbSize = Me.GetThumbRectangle(0, e.Graphics).Size
        Dim trackRect = Me.GetTrackRectangle(CInt(thumbSize.Width / 2))
        Dim ticksRect = trackRect : ticksRect.Offset(0, 15)

        TrackBarRenderer.DrawVerticalTrack(e.Graphics, trackRect)
        TrackBarRenderer.DrawHorizontalTicks(e.Graphics, ticksRect, Me.Maximum - Me.Minimum + 1, VisualStyles.EdgeStyle.Etched)
        TrackBarRenderer.DrawBottomPointingThumb(e.Graphics, Me.GetLeftThumbRectangle(e.Graphics), leftThumbState)
        TrackBarRenderer.DrawBottomPointingThumb(e.Graphics, Me.GetRightThumbRectangle(e.Graphics), rightThumbState)
    End Sub

#End Region

#Region " Events "

    Public Event ValueChanged As EventHandler
    Public Event LeftValueChanged As EventHandler
    Public Event RightValueChanged As EventHandler

    Protected Overridable Sub OnValueChanged(e As EventArgs)
        RaiseEvent ValueChanged(Me, e)
    End Sub

    Protected Overridable Sub OnLeftValueChanged(e As EventArgs)
        RaiseEvent LeftValueChanged(Me, e)
    End Sub

    Protected Overridable Sub OnRightValueChanged(e As EventArgs)
        RaiseEvent RightValueChanged(Me, e)
    End Sub

#End Region

End Class

Monday, February 9, 2015 10:54 PM | 2 votes

Hi,

 Whoever made it did not have Option Strict turned on and there was also a namespace that was not imported. I just went through the code and fixed all the errors and added the namespace. It seems to be working after that.

 Open your  project or even a new form project just to test it on. When the project is opened go to the VB Menu and click (Project) and then select (Add Class). You can leave it named as the default name "Class1.vb" if you want. When the empty class code opens, copy and paste the fixed code below in replacing the 2 lines of code that are generated automatically. Save the project.

 Then go to the VB Menu and click (Build) and select (Build YourProjectsName). After it is finished building you can go to the Forms [Design] tab and you will find the DoubleTrackBar control in the top of your toolbox.   8)

Imports System.ComponentModel

Public Class DoubleTrackBar
    Inherits Control

    Public Sub New()
        Me.DoubleBuffered = True
        Me.SetDefaults()
    End Sub

    Private Sub SetDefaults()
        'Added these to set to a decent size when a new one is added to the form
        Me.Width = 200
        Me.Height = 50

        Me.Orientation = Orientation.Horizontal
        Me.SmallChange = 1
        Me.Maximum = 400
        Me.Minimum = 0
        Me.ValueLeft = 0
        Me.ValueRight = 300
    End Sub

#Region " Private Fields "

    Private leftThumbState As VisualStyles.TrackBarThumbState
    Private rightThumbState As VisualStyles.TrackBarThumbState

    Private draggingLeft, draggingRight As Boolean
#End Region

#Region " Enums "

    Public Enum Thumbs
        None = 0
        Left = 1
        Right = 2
    End Enum

#End Region

#Region " Properties "

    Private _SelectedThumb As Thumbs
    ''' <summary>
    ''' Gets the thumb that had focus last.
    ''' </summary>
    ''' <returns>The thumb that had focus last.</returns>
    <Description("The thumb that had focus last.")> _
    Public Property SelectedThumb() As Thumbs
        Get
            Return _SelectedThumb
        End Get
        Private Set(ByVal value As Thumbs)
            _SelectedThumb = value
        End Set
    End Property

    Private _ValueLeft As Integer
    ''' <summary>
    ''' Gets or sets the position of the left slider.
    ''' </summary>
    ''' <returns>The position of the left slider.</returns>
    <Description("The position of the left slider.")> _
    Public Property ValueLeft() As Integer
        Get
            Return _ValueLeft
        End Get
        Set(ByVal value As Integer)
            If value < Me.Minimum OrElse value > Me.Maximum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueLeft'. 'ValueLeft' should be between 'Minimum' and 'Maximum'.", value.ToString()), "ValueLeft")
            End If
            If value > Me.ValueRight Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueLeft'. 'ValueLeft' should be less than or equal to 'ValueRight'.", value.ToString()), "ValueLeft")
            End If
            _ValueLeft = value

            Me.OnValueChanged(EventArgs.Empty)
            Me.OnLeftValueChanged(EventArgs.Empty)

            Me.Invalidate()
        End Set
    End Property

    Private _ValueRight As Integer
    ''' <summary>
    ''' Gets or sets the position of the right slider.
    ''' </summary>
    ''' <returns>The position of the right slider.</returns>
    <Description("The position of the right slider.")> _
    Public Property ValueRight() As Integer
        Get
            Return _ValueRight
        End Get
        Set(ByVal value As Integer)
            If value < Me.Minimum OrElse value > Me.Maximum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueRight'. 'ValueRight' should be between 'Minimum' and 'Maximum'.", value.ToString()), "ValueRight")
            End If
            If value < Me.ValueLeft Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'ValueRight'. 'ValueRight' should be greater than or equal to 'ValueLeft'.", value.ToString()), "ValueLeft")
            End If
            _ValueRight = value

            Me.OnValueChanged(EventArgs.Empty)
            Me.OnRightValueChanged(EventArgs.Empty)

            Me.Invalidate()
        End Set
    End Property

    Private _Minimum As Integer
    ''' <summary>
    ''' Gets or sets the minimum value.
    ''' </summary>
    ''' <returns>The minimum value.</returns>
    <Description("The minimum value.")> _
    Public Property Minimum() As Integer
        Get
            Return _Minimum
        End Get
        Set(ByVal value As Integer)
            If value >= Me.Maximum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'Minimum'. 'Minimum' should be less than 'Maximum'.", value.ToString()), "Minimum")
            End If
            _Minimum = value
            Me.Invalidate()
        End Set
    End Property

    Private _Maximum As Integer
    ''' <summary>
    ''' Gets or sets the maximum value.
    ''' </summary>
    ''' <returns>The maximum value.</returns>
    <Description("The maximum value.")> _
    Public Property Maximum() As Integer
        Get
            Return _Maximum
        End Get
        Set(ByVal value As Integer)
            If value <= Me.Minimum Then
                Throw New ArgumentException(String.Format("Value of '{0}' is not valid for 'Maximum'. 'Maximum' should be greater than 'Minimum'.", value.ToString()), "Maximum")
            End If
            _Maximum = value
            Me.Invalidate()
        End Set
    End Property

    Private _Orientation As Orientation
    ''' <summary>
    ''' Gets or sets the orientation of the control.
    ''' </summary>
    ''' <returns>The orientation of the control.</returns>
    <Description("The orientation of the control.")> _
    Public Property Orientation() As Orientation
        Get
            Return _Orientation
        End Get
        Set(ByVal value As Orientation)
            _Orientation = value
        End Set
    End Property

    Private _SmallChange As Integer
    ''' <summary>
    ''' Gets or sets the amount of positions the closest slider moves when the control is clicked.
    ''' </summary>
    ''' <returns>The amount of positions the closest slider moves when the control is clicked.</returns>
    <Description("The amount of positions the closest slider moves when the control is clicked.")> _
    Public Property SmallChange() As Integer
        Get
            Return _SmallChange
        End Get
        Set(ByVal value As Integer)
            _SmallChange = value
        End Set
    End Property

    Private ReadOnly Property RelativeValueLeft() As Double
        Get
            Dim diff As Double = Me.Maximum - Me.Minimum
            Return If(diff = 0, Me.ValueLeft, Me.ValueLeft / diff)
        End Get
    End Property

    Private ReadOnly Property RelativeValueRight() As Double
        Get
            Dim diff As Double = Me.Maximum - Me.Minimum
            Return If(diff = 0, Me.ValueLeft, Me.ValueRight / diff)
        End Get
    End Property

#End Region

#Region " Methods "

    Public Sub IncrementLeft()
        Dim newValue As Integer = Math.Min(Me.ValueLeft + 1, Me.Maximum)
        If Me.IsValidValueLeft(newValue) Then
            Me.ValueLeft = newValue
        End If
        Me.Invalidate()
    End Sub

    Public Sub IncrementRight()
        Dim newValue As Integer = Math.Min(Me.ValueRight + 1, Me.Maximum)
        If Me.IsValidValueRight(newValue) Then
            Me.ValueRight = newValue
        End If
        Me.Invalidate()
    End Sub

    Public Sub DecrementLeft()
        Dim newValue As Integer = Math.Max(Me.ValueLeft - 1, Me.Minimum)
        If Me.IsValidValueLeft(newValue) Then
            Me.ValueLeft = newValue
        End If
        Me.Invalidate()
    End Sub

    Public Sub DecrementRight()
        Dim newValue As Integer = Math.Max(Me.ValueRight - 1, Me.Minimum)
        If Me.IsValidValueRight(newValue) Then
            Me.ValueRight = newValue
        End If
        Me.Invalidate()
    End Sub

    Private Function IsValidValueLeft(ByVal value As Integer) As Boolean
        Return (value >= Me.Minimum AndAlso value <= Me.Maximum AndAlso value < Me.ValueRight)
    End Function

    Private Function IsValidValueRight(ByVal value As Integer) As Boolean
        Return (value >= Me.Minimum AndAlso value <= Me.Maximum AndAlso value > Me.ValueLeft)
    End Function

    Private Function GetLeftThumbRectangle(Optional ByVal g As Graphics = Nothing) As Rectangle
        Dim shouldDispose As Boolean = (g Is Nothing)
        If shouldDispose Then g = Me.CreateGraphics()

        Dim rect As Rectangle = Me.GetThumbRectangle(Me.RelativeValueLeft, g)
        If shouldDispose Then g.Dispose()

        Return rect
    End Function

    Private Function GetRightThumbRectangle(Optional ByVal g As Graphics = Nothing) As Rectangle
        Dim shouldDispose As Boolean = (g Is Nothing)
        If shouldDispose Then g = Me.CreateGraphics()

        Dim rect As Rectangle = Me.GetThumbRectangle(Me.RelativeValueRight, g)
        If shouldDispose Then g.Dispose()

        Return rect
    End Function

    Private Function GetThumbRectangle(ByVal relativeValue As Double, ByVal g As Graphics) As Rectangle
        Dim size As Size = TrackBarRenderer.GetBottomPointingThumbSize(g, VisualStyles.TrackBarThumbState.Normal)
        Dim border As Integer = CInt(size.Width / 2)
        Dim w As Integer = Me.GetTrackRectangle(border).Width
        Dim x As Integer = CInt(Math.Abs(Me.Minimum) / (Me.Maximum - Me.Minimum) * w + relativeValue * w)

        Dim y As Integer = CInt((Me.Height - size.Height) / 2)
        Return New Rectangle(New Point(x, y), size)
    End Function

    Private Function GetTrackRectangle(ByVal border As Integer) As Rectangle
        'TODO: Select Case for hor/ver
        Return New Rectangle(border, CInt(Me.Height / 2) - 3, Me.Width - 2 * border - 1, 4)
    End Function

    Private Function GetClosestSlider(ByVal point As Point) As Thumbs
        Dim leftThumbRect As Rectangle = Me.GetLeftThumbRectangle()
        Dim rightThumbRect As Rectangle = Me.GetRightThumbRectangle()
        If Me.Orientation = Windows.Forms.Orientation.Horizontal Then
            If Math.Abs(leftThumbRect.X - point.X) > Math.Abs(rightThumbRect.X - point.X) _
            AndAlso Math.Abs(leftThumbRect.Right - point.X) > Math.Abs(rightThumbRect.Right - point.X) Then
                Return Thumbs.Right
            Else
                Return Thumbs.Left
            End If
        Else
            If Math.Abs(leftThumbRect.Y - point.Y) > Math.Abs(rightThumbRect.Y - point.Y) _
            AndAlso Math.Abs(leftThumbRect.Bottom - point.Y) > Math.Abs(rightThumbRect.Bottom - point.Y) Then
                Return Thumbs.Right
            Else
                Return Thumbs.Left
            End If
        End If
    End Function

    Private Sub SetThumbState(ByVal location As Point, ByVal newState As VisualStyles.TrackBarThumbState)
        Dim leftThumbRect As Rectangle = Me.GetLeftThumbRectangle()
        Dim rightThumbRect As Rectangle = Me.GetRightThumbRectangle()

        If leftThumbRect.Contains(location) Then
            leftThumbState = newState
        Else
            If Me.SelectedThumb = Thumbs.Left Then
                leftThumbState = VisualStyles.TrackBarThumbState.Hot
            Else
                leftThumbState = VisualStyles.TrackBarThumbState.Normal
            End If
        End If

        If rightThumbRect.Contains(location) Then
            rightThumbState = newState
        Else
            If Me.SelectedThumb = Thumbs.Right Then
                rightThumbState = VisualStyles.TrackBarThumbState.Hot
            Else
                rightThumbState = VisualStyles.TrackBarThumbState.Normal
            End If
        End If
    End Sub

    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseMove(e)
        Me.SetThumbState(e.Location, VisualStyles.TrackBarThumbState.Hot)

        Dim offset As Integer = CInt(e.Location.X / (Me.Width) * (Me.Maximum - Me.Minimum))
        Dim newValue As Integer = Me.Minimum + offset
        If draggingLeft Then
            If Me.IsValidValueLeft(newValue) Then Me.ValueLeft = newValue
        ElseIf draggingRight Then
            If Me.IsValidValueRight(newValue) Then Me.ValueRight = newValue
        End If

        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseDown(e)
        Me.Focus()
        Me.SetThumbState(e.Location, VisualStyles.TrackBarThumbState.Pressed)

        draggingLeft = (leftThumbState = VisualStyles.TrackBarThumbState.Pressed)
        If Not draggingLeft Then draggingRight = (rightThumbState = VisualStyles.TrackBarThumbState.Pressed)

        If draggingLeft Then
            Me.SelectedThumb = Thumbs.Left
        ElseIf draggingRight Then
            Me.SelectedThumb = Thumbs.Right
        End If

        If Not draggingLeft AndAlso Not draggingRight Then
            If Me.GetClosestSlider(e.Location) = 1 Then
                If e.X < Me.GetLeftThumbRectangle().X Then
                    Me.DecrementLeft()
                Else
                    Me.IncrementLeft()
                End If
                Me.SelectedThumb = Thumbs.Left
            Else
                If e.X < Me.GetRightThumbRectangle().X Then
                    Me.DecrementRight()
                Else
                    Me.IncrementRight()
                End If
                Me.SelectedThumb = Thumbs.Right
            End If
        End If

        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseUp(e)
        draggingLeft = False
        draggingRight = False
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseWheel(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseWheel(e)

        If e.Delta = 0 Then Return

        If Me.SelectedThumb = 1 Then
            If e.Delta > 0 Then
                Me.IncrementLeft()
            Else
                Me.DecrementLeft()
            End If
        ElseIf Me.SelectedThumb = 2 Then
            If e.Delta > 0 Then
                Me.IncrementRight()
            Else
                Me.DecrementRight()
            End If
        End If
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Dim thumbSize As Size = Me.GetThumbRectangle(0, e.Graphics).Size
        Dim trackRect As Rectangle = Me.GetTrackRectangle(CInt(thumbSize.Width / 2))
        Dim ticksRect As Rectangle = trackRect
        ticksRect.Offset(0, -15) 'changed to -15 to place ticks at the top

        'added this to keep ticks at a decent spacing
        Dim tickspacing As Integer = CInt((Me.Maximum - Me.Minimum) / 10) + 1

        TrackBarRenderer.DrawHorizontalTrack(e.Graphics, trackRect)
        TrackBarRenderer.DrawHorizontalTicks(e.Graphics, ticksRect, tickspacing, VisualStyles.EdgeStyle.Etched)

        'Changed these to draw the top pointing thumb button
        TrackBarRenderer.DrawTopPointingThumb(e.Graphics, Me.GetLeftThumbRectangle(e.Graphics), leftThumbState)
        TrackBarRenderer.DrawTopPointingThumb(e.Graphics, Me.GetRightThumbRectangle(e.Graphics), rightThumbState)
    End Sub

#End Region

#Region " Events "

    Public Event ValueChanged As EventHandler
    Public Event LeftValueChanged As EventHandler
    Public Event RightValueChanged As EventHandler

    Protected Overridable Sub OnValueChanged(ByVal e As EventArgs)
        RaiseEvent ValueChanged(Me, e)
    End Sub

    Protected Overridable Sub OnLeftValueChanged(ByVal e As EventArgs)
        RaiseEvent LeftValueChanged(Me, e)
    End Sub

    Protected Overridable Sub OnRightValueChanged(ByVal e As EventArgs)
        RaiseEvent RightValueChanged(Me, e)
    End Sub

#End Region

End Class

If you say it can`t be done then i`ll try it


Tuesday, February 10, 2015 12:12 AM

Thank you so much. It works now. I do have some more problems. 1. when I add the DoubleTrackBar to form, the code generated in Form1.Designer.vb shows as Me.DoubleTrackBar1 = New DoubleTrackBar.DoubleTrackBar() Then error msg shows as Type 'DoubleTrackBar.DoubleTrackBar' is not defined. when I change it to Me.DoubleTrackBar1 = New DoubleTrackBar() The compile goes through. However, when I another control, say Textbox, it changed to Me.DoubleTrackBar1 = New DoubleTrackBar.DoubleTrackBar() again in Form1.Designer.vb. 2.I want to add "Tick" on it. I can't find the option. How should I do it? Thank you very much. Fred


Tuesday, February 10, 2015 1:23 AM

Hi,

 Did you name your project "DoubleTrackBar" ? In the Form.Designer.vb file it should be in this format.

testing_doubletrackbar__msdn_ is the Namespace of my application.

DoubleTrackBar is the name of the DoubleTrackBar class.

Me.DoubleTrackBar1 = New testing_doubletrackbar__msdn_.DoubleTrackBar

 

 Look in your application`s properties and see if DoubleTrackBar is your applications namespace name. If it is then maybe try it in another test application and name the project something other than DoubleTrackbar Naming the project the same as the class does not seem like it would mess it up though.

 Also, at the end of the Form.Designer.vb file it should also be declaring the new instance of it like this. Of coarse your namespace will be names different than mine though.

Friend WithEvents DoubleTrackBar1 As testing_doubletrackbar__msdn_.DoubleTrackBar

 

 I am using VS2008 targeting .Net 3.5 so, maybe someone that has a newer VS version targeting .Net 4.0 or 4.5 will test it and see if they are having these problems. It seems to working fine on my end.

 To show the Ticks you will need to resize the height of the DoubleTrackBar. They are there but, not showing until you make the height taller.

If you say it can`t be done then i`ll try it


Tuesday, February 10, 2015 3:08 AM | 1 vote

Works fine in Visual Studio 2015 Preview compiled to both .Net 4.0 and 4.6 preview (4.5.3) except in 4.6 preview the lines with this

Windows.Forms.Orientation.Horizontal

had to be changed to just this for some reason

Orientation.Horizontal

La vida loca


Tuesday, February 10, 2015 3:31 AM | 1 vote

@ Mr. Monkeyboy,

 Thanks for testing it. I was not sure if it was my system or not. There`s another one to add to the collection.   8)

 I started making a new class before OP replied back that i am hoping to maybe incorporate all the functions of a TrackBar into plus a few others needed for the 2nd thumb. I have the basic operations working and looking like a standard Trackbar right now but, have quite a ways to go on it still.

 

If you say it can`t be done then i`ll try it


Tuesday, February 10, 2015 4:18 AM

@ Mr. Monkeyboy,

 Thanks for testing it. I was not sure if it was my system or not. There`s another one to add to the collection.   8)

 I started making a new class before OP replied back that i am hoping to maybe incorporate all the functions of a TrackBar into plus a few others needed for the 2nd thumb. I have the basic operations working and looking like a standard Trackbar right now but, have quite a ways to go on it still.

 

If you say it can`t be done then i`ll try it

Awesome. I set that TrackBar to vertical but it didn't work like the original one will do. Plus the original one can be made to show tick marks in different ways. When you get done I'll test it in VS 2015 Preview if you'd like.

La vida loca


Tuesday, February 10, 2015 4:58 PM

Thank you very much.

Yes, it works after my created a new project named "TestDoubleTrackBar". It seems that there is some problem if the project name is the same as class name.

I do have some new issues. What I want to do is to set the range to (100, 400). I then divide the left and right values by 100 to get decimal value between 1.00 and 4.00. It works when I change

    Private Sub SetDefaults()
        '        Me.Orientation = Windows.Forms.Orientation.Horizontal
        Me.Orientation = Orientation.Horizontal
        Me.SmallChange = 1
        Me.Maximum = 400
        Me.Minimum = 0
        Me.ValueLeft = 0
        Me.ValueRight = 300
    End Sub

and then set the properties of DoubleTrackBar1 to

Maximum: 400

Minimum: 0

ValueLeft: 100 

ValueRight: 300

It crashed when I set the Minimum to 100.

Please help!

Thank you.

Fred


Tuesday, February 10, 2015 5:01 PM

@Mr. Monkeyboy

I still can't get the ticks. I've changed

    Private Sub SetDefaults()
        '        Me.Orientation = Windows.Forms.Orientation.Horizontal
        Me.Orientation = Orientation.Horizontal
        Me.SmallChange = 1
        Me.Maximum = 400
        Me.Minimum = 0
        Me.ValueLeft = 0
        Me.ValueRight = 300
    End Sub

Which property is for display the ticks?

Thank you very much.

Fred

 


Tuesday, February 10, 2015 6:27 PM

Hi,

 There should be Ticks if you adjust the height of the control to be bigger. I did not see them ether until i made its height larger. As far as adjusting the Min Max values i did not really look through the code but, it is probably throwing off some calculations in the code somewhere that are causing the error. It seems to be designed to not take a lot of these things into account. 

 This control seems to be very limited in its functionality and will probably require some reworking to make it function half way decent. I would recommend maybe trying one of the Range controls from the links below. They are written in C# and i did not try them out but, i would imagine you can add the Class Library dll to your toolbox using the (Tools/Choose ToolBox Items) menu option. If not then you can always add a Reference to the dll in your project and add them to the form from your code.

 They are probably a lot more functional than the current one you are trying to use. Frank mentioned one in his post too but, that may be one you have to purchase.

A custom range selector control in C# (with a little animating slider)

CRangeSlider - a Ctrl for Selecting a Range or Interval

WPF MultiRangeSlider Control

 If i get a chance later i will look the code over to see if i can find why it throws that error but, i am kind of busy working on other things today.  8)

If you say it can`t be done then i`ll try it


Tuesday, February 10, 2015 7:44 PM

Hi,

 I went through the code real quick and changed a couple lines in the code and reposted it in my original post that had the fixed code in it to keep this thread as short as possible. I added 2 lines in the SetDefaults sub to make the control big enough to see the Ticks when you add one to the form. I also set the values you wanted there too.

 There is no property in this control to adjust the spacing of the Ticks so, i changed a little code in the OnPaint sub to keep a fairly decent spacing between the Ticks as the trackbar`s width is adjusted (its just for looks). While i was doing that i changed the code to show the Ticks at the top of the trackbar and to draw the top pointing thumb buttons. I don`t know if you really wanted that but, i think it is nicer than the upsidedown looking trackbar.

 If you want it the other way then let me know and i will change it or tell you how to change it back. Also, if you are going to change any code in the trackbar class then depending on what you change, you may want to delete the trackbar from the form and then rebuild the project. Then add a new trackbar from the toolbox. It does not always cause problems but, i have seen times it will.

 Here is what it looks like when i add a new one to the form in the [Design] tab.

If you say it can`t be done then i`ll try it


Tuesday, February 10, 2015 9:41 PM

Thank you, IronRazerz.

I got the ticks. I still have problem when set 

        Me.Maximum = 400
        Me.Minimum = 100
        Me.ValueLeft = 180
        Me.ValueRight = 300

It crashes when build. 

Thanks. 


Friday, February 13, 2015 7:05 PM | 2 votes

I'm suing VB 2010 Express and I need to create a trackbar control with double trackbars.

I can't find "Doubletrackbar" control from Toolbox. I searched online and found one post toking about it (http://www.vbforums.com/showthread.php?620394-WIP-Double-TrackBar). Unfortunately, I can't use the code in the post.   Anyone can help?

Thank you very much.

So here is my delayed response to this thread. I read it the other day and thought to myself, "why not?".

This example is independant of previously mentioned articles on the internet and does not quite work exactly as the one you had asked a question about. Instead this one works on the notion of the ability to add as many heads to the slider as you want to, with a partially customizable head. With these customizable heads, you can dictate the following:

  • How many heads will be on the slider
  • Which side of the slider they will be on(Top, Bottom, Left, or Right)
  • The Pens and Brushes to be used, including support for linear gradient brushes
  • The size of each individual slider head(Width, height, and tip length)
  • Value changed event for each head
  • Varying min and max values for each head

Please note* This example has not been fully tested and some errors may arise when attempting to do complex or strange things... I have made a code gallery, link at bottom of page.

Screenshot:

Code Gallery Link:

Multi-Headed Trackbar

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.


Friday, February 13, 2015 9:38 PM

Got it working Paul! Very nice.

Most of what I understand is the ATAN2 function.

:)


Friday, February 13, 2015 11:47 PM

Got it working Paul! Very nice.

Most of what I understand is the ATAN2 function.

:)

Thanks Tommy,

Theres a few things I noticed that I forgot to do... Such as add LGB support for all head orientations, that and I forgot to complete the LEFT and TOP properties of the slider head. One more interesting thing to note is that right now, I have not implemented support to change the minimum value to something other than 0. Just a few oversights on my part. But this control was a bit of a challenge, and I have quite a few ideas still to add options and features, so still got a ways to go.

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.


Saturday, February 14, 2015 12:05 AM

@ Paul,

 Yes that is pretty cool. I started a Double Trackbar similar to the one shown in my images above but, this definitely has a lot more functionality to it than mine will. I like the part of being able to set the Thumbs on opposite sides and the ability of setting the Min Max for each thumb. I was making mine so it is more of a range control where the thumbs can not pass each other. Yours has a lot broader range of possibilities for it though.

 You really should put that on the Code Gallery when you finish it up. I think there would be a lot of hits on it. Plus it would be easier for everyone to find and reference a link to.   8)

If you say it can`t be done then i`ll try it


Saturday, February 14, 2015 12:08 AM

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.


Saturday, February 14, 2015 12:17 AM

@ Paul,

 Yes that is pretty cool. I started a Double Trackbar similar to the one shown in my images above but, this definitely has a lot more functionality to it than mine will. I like the part of being able to set the Thumbs on opposite sides and the ability of setting the Min Max for each thumb. I was making mine so it is more of a range control where the thumbs can not pass each other. Yours has a lot broader range of possibilities for it though.

 You really should put that on the Code Gallery when you finish it up. I think there would be a lot of hits on it. Plus it would be easier for everyone to find and reference a link to.   8)

If you say it can`t be done then i`ll try it

Thanks for the compliment :-).

Anything with drawing always attracts my attention. I did plan on creating a wiki-article and a code gallery sample, but this example really isnt finished yet. I intend on making it to where it supports signed floating point ranges. Right now it supports integer ranges from a fixed 0 to a positive number. Of course there is outside logic that could be applied to implement the ladder. I am also adding a Pre-HeadRender event, that way someone could render their own graphics before the heads are rendered(such as custom tick-marks). I also plan on adding a Post-HeadRender paint event for the rendering of information above the heads. I also want to add support for custom head-polygons, and also even heads rendered from image resources. I really do have alot of ideas for this, but I was trying to get an example up here as quick as possible(still took what? 3 days?) I've probably had other ideas as well, which I will remember as I explore this idea further, any suggestions would be neat too.

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.


Saturday, February 14, 2015 12:40 AM | 1 vote

Anything to do with custom controls and drawing is very interesting to me too. As i told Tom the other day, i guess i am just a "Control" freak. haha.

 Yes the floating point values would be nice. It would save a little extra coding in the users form. The Pre and Post HeadRenders would be a nice addition too in case the user wants to do a little custom drawing. If your not careful your going to have to start charge money for this.

 I have not tested it yet but, have read through and glanced through the code just to see what all was going on. Been pretty busy working on a few projects of my own lately but, i will give it a go later or tomorrow. It sound like you have enough ideas to keep you busy with it for a few more days anyways. Let us know when you get it up on the Code Gallery or wherever. I will surely post a link to it if i see another question like this.   8)

If you say it can`t be done then i`ll try it


Saturday, February 14, 2015 6:15 AM | 1 vote

Let us know when you get it up on the Code Gallery or wherever. I will surely post a link to it if i see another question like this.  

IronRazerz,

I think I'm done for now...

Here is a link to Code Gallery:

Multi-Headed Trackbar Control

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.


Saturday, February 14, 2015 11:04 AM

Paul,

 Got it, thanks Paul. I downloaded your class for Accessing and Modifying Binary Wave files too. That`s another part of programming that i am pretty interested in.  8)

If you say it can`t be done then i`ll try it


Saturday, February 14, 2015 12:52 PM

Let us know when you get it up on the Code Gallery or wherever. I will surely post a link to it if i see another question like this.  

IronRazerz,

I think I'm done for now...

Here is a link to Code Gallery:

Multi-Headed Trackbar Control

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.

Crazy man.  I love how the sliderhead jumps to the top or bottom as you move it with the mouse.

You certainly are productive. I hope they pay you enough where you work.  :)


Saturday, February 14, 2015 2:50 PM

Crazy man.  I love how the sliderhead jumps to the top or bottom as you move it with the mouse.

You certainly are productive. I hope they pay you enough where you work.  :)

That was a last minute idea I had. I got to thinking about how overlapping heads might prove to be an inconvenience, and that's the idea I went with to lessen the problem, the other option was incorporating a Z-order for the heads(which I should still probably do).

As far as getting paid enough... Maybe one day... :-)

“If you want something you've never had, you need to do something you've never done.”

Don't forget to mark helpful posts and answers ! Answer an interesting question? Write a new article about it! My Articles

*This post does not reflect the opinion of Microsoft, or its employees.