Click here to Skip to main content
6,821,293 members and growing! (19,459 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » Windows Presentation Foundation » General     Intermediate

Microsoft Blend Style Incrementing TextBox

By Karl Shifflett

An article on how to extend the WPF TextBox control to function like the Microsoft Blend TextBox that change value by using the mouse. As a bonus feature, a very cool data binding tooltip is included.
C#2.0, C#3.0, VB8.0, VB9.0WinXP, Vista, .NET3.0, XAML, WPF, VS2005, Architect, Dev, Design
Posted:24 Jun 2007
Views:37,838
Bookmarked:25 times
Unedited contribution
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 2.76 Rating: 3.55 out of 5

1
1 vote, 16.7%
2
2 votes, 33.3%
3
1 vote, 16.7%
4
2 votes, 33.3%
5

Introduction

This article describes how to extend the WPF TextBox control to function like the Microsoft Blend TextBox that increment and decrement their value by using the mouse. As a bonus feature, a very cool data binding tooltip is included.

Background

As part of a Blend presentation, I included a WPF Take Home Pay Calculator program. (Demo only calculates North Carolina State tax for 2007 not a full blown program). As a demo feature, I wanted TextBoxes that the user could change the value by using their mouse. You can download that presentation and code at my blog here : Get The Code From : Grain Of Sand

Obviously, developers wouldn't want every TextBox on their forms to have this feature! However, you may have an application that this fits. This is more of a you can do this article.

Also included in the .xaml is a resource that renders the ToolTip and data binds back to the properties of the control that spawned the tooltip.

I must give credit to Tom Mathews for his demo code on ToolTip data binding. You can view his code here : Binding a ToolTip in XAML.

I spent a good deal of time trying different things and almost gave up before I found Tom's post. Thank you Tom!

Desired Feature Of TextBox : To Function Like The Microsoft Blend TextBox

  • Increment or decrement numeric value of TextBox by allowing the user to click on the TextBox and drag up, down, right or left and have value change.
  • If the user enters a non-numeric value, just return the TextBox to its previous value after the user leaves the TextBox.
  • Extra Credit : Display a ToolTip to the user, explaining how the feature works and how much the value will change based on the direction they drag their mouse.

Extending The TextBox

Like a good number of developers, I have authored all my own ASP.NET controls and WinForm controls. I have not extended any WPF controls so far, so this is my first. If I miss something or didn't do it the WPF way please post a comment.

Properties

Added two new properties XIncrementValue and YIncrementValue to the extended TextBox.

XIncrementValue is the how much the value will be incremented or decremented when the user drags the mouse right or left.

YIncrementValue is the how much the value will be incremented or decremented when the user drags the mouse up or down.

Public Property XIncrementValue() As Integer
    Get
        Return _intXIncrementValue
    End Get
    Set(ByVal Value As Integer)

        If Value < 0 Then
            Throw New ArgumentOutOfRangeException("XIncrementValue", "Must be equal to or greater than 0.")
        End If

        _intXIncrementValue = Value
    End Set
End Property

Public Property YIncrementValue() As Integer
    Get
        Return _intYIncrementValue
    End Get
    Set(ByVal Value As Integer)

        If Value < 0 Then
            Throw New ArgumentOutOfRangeException("YIncrementValue", "Must be equal to or greater than 0.")
        End If

        _intYIncrementValue = Value
    End Set
End Property

Trapping The Mouse Down To Begin The Increment - Decrement

I have declare a module level variable _objMouseIncrementor of Type MouseIncrementor. When the user first clicks the TextBox a new MouseIncrementor object is fired up and stored.

Notice that we are storing the location of the mouse and setting the initial state of the MouseIncrementor to MouseIncrementor.MouseDirections.None. This is important because until the user starts dragging right, left, up or down, the TextBox control does not know how to change the value of the TextBox.

The MouseIncrementor class is a simple container for the data we need to know in order to make the calculations and also to know if the control is in Mouse Increment mode. It keeps track of the mouse movement direction and the Point of the mouse.

Private _objMouseIncrementor As MouseIncrementor

Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Input.MouseButtonEventArgs)
    MyBase.OnMouseDown(e)
    _objMouseIncrementor = New MouseIncrementor(e.GetPosition(Me), MouseIncrementor.MouseDirections.None)
End Sub

A simple class for storing the state of the mouse increment, decrement operation.

Class MouseIncrementor

    Private _enumMouseDirection As MouseDirections = MouseDirections.None
    Private _objPoint As Point

    Enum MouseDirections
        LeftRight
        None
        UpDown
    End Enum

    Public Property MouseDirection() As MouseDirections
        Get
            Return _enumMouseDirection
        End Get
        Set(ByVal Value As MouseDirections)
            _enumMouseDirection = Value
        End Set
    End Property

    Public Property Point() As Point
        Get
            Return _objPoint
        End Get
        Set(ByVal Value As Point)
            _objPoint = Value
        End Set
    End Property

    Public Sub New(ByVal objPoint As Point, _
                   ByVal enumMouseDirection As MouseDirections)
        _objPoint = objPoint
        _enumMouseDirection = enumMouseDirection

    End Sub

