Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF

WPF Maskable TextBox for Numeric Values

Rate me:
Please Sign up or sign in to vote.
4.76/5 (37 votes)
19 Mar 2009CPOL3 min read 206.6K   9.4K   70   29
Extension of the WPF TextBox which accepts only numeric (integer and floating point) values.

Introduction

This article describes how to enhance the WPF TextBox and make it accept just numeric (integer and floating point) values. The second goal is make the TextBox smart enough to make it easier to input numerics. This is an easy means to provide the TextBox with some kind of intelligence, not just rejecting non-numeric symbols. The provided extension also allows setting minimum and/or maximum values.

If you search in the net, you will probably find some solutions for this problem where developers create their own versions of the TextBox either by inheriting from it or creating a Custom/User Controls that include the standard WPF TextBox. Most other solutions have one major drawback - you would need to replace your TextBox definitions with your new MaskTextBox. Sometimes, it is not painful, sometimes, it is. The reason I chose another solution is that in my case, such kind of changes would be painful.

The approach I’m proposing here is the usage of WPF Attached Properties, which basically are similar to Dependency Properties. The major difference among these to is that Dependency Properties are defined inside the control, but Attached Properties are defined outside. For instance, TextBox.Text is a Dependency Property, but Grid.Column is an Attached Property.

Background (Extending the Functionality of the TextBox)

First of all, I will define an enumeration which tells us whether the TextBox accepts integer, decimal, or any kind of values:

C#
public enum MaskType  
{  
   Any,  
   Integer,  
   Decimal  
}

The next thing would be the definition of the Attached Property.

C#
public class TextBoxMaskBehavior
{
   public static MaskType GetMask(DependencyObject obj)
   {
      return (MaskType)obj.GetValue(MaskProperty);
   }

   public static void SetMask(DependencyObject obj, MaskType value)
   {
      obj.SetValue(MaskProperty, value);
   }

   public static readonly DependencyProperty MaskProperty =
      DependencyProperty.RegisterAttached(
      "Mask",
      typeof(MaskType),
      typeof(TextBoxMaskBehavior),
      new FrameworkPropertyMetadata(MaskChangedCallback)
      );

   private static void MaskChangedCallback(DependencyObject d, 
                       DependencyPropertyChangedEventArgs e)
   {
      // ...
   }
}

Now, we specify the mask for the WPF TextBox like this:

XML
<TextBox local:TextBoxMaskBehavior.Mask="Integer" />

or

XML
<TextBox local:TextBoxMaskBehavior.Mask="Decimal" />

and our assumption would be that the particular TextBox would accept integer and decimal values, respectively. Whenever TestBoxMaskBehavior.Mask is set, MaskChangedCallback is being called. We will add the corresponding handling there.

Background (Subscribing to the TextBox Changes)

The WPF TextBox comes with an event: PreviewTextInput, which allows to listen for text input. It also allows to cancel a particular text input by setting the Handled property of the TextCompositionEventArgs to true. Shown below is an illustrative example:

XML
<TextBox PreviewTextInput="TextBox_PreviewTextInput" />

The C# code:

C#
private static void TextBox_PreviewTextInput(object sender, 
        System.Windows.Input.TextCompositionEventArgs e)
{
   try
   {
      Convert.ToInt32(e.Text);
   }
   catch
   {
      e.Handled = true;
   }
}

Please note that this code is just an example of the PreviewTextInput event usage. My solution has much more useful features rather than checking whether the input text is a number or not :).

C#
DataObject.AddPastingHandler(myTextBox, TextBoxPastingEventHandler);
//...
private void TextBoxPastingEventHandler(object sender, DataObjectPastingEventArgs e)
{
   string clipboard = e.DataObject.GetData(typeof(string)) as string;
   try
   {
      Convert.ToInt32(clipboard);
   }
   catch
   {
      e.CancelCommand();
      e.Handled = true;
   }
}

I think that was self-explanatory :).

Maskable TextBox

