Roland Bär
Roland Bär

Reputation: 1740

Automatic formatting in WPF TextBox

I try to create a simple Hex Input Control. In this control there should be a space char be added after 2 chars while the user is editing. The user should not be forced to press the space bar, the space should be added automatically. The space chars should then not be in the binded value. There also should not be a fixed length for the hex string (like it would be with a masked textbox).

Is there an extension or control available for this or has anybody already created such a control?

Upvotes: 0

Views: 700

Answers (2)

Omri Btian
Omri Btian

Reputation: 6547

You can implement your own control that does this, by overriding it's OnTextChanged method

public class HexTextBox : TextBox
{
    private bool isUserTextChaned = true;

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        if (isUserTextChaned)
        {
            isUserTextChaned = false;

            string temp = Text.Replace(" ", string.Empty);

            // Support for backspace
            if (e.Changes.First().RemovedLength > 0 && !Text.EndsWith(" "))
            {
                temp = temp.Substring(0, temp.Length - e.Changes.First().RemovedLength);
            }

            // Insert spaces
            temp = Regex.Replace(temp, @"(.{2})", "$1 ");

            // Update text
            Text = temp;

            isUserTextChaned = true;

            // Set cursor
            Select(Text.Length, Text.Length);

            base.OnTextChanged(e);
        }
    }
}

Explaination:

Since every change to the Text property will fire the TextChanged event, to avoid recursion I've added the isUserTextChanged field to determine that the TextChanged was fired externally.

Next, every third character we redefine the Text property with a ' ' inbetween every two chars.

Finally, we set the cursor to the end of the Text by selecting the last char Select(Text.Length, Text.Length);.

Hope this helps :)

Upvotes: 1

Florian Gl
Florian Gl

Reputation: 6014

An easy solution would be doing this with a converter:

public class SpaceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string newValue = "";
        int cnt = 0;
        foreach (char c in value.ToString())
        {
            if (cnt == 2)
            {
                 newValue += " ";
                 cnt=0;
            }
            newValue += c;
            cnt++;
        }
        return newValue;
    }

    public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
    {
        return value.Tostring().Replace(" ","");
    }
}

EDIT: To set the cursor I created a attached property which takes care about the formatting:

public class TextBoxFormatter
{
    public static readonly DependencyProperty EnableFormattingProperty =
        DependencyProperty.RegisterAttached("EnableFormatting", typeof(bool), typeof(TextBoxFormatter),
            new PropertyMetadata(default(bool), PropertyChangedCallback));

    private static void PropertyChangedCallback(DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        TextBox tb = dependencyObject as TextBox;
        if (tb != null)
        {
            bool value = (bool)dependencyPropertyChangedEventArgs.NewValue;
            tb.TextChanged -= TBTextChanged;
            if (value)
            {
                Format(tb, true);
                tb.TextChanged += TBTextChanged;
            }
        }
    }

    public static void SetEnableFormatting(TextBox element, bool value)
    {
        element.SetValue(EnableFormattingProperty, value);
    }

    public static bool GetEnableFormatting(TextBox element)
    {
        return (bool) element.GetValue(EnableFormattingProperty);
    }

    private static void TBTextChanged(object sender, TextChangedEventArgs e)
    {
        Format(sender as TextBox);
    }

    private static void Format(TextBox tb, bool init = false)
    {
        if (tb != null)
        {
            int ci = tb.CaretIndex;
            string newValue = "";
            int cnt = 0;
            foreach (char c in tb.Text)
            {
                if (c != ' ')
                {
                    if (cnt > 0 && ShouldFormat(cnt))
                    {
                        newValue += " ";
                        if (ci > cnt && init)
                            ci++;
                    }
                    cnt++;
                    newValue += c;
                }
            }
            tb.Text = newValue;
            SetCaret(tb, ci);
        }
    }

    private static void SetCaret(TextBox tb, int oldCaret)
    {
        if (oldCaret <= 0 || oldCaret >= tb.Text.Length)
            return;

        if (tb.Text[oldCaret-1] != ' ' && tb.Text[oldCaret] != ' ')
            tb.CaretIndex += oldCaret;
        else
            tb.CaretIndex = oldCaret+1;
    }

    private static bool ShouldFormat(int index)
    {
        return index%2 == 0;
    }
}

How to use it:

<TextBox ns:TextBoxFormatter.EnableFormatting="True"></TextBox>

Upvotes: 3

Related Questions