End Class

Trapping The MouseMove, Incrementing, Decrementing The Value

Sanity Checks

  • Verify that we are in an incrementing or decrementing operation. We know this by checking if the _objMouseIncrementor object has been instantiated or not.
  • Check if the developer set the XIncrementValue and YIncrementValue property values at design or run-time.
  • Check to make sure the value in the TextBox will parse to a number.

We need to determine if this is the first time the OnMouseMove sub has run since the _objMouseIncrementor object was instantiated. We know this by checking the _objMouseIncrementor.MouseDirection property. If its currently set to None then by checking the delta of the X and Y movements and testing for the higher value we can set the _objMouseIncrementor.MouseDirection to either LeftRight or UpDown so that the next time OnMouseMove is called, we know how to calculate the new TextBox value.

Now that we know which direction the mouse is moving and the delta from the last position, we can quickly calculate the new value of the TextBox.

Last order of business to the actually set the TextBox value and to record the current position of the mouse.

Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Input.MouseEventArgs)
    MyBase.OnMouseMove(e)

    If _objMouseIncrementor Is Nothing Then
        'nothing to do here
        Exit Sub
    End If

    If _intXIncrementValue = 0 AndAlso _intYIncrementValue = 0 Then
        'nothing to do here
        Exit Sub
    End If

    Dim dblValue As Double

    If Double.TryParse(Me.Text, dblValue) = False Then
        'since we can't parse the value, we are out of here, i.e. user put text in our number box
        _objMouseIncrementor = Nothing
        Exit Sub
    End If

    Dim intDeltaX As Double = _objMouseIncrementor.Point.X - e.GetPosition(Me).X
    Dim intDeltaY As Double = _objMouseIncrementor.Point.Y - e.GetPosition(Me).Y

    If _objMouseIncrementor.MouseDirection = MouseIncrementor.MouseDirections.None Then

        'this is our first time here, so we need to record if we are tracking x or y movements
        If Math.Abs(intDeltaX) > Math.Abs(intDeltaY) Then
            _objMouseIncrementor.MouseDirection = MouseIncrementor.MouseDirections.LeftRight

        Else
            _objMouseIncrementor.MouseDirection = MouseIncrementor.MouseDirections.UpDown
        End If

    End If

    If _objMouseIncrementor.MouseDirection = MouseIncrementor.MouseDirections.LeftRight Then

        If intDeltaX > 0 Then
            dblValue -= _intXIncrementValue

        ElseIf intDeltaX < 0 Then
            dblValue += _intXIncrementValue
        End If

    Else

        If intDeltaY > 0 Then
            dblValue += _intYIncrementValue

        Else
            dblValue -= _intYIncrementValue
        End If

    End If

    Me.Text = dblValue.ToString
    _objMouseIncrementor.Point = e.GetPosition(Me)

End Sub

When User Releases The Mouse Button

When the OnMouseUp event code runs it sets _objMouseIncrementor = Nothing. This takes the TextBox control out of mouse increment mode.

Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Input.MouseButtonEventArgs)
    MyBase.OnMouseUp(e)
    _objMouseIncrementor = Nothing
    _strOriginalTextValue = Nothing
End Sub

Allowing The User To Select Text In TextBox With The Mouse

With all this mouse movement and mouse down trapping going on, we can't forget to allow the user to double click the TextBox to select the text. Easily accomplish by the following event handler.

Protected Overrides Sub OnMouseDoubleClick(ByVal e As System.Windows.Input.MouseButtonEventArgs)
    MyBase.OnMouseDoubleClick(e)
    _objMouseIncrementor = Nothing
    Me.SelectAll()
End Sub

Handling Text Entry In Our Numeric TextBox

Please keep in mind, there are better ways to restrict the allowed characters in a TextBox than the below code. We are trying to emulate the Microsoft Blend TextBox, so let us just do it like Blend. Below is the simple code that accomplishes this.

The only comment for this code is the _strOriginalTextValue variable that gets set in the OnGotFocus event. This value allows us to restore the TextBox value if the user enters a non numeric value.

Protected Overrides Sub OnLostFocus(ByVal e As System.Windows.RoutedEventArgs)
    MyBase.OnLostFocus(e)

    'this simulates the functionality of the Blend textbox.
    'it allows the end user to enter non numeric characters in the textbox and simply resets the original value rather an catching mistakes at keypress time.

    Dim dblValue As Double

    If Double.TryParse(MyBase.Text, dblValue) = True Then
        MyBase.Text = dblValue.ToString

    Else
        If String.IsNullOrEmpty(_strOriginalTextValue) Then
            _strOriginalTextValue = "0"
        End If
        MyBase.Text = _strOriginalTextValue
    End If

    _strOriginalTextValue = Nothing
    _objMouseIncrementor = Nothing

End Sub

The ToolTip!

Most of the below code is standard xaml mark up.

However, the cool part of this ToolTip is the data binding to properties from the control this ToolTip belongs to.

Since a ToolTip is really a new Window, you can't data bind by trying to set its source to an element on the Window like we normally would by setting the ElementName property. Instead you must set a DataContext for the ToolTip. It took me forever to find out how to do this. I finally found this in Tom Mathews article that I mentioned at the start of this article that explains this.

The magic is all in the statement, DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}". This establishes the DataContext of the ToolTip as the control. Now we have full access to all of the control's properties. This code is slightly different from Tom's but is also performing a different function.

Since we now have a DataContext, just data bind to properties on the control that opened this ToolTip. Example : Text="{Binding Path=XIncrementValue}"

Below the two bold values, 100 and 50 are the YIncrementValue and XIncrementValue properties of the TextBox.

Screenshot - CoolTooltip.png

<Window.Resources>

    <ToolTip x:Key="toolTip4TextBoxWithMouseIncrementing" DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
        <StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Width="409" Height="Auto" Background="#FFDEE5F0">
            <StackPanel.Resources>
                <LinearGradientBrush x:Key="CoolToolTipGradientBrush" EndPoint="1,0.5" StartPoint="0,0.5">
                    <GradientStop Color="#FF0024A5" Offset="0"/>
                    <GradientStop Color="#FF148CAB" Offset="1"/>
                    <GradientStop Color="#FF14A7AE" Offset="0.242"/>
                    <GradientStop Color="#FF1676AA" Offset="0.542"/>
                    <GradientStop Color="#FE09E4CE" Offset="0.836"/>
                </LinearGradientBrush>
            </StackPanel.Resources>

            <TextBlock FontWeight="Bold" Foreground="White" 
                Text="Using The Incrementing TextBox" 
                Background="{DynamicResource CoolToolTipGradientBrush}" 
                Margin="0,0,0,10" Padding="0,3,0,3" TextAlignment="Center"/>

            <TextBlock TextWrapping="WrapWithOverflow" Margin="5,0,5,0">
                 You can increment or decrement the value of this 
                 textbox by simply clicking the mouse, hold and drag 
                 up or down, right or left. Release when happy with the value.</TextBlock>

            <Line Stroke="#FF0069D7" StrokeThickness="2" Margin="0,5,0,5" HorizontalAlignment="Stretch" X2="409"/>

            <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
               <TextBlock Margin="5,0,5,0">When moving the mouse up(+) or down(-) the value will change by :</TextBlock>
               <TextBlock FontWeight="Bold" Text="{Binding Path=YIncrementValue}"/>
            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                <TextBlock Margin="5,0,5,0">When moving the mouse right(+) or left(-) the value will change by :</TextBlock>
                <TextBlock FontWeight="Bold" Text="{Binding Path=XIncrementValue}"/>
            </StackPanel>

            <TextBlock Text="WPF is so cool!" 
                  HorizontalAlignment="Stretch" 
                  Background="{DynamicResource CoolToolTipGradientBrush}" 
                  Foreground="#FFFFFFFF" TextAlignment="Center" Margin="0,5,0,0" Padding="0,3,0,3"/>
        </StackPanel>
    </ToolTip>


</Window.Resources>

Closing

Hope this article can help someone learn a little more about WPF.

History

  • 24 June 2007 : Initial Release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Karl Shifflett


Member
Karl loves .NET, WPF, WCF, ASP.NET, VB.NET and C#.

Awards:

  • December 2008 VB.NET Code Project Article Award
  • 2009 Code Project MVP
  • 2008 Code Project MVP
  • 2008 Microsoft MVP - Client App Dev
  • December 2007 VB.NET Code Project Article Award
  • Gold Medal Winner at IBM's 1998 PROIV Programming Contest in Las Vegas

Click here to check out my Blog.

Click here to read everything Mole, at Mole's Home page. Includes full screen videos.

Click here to visit The Mole Project and meet Team Mole (www.moleproject.com)

Click here to read Code Project article Mole For Visual Studio - With Editing - Visualize All Project Types

Click here to read about XAML Power Toys

Click here to learn why I chose WPF over ASP.NET for a very large project.

Just a grain of sand on the worlds beaches.


Occupation: Program Manager
Company: Microsoft Corporation
Location: United States United States

Other popular Windows Presentation Foundation articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 5 of 5 (Total in Forum: 5) (Refresh)FirstPrevNext
GeneralNice Article! PinmemberNan Sheng16:17 14 Oct '07  
GeneralRe: Nice Article! PinmemberKarl_Shifflett16:22 14 Oct '07  
QuestionCan't load project Pinmemberthe-rpd11:54 26 Jun '07  
AnswerRe: Can't load project [modified] PinmemberKarl_Shifflett16:43 26 Jun '07  
GeneralRe: Can't load project Pinmemberthe-rpd7:16 27 Jun '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.

PermaLink | Privacy | Terms of Use
Last Updated: 24 Jun 2007
Editor:
Copyright 2007 by Karl Shifflett
Everything else Copyright © CodeProject, 1999-2010
Web17 | Advertise on the Code Project