We have covered the techniques that I used in the solution. The full source code and the demo binary are available from the links above. Here is a list of the features available for my Maskable TextBox:

  • Rejects symbols other than digits, negative signs, and the decimal separator.
  • Clamps to minimum/maximum values, if any specified.
  • When typing negative sign, regardless from the caret position, adds it in the beginning if not exist, or removes the existing one.
  • When typing the decimal separator, removes the existing one (if exists), and places the new one in the correct place.
  • And, more useful stuff. Just try to use it. I’m sure you will not regret.

Origin

The origin of this article can be found at WPF Maskable TextBox for Numeric Values.

License

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


Written By
Software Developer (Senior) Independent Contractor
United States United States

Comments and Discussions

 
GeneralMy vote of 5 Pin
CkyRoomee27-Oct-22 1:17
CkyRoomee27-Oct-22 1:17 
GeneralMy vote of 5 Pin
viler844-Sep-14 23:25
viler844-Sep-14 23:25 
QuestionIts not possible to enter the decimal seperator. Pin
jens_weller8-Jan-14 23:19
jens_weller8-Jan-14 23:19 
AnswerRe: Its not possible to enter the decimal seperator. Pin
abdelilah douch25-Sep-17 6:55
abdelilah douch25-Sep-17 6:55 
QuestionDoes not work with binding Pin
Member 985897917-Jul-13 2:59
Member 985897917-Jul-13 2:59 
QuestionMaxLength Pin
Member 1000105720-Apr-13 6:57
Member 1000105720-Apr-13 6:57 
AnswerRe: MaxLength Pin
Member 1044762028-Jan-14 7:12
Member 1044762028-Jan-14 7:12 
GeneralMy vote of 5 Pin
Kalpesh_Patolia10-Feb-13 20:04
Kalpesh_Patolia10-Feb-13 20:04 
GeneralMy vote of 5 Pin
rjvenugopal27-Jan-13 20:09
rjvenugopal27-Jan-13 20:09 
GeneralMy vote of 4 Pin
zxzbenjamin24-Dec-12 19:05
zxzbenjamin24-Dec-12 19:05 
this can not stop chinese ime such as sougou inputting characters
QuestionDelimiter Pin
mehrdadc488-Apr-12 2:11
mehrdadc488-Apr-12 2:11 
AnswerRe: Delimiter Pin
Ruben Hakopian24-Apr-12 19:22
Ruben Hakopian24-Apr-12 19:22 
GeneralMy vote of 5 Pin
San@Coding2-Apr-12 2:16
San@Coding2-Apr-12 2:16 
GeneralMy vote of 5 Pin
Stephane19845-Jan-12 8:29
Stephane19845-Jan-12 8:29 
Questionnegative sign rejected [modified] Pin
Randall Edick25-Aug-11 6:49
Randall Edick25-Aug-11 6:49 
AnswerRe: negative sign rejected [modified] Pin
Ruben Hakopian24-Apr-12 19:25
Ruben Hakopian24-Apr-12 19:25 
GeneralMy vote of 4 Pin
wujiong19-Jun-11 22:57
wujiong19-Jun-11 22:57 
QuestionGreate work but there is some confusing Pin
laskoteam7-Nov-10 0:05
laskoteam7-Nov-10 0:05 
QuestionSupport of up/down key? Pin
miliu27-Oct-10 7:36
miliu27-Oct-10 7:36 
GeneralVery well done Pin
EagleSigmaSF11-Aug-10 22:08
EagleSigmaSF11-Aug-10 22:08 
GeneralGood Work With Small fall Pin
Joe Rozario3-Jun-10 2:17
Joe Rozario3-Jun-10 2:17 
GeneralGood Work Pin
Fact Pandit3-Feb-10 9:05
Fact Pandit3-Feb-10 9:05 
GeneralRe: Good Work Pin
Ruben Hakopian3-Feb-10 9:10
Ruben Hakopian3-Feb-10 9:10 
GeneralRe: Good Work Pin
Fact Pandit6-Sep-10 12:25
Fact Pandit6-Sep-10 12:25 
GeneralThank you. Very nice approach. Pin
Roman Lerman24-Oct-09 20:08
Roman Lerman24-Oct-09 20:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

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