Click here to Skip to main content
15,881,882 members
Articles / Desktop Programming / WPF
Article

Custom WPF TextBox which allows input based on the data type or a Regular Expression

Rate me:
Please Sign up or sign in to vote.
1.00/5 (1 vote)
4 Jan 2012CPOL2 min read 24.2K   666   6   2
Extending the WPF textbox to permit only numeric (for int and float) and regex.

Introduction

This article describes how to create a custom WPF textbox control which permits the user to input only those characters based on the data type. For example, for int data type, only numeric characters are permitted. Also there is a feature to define a Regular Expression mask for the textbox.

Using the code

First we need to create a dependency property which will allow us to set the data type.

C#
public static DependencyProperty DataTypeProperty = DependencyProperty.Register("DataType", 
       typeof(string), typeof(MaskTextBox), new PropertyMetadata("string"));

public string DataType
{
    get { return (string)GetValue(DataTypeProperty); }
    set { SetValue(DataTypeProperty, value); }
}
...

In this sample, we have used three data types: int, float, and RegEx. For RegEx, we will also require another dependency property called RegEx which will enable us to specify the Regular Expression.

C#
public static DependencyProperty RegExProperty = DependencyProperty.Register("RegEx", 
       typeof(string), typeof(MaskTextBox), new PropertyMetadata("string"));
public string RegEx
{
    get { return (string)GetValue(RegExProperty); }
    set { SetValue(RegExProperty, value); }
}
...

Next, we write a method that will validate the input depending on the data type. This method uses the TryParse method to verify the int and float data types. For RegEx, we use the IsMatch method to verify the input with the Regular Expression specified in the Regex property. This method is used mainly when data is pasted into the textbox.

C#
private Boolean IsDataValid(IDataObject data)
{
    Boolean isValid = false;
    if (data != null)
    {
        String text = data.GetData(DataFormats.Text) as String;
        if (!String.IsNullOrEmpty(text == null ? null : text.Trim()))
        {
            switch (DataType)
            {
                case "INT":
                    Int32 result = -1;
                    if (Int32.TryParse(text.Trim(), out result))
                    {
                        if (result > 0)
                        {
                            isValid = true;
                        }
                    }
                    break;

                case "FLOAT":
                    float floatResult = -1;
                    if (float.TryParse(text.Trim(), out floatResult))
                    {
                        if (floatResult > 0)
                        {
                            isValid = true;
                        }
                    }
                    break;
                case "RegEx":
                    if (System.Text.RegularExpressions.Regex.IsMatch(text, RegEx))
                    {
                        isValid = true;
                    }
                    break;
            }
        }
    }
    return isValid;
}

protected override void OnDrop(DragEventArgs e)
{
    e.Handled = !IsDataValid(e.Data);
    base.OnDrop(e);
}

protected override void OnDragOver(DragEventArgs e)
{
    if (!IsDataValid(e.Data))
    {
        e.Handled = true;
        e.Effects = DragDropEffects.None;
    }
    base.OnDragEnter(e);
}

EventManager.RegisterClassHandler(
        typeof(MaskTextBox),
        DataObject.PastingEvent,
        (DataObjectPastingEventHandler)((sender, e) =>
                         {
                             if (!IsDataValid(e.DataObject))
                             {
                                 DataObject data = new DataObject();
                                 data.SetText(String.Empty);
                                 e.DataObject = data;
                                 e.Handled = false;
                             }
                         }));
...

We also need to restrict the user from entering invalid characters. For this, we override the OnPreviewTextInput method of the WPF textbox. In this method, we generate the final text by inserting the new character at the caret index. This ensures that even if the user types the new character in between or at the beginning of the existing characters, the validation does not fail. Next, the text is validated according to the data type specified. If the validation fails, e.Handled is set to true and the user action will get cancelled.

C#
string text = this.Text;
text = text.Insert(this.CaretIndex, e.Text);
switch (DataType)
{
    case "INT":
        Int32 result = -1;
        if (!Int32.TryParse(text.Trim(), out result))
        {
            if (!text.Equals("-"))
                e.Handled = true;
        }
        break;

    case "FLOAT":
        float floatResult = -1;
        if (!float.TryParse(text.Trim(), out floatResult))
        {
            if (!text.Equals("-"))
                e.Handled = true;
        }
        break;
    case "RegEx":
        if (!System.Text.RegularExpressions.Regex.IsMatch(text, RegEx))
        {
            e.Handled = true;
        }
        break;
}
...

Note that in the above code, for cases "INT" and "FLOAT", there is an if condition. This is because for data type int, if the user has to enter -5, the first character that the user will type is "-". However the method Int32.TryParse will return false for "-". Hence the check is required so that the validation does not fail.

We also need to take care that the user does not input just the "-" sign within the textbox. For this, we can add a handler to the LostFocusEvent of the textbox. So whenever the textbox loses focus, the text is verified and if it is just "-", the text is cleared.

C#
this.AddHandler(MaskTextBox.LostFocusEvent, new RoutedEventHandler(LostFocusEventHandler));
public void LostFocusEventHandler(object sender, RoutedEventArgs e)
{
    if (this.Text == "-")
        this.Text = string.Empty;
}
...

Also, Int32.TryParse does not fail the validation if the user inputs a space. We can handle this in the PreviewKeyDownEvent of the textbox and set e.Handled = true if a space is input.

C#
this.AddHandler(MaskTextBox.PreviewKeyDownEvent, 
                new RoutedEventHandler(PreviewKeyDownEventHandler));
public void PreviewKeyDownEventHandler(object sender, RoutedEventArgs e)
{
    KeyEventArgs ke = e as KeyEventArgs;
    if (ke.Key == Key.Space)
    {
        ke.Handled = true;
    }
}
...

License

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


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIt seems you should change function OnPreviewTextInput in the following: Pin
JustMe4TheCodeProject7-May-14 23:39
JustMe4TheCodeProject7-May-14 23:39 
Questionsample? Pin
Jaime Olivares24-Jul-12 11:19
Jaime Olivares24-Jul-12 11:19 

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.