2

I tryed to create my own numeric textbox here is my code:

public class NumericTextBox : TextBox
{

    public NumericTextBox()
        : base()
    {
        this.Text = "0";
    }

    private void HandleKeyEvent(KeyEventArgs e)
    {
        e.Handled = true;
        if ((Keyboard.Modifiers & ModifierKeys.Alt) != 0)
        {
            return;
        }
        if (e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Left || e.Key == Key.Right ||
            e.Key == Key.D0 || e.Key == Key.D1 || e.Key == Key.D2 || e.Key == Key.D3 || e.Key == Key.D4 || e.Key == Key.D5 || e.Key == Key.D6 ||
            e.Key == Key.D7 || e.Key == Key.D8 || e.Key == Key.D9 ||
            e.Key == Key.NumPad0 || e.Key == Key.NumPad1 || e.Key == Key.NumPad2 || e.Key == Key.NumPad3 || e.Key == Key.NumPad4 || e.Key == Key.NumPad5 || e.Key == Key.NumPad6 ||
            e.Key == Key.NumPad7 || e.Key == Key.NumPad8 || e.Key == Key.NumPad9)
        {
            e.Handled = false;
        }
        else if ((e.Key == Key.Subtract || (e.Key == Key.Unknown && e.PlatformKeyCode == 189)) && base.SelectionStart == 0 && (base.Text.Length == 0 || base.Text[0] != '-'))
        {
            e.Handled = false;
        }
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        HandleKeyEvent(e);
        base.OnKeyDown(e);
    }

    protected override void OnKeyUp(KeyEventArgs e)
    {
        HandleKeyEvent(e);
        base.OnKeyUp(e);
    }
}

everything works like supposed but if you press alt and some numbers it creates the ascii symbol corresponding to the number.. is there any way to block an "alt + number combination? it seems that alt + key just gets entered without going threw OnKeyUp or OnKeyDown...

5 Answers 5

2

Here's an alternative, requiring only an attached property and the following code. First, the code:

public enum InputType
{
    PositiveInteger,
    PositiveDecimal,
    PositiveNullableInteger,
    PositiveNullableDecimal,
}

public static class Input
{
    public static readonly DependencyProperty TypeProperty =
        DependencyProperty.RegisterAttached("Type", typeof(InputType), typeof(TextBox),
                                            new PropertyMetadata(default(InputType), OnTypeChanged));

    public static void SetType(TextBox element, InputType value)
    {
        element.SetValue(TypeProperty, value);
    }

    public static InputType GetType(TextBox element)
    {
        return (InputType)element.GetValue(TypeProperty);
    }

    private class TextSelection
    {
        public string Text { get; private set; }

        public int SelectionStart { get; private set; }

        public int SelectionLength { get; private set; }

        public TextSelection(string text, int selectionStart, int selectionLength)
        {
            Text = text;
            SelectionStart = selectionStart;
            SelectionLength = selectionLength;
        }
    }

    private static readonly DependencyProperty PreviousTextSelectionProperty =
        DependencyProperty.RegisterAttached("PreviousTextSelection", typeof(TextSelection),
        typeof(TextBox), new PropertyMetadata(default(TextSelection)));

    private static void SetPreviousTextSelection(TextBox element, TextSelection value)
    {
        element.SetValue(PreviousTextSelectionProperty, value);
    }

    private static TextSelection GetPreviousTextSelection(TextBox element)
    {
        return (TextSelection)element.GetValue(PreviousTextSelectionProperty);
    }

    private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (UIApplication.DesignMode)
            return;

        var textBox = (TextBox)d;
        textBox.TextChanged += OnTextChanged;
        textBox.SelectionChanged += OnSelectionChanged;
    }

    /// <summary>
    /// Determines whether the specified text is valid.
    /// </summary>
    /// <param name="text">The text.</param>
    /// <param name="inputType">Type of the input.</param>
    /// <returns>
    ///   <c>true</c> if the specified text is valid; otherwise, <c>false</c>.
    /// </returns>
    private static bool IsValid(string text, InputType inputType)
    {
        switch (inputType)
        {
        case InputType.PositiveInteger:
            int i;
            return int.TryParse(text, out i);
        case InputType.PositiveDecimal:
            decimal d;
            return decimal.TryParse(text, out d) && d >= 0;
        case InputType.PositiveNullableInteger:
            return text.IsNullOrEmpty() || IsValid(text, InputType.PositiveInteger);
        case InputType.PositiveNullableDecimal:
            return text.IsNullOrEmpty() || IsValid(text, InputType.PositiveDecimal);
        default:
            throw new ArgumentOutOfRangeException("inputType");
        }
    }

    private static void OnTextChanged(object sender, TextChangedEventArgs e)
    {
        var textBox = (TextBox)sender;
        var inputType = GetType(textBox);

        if (IsValid(textBox.Text, inputType))
        {
            SetPreviousTextSelection(textBox, new TextSelection(textBox.Text, textBox.SelectionStart, textBox.SelectionLength));
        }
        else
        {
            var textSelection = GetPreviousTextSelection(textBox);
            if (textSelection == null)
            {
                textBox.Text = "";
            }
            else
            {
                textBox.Text = textSelection.Text;
                textBox.SelectionStart = textSelection.SelectionStart;
                textBox.SelectionLength = textSelection.SelectionLength;
            }
        }
    }

    private static void OnSelectionChanged(object sender, RoutedEventArgs e)
    {
        var textBox = (TextBox)sender;
        SetPreviousTextSelection(textBox, new TextSelection(textBox.Text, textBox.SelectionStart, textBox.SelectionLength));
    }
}

Then use it in your xaml code (the "ui:" namespace requires to be resolved, but hey, you still have to do your homeworks :)):

<TextBox Text="{Binding MyText, Mode=TwoWay}" ui:Input.Type="PositiveNullableDecimal" />

So basically, the extender memorizes the last valid state (text+selection) and reverts it if the new result is invalid. The enum InputType can be of course extended.

Sign up to request clarification or add additional context in comments.

1 Comment

I haven't tested this solution but its sounds like its far superior to mine so +1!
1

is there any way to block an "alt + number combination?

Not really. My advice would be don't bother and see what happens.

TBH if you really want to build a Numeric input control you shouldn't be deriving from TextBox. You would derive from Control and place a TextBox in the default control template of your new control.

In fact to be really honest I'd just used the NumericUpDown in the Toolkit.

Comments

1

I got it working by using the TextChanged event here is my code...

public class NumericTextBox : TextBox
{

    int value;

    public NumericTextBox()
        : base()
    {
        this.Text = "0";
        this.TextChanged += new TextChangedEventHandler(NumericTextBox_TextChanged);
    }

    void NumericTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        int selectionStart = base.SelectionStart;
        bool changed = false;
        List<char> charList = new List<char>();
        for (int i = 0; i < base.Text.Length; i++)
        {
            if (IsValidChar(base.Text[i], i))
            {
                charList.Add(base.Text[i]);
            }
            else
            {
                if (selectionStart >= i)
                {
                    selectionStart--;
                }
                changed = true;
            }
        }
        if (changed)
        {
            string text = new string(charList.ToArray());
            this.Text = text;
            this.SelectionStart = selectionStart;
        }
        int newValue;
        if (!int.TryParse(this.Text, out newValue))
        {
            this.Text = value.ToString();
            this.SelectionStart = this.Text.Length;
        }
        else
        {
            value = newValue;
        }
    }

    private bool IsValidChar(char c, int index)
    {
        return ((c == '-' && index == 0) || c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9');
    }

    private void HandleKeyEvent(KeyEventArgs e)
    {
        e.Handled = true;
        if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
        {
            e.Handled = false;
        }
        if (e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Left || e.Key == Key.Right ||
            e.Key == Key.D0 || e.Key == Key.D1 || e.Key == Key.D2 || e.Key == Key.D3 || e.Key == Key.D4 || e.Key == Key.D5 || e.Key == Key.D6 ||
            e.Key == Key.D7 || e.Key == Key.D8 || e.Key == Key.D9 ||
            e.Key == Key.NumPad0 || e.Key == Key.NumPad1 || e.Key == Key.NumPad2 || e.Key == Key.NumPad3 || e.Key == Key.NumPad4 || e.Key == Key.NumPad5 || e.Key == Key.NumPad6 ||
            e.Key == Key.NumPad7 || e.Key == Key.NumPad8 || e.Key == Key.NumPad9)
        {
            e.Handled = false;
        }
        else if ((e.Key == Key.Subtract || (e.Key == Key.Unknown && e.PlatformKeyCode == 189)) && base.SelectionStart == 0 && (base.Text.Length == 0 || base.Text[0] != '-'))
        {
            e.Handled = false;
        }
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        HandleKeyEvent(e);
        base.OnKeyDown(e);
    }

    protected override void OnKeyUp(KeyEventArgs e)
    {
        HandleKeyEvent(e);
        base.OnKeyUp(e);
    }
}

1 Comment

This is dangerous, because it assumes a number is written with a "-" and digits between "0" and "9", which is not true for all cultures. Also you have to handle both key and text events, so this is twice the minimum job :) I'm posting an alternative below.
0

Are you just trying to prevent non-numeric text entry? A different approach described in this blog post is to create a text box filter that can be added to a normal TextBox as an attached dependency property. Either way, you will still have to validate the data after it is entered as the user could paste invalid data.

Comments

-1

Short and sweet - the Alt key is handled at a lower level then your program.

This article describes the problem in more detail while this link provides some c++ code that could help you if you really wanted to get around this issue.

1 Comment

You can't really get at this level of coding in SL.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.