Click here to Skip to main content
Click here to Skip to main content

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

By , 4 Jan 2012
 

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.

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.

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.

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.

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.

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.

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)

About the Author

Joanne88

India India
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionsample?memberJaime Olivares24-Jul-12 11:19 

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130617.1 | Last Updated 4 Jan 2012
Article Copyright 2012 by Joanne88
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid