Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
4 Jan 2012 1  
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.

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 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