문제

WPF 애플리케이션에서 숫자 값 입력을 어떻게 처리하고 있습니까?

NumericUpDown 컨트롤 없이 TextBox를 사용하고 아래 코드로 PreviewKeyDown 이벤트를 처리해 왔지만 꽤 보기 흉합니다.

타사 컨트롤에 의존하지 않고 사용자로부터 숫자 데이터를 얻는 더 우아한 방법을 찾은 사람이 있습니까?

private void NumericEditPreviewKeyDown(object sender, KeyEventArgs e)
{
    bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || e.Key == Key.Decimal;
    bool isNumeric = (e.Key >= Key.D0 && e.Key <= Key.D9) || e.Key == Key.OemPeriod;

    if ((isNumeric || isNumPadNumeric) && Keyboard.Modifiers != ModifierKeys.None)
    {
        e.Handled = true;
        return;
    }

    bool isControl = ((Keyboard.Modifiers != ModifierKeys.None && Keyboard.Modifiers != ModifierKeys.Shift)
        || e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Insert
        || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up
        || e.Key == Key.Tab
        || e.Key == Key.PageDown || e.Key == Key.PageUp
        || e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Escape
        || e.Key == Key.Home || e.Key == Key.End);

    e.Handled = !isControl && !isNumeric && !isNumPadNumeric;
}
도움이 되었습니까?

해결책

어떻습니까:

protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
    e.Handled = !AreAllValidNumericChars(e.Text);
    base.OnPreviewTextInput(e);
}

private bool AreAllValidNumericChars(string str)
{
    foreach(char c in str)
    {
        if(!Char.IsNumber(c)) return false;
    }

    return true;
}

다른 팁

이것이 내가 하는 방법이다.정규식을 사용하여 상자에 포함될 텍스트가 숫자인지 여부를 확인합니다.

Regex NumEx = new Regex(@"^-?\d*\.?\d*$");

private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    if (sender is TextBox)
    {
        string text = (sender as TextBox).Text + e.Text;
        e.Handled = !NumEx.IsMatch(text);
    }
    else
        throw new NotImplementedException("TextBox_PreviewTextInput Can only Handle TextBoxes");
}

이제 WPF와 Silverlight에는 이 작업을 수행하는 훨씬 더 좋은 방법이 있습니다.컨트롤이 속성에 바인딩된 경우 바인딩 문을 약간 변경하기만 하면 됩니다.바인딩에는 다음을 사용하세요.

<TextBox Text="{Binding Number, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>

사용자 정의 속성에서도 이 기능을 사용할 수 있습니다. 상자의 값이 유효하지 않고 컨트롤이 빨간색 테두리로 강조 표시되면 예외를 발생시키기만 하면 됩니다.빨간색 테두리의 오른쪽 상단을 클릭하면 예외 메시지가 나타납니다.

저는 사용자가 위쪽 및 아래쪽 키를 사용하여 텍스트 상자의 값을 변경할 수 있도록 연결된 속성을 사용해 왔습니다.사용하려면 그냥 사용하세요

<TextBox local:TextBoxNumbers.SingleDelta="1">100</TextBox>

이것은 실제로 이 질문에 언급된 유효성 검사 문제를 해결하지는 않지만 숫자 위/아래 컨트롤이 없는 것에 대해 내가 수행하는 작업을 해결합니다.조금만 사용해보면 예전 숫자 업/다운 컨트롤보다 오히려 더 마음에 들 수도 있겠다는 생각이 듭니다.

코드는 완벽하지는 않지만 처리해야 하는 경우를 처리합니다.

  • Up 화살, Down 화살
  • Shift + Up 화살, Shift + Down 화살
  • Page Up, Page Down
  • 제본 Converter 텍스트 속성에

Code behind

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

namespace Helpers
{
    public class TextBoxNumbers
    {    
        public static Decimal GetSingleDelta(DependencyObject obj)
        {
            return (Decimal)obj.GetValue(SingleDeltaProperty);
        }

        public static void SetSingleDelta(DependencyObject obj, Decimal value)
        {
            obj.SetValue(SingleDeltaProperty, value);
        }

        // Using a DependencyProperty as the backing store for SingleValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SingleDeltaProperty =
            DependencyProperty.RegisterAttached("SingleDelta", typeof(Decimal), typeof(TextBoxNumbers), new UIPropertyMetadata(0.0m, new PropertyChangedCallback(f)));

        public static void f(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            TextBox t = o as TextBox;

            if (t == null)
                return;

            t.PreviewKeyDown += new System.Windows.Input.KeyEventHandler(t_PreviewKeyDown);
        }

        private static Decimal GetSingleValue(DependencyObject obj)
        {
            return GetSingleDelta(obj);
        }

        private static Decimal GetDoubleValue(DependencyObject obj)
        {
            return GetSingleValue(obj) * 10;
        }

        private static Decimal GetTripleValue(DependencyObject obj)
        {
            return GetSingleValue(obj) * 100;
        }

        static void t_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            TextBox t = sender as TextBox;
            Decimal i;

            if (t == null)
                return;

            if (!Decimal.TryParse(t.Text, out i))
                return;

            switch (e.Key)
            {
                case System.Windows.Input.Key.Up:
                    if (Keyboard.Modifiers == ModifierKeys.Shift)
                        i += GetDoubleValue(t);
                    else
                        i += GetSingleValue(t);
                    break;

                case System.Windows.Input.Key.Down:
                    if (Keyboard.Modifiers == ModifierKeys.Shift)
                        i -= GetDoubleValue(t);
                    else
                        i -= GetSingleValue(t);
                    break;

                case System.Windows.Input.Key.PageUp:
                    i += GetTripleValue(t);
                    break;

                case System.Windows.Input.Key.PageDown:
                    i -= GetTripleValue(t);
                    break;

                default:
                    return;
            }

            if (BindingOperations.IsDataBound(t, TextBox.TextProperty))
            {
                try
                {
                    Binding binding = BindingOperations.GetBinding(t, TextBox.TextProperty);
                    t.Text = (string)binding.Converter.Convert(i, null, binding.ConverterParameter, binding.ConverterCulture);
                }
                catch
                {
                    t.Text = i.ToString();
                }
            }
            else
                t.Text = i.ToString();
        }
    }
}

여기에 답변으로 표시된 답변을 LINQ 표현식을 사용하여 기본적으로 2줄로 단순화하기로 결정했습니다.

e.Handled = !e.Text.All(Char.IsNumber);
base.OnPreviewTextInput(e);

나는 커스텀을 사용한다 ValidationRule 텍스트가 숫자인지 확인합니다.

public class DoubleValidation : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        if (value is string)
        {
            double number;
            if (!Double.TryParse((value as string), out number))
                return new ValidationResult(false, "Please enter a valid number");
        }

        return ValidationResult.ValidResult;
    }

그런 다음 바인딩할 때 TextBox 숫자 속성에 새 사용자 정의 클래스를 추가합니다. Binding.ValidationRules 수집.아래 예에서는 유효성 검사 규칙이 매번 확인됩니다. TextBox.Text 변화.

<TextBox>
    <TextBox.Text>
        <Binding Path="MyNumericProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:DoubleValidation/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

PreviewKeyDown 이벤트 대신 KeyDown 이벤트를 사용해 보는 것은 어떨까요?여기서 잘못된 문자를 중지할 수 있지만 모든 제어 문자가 허용됩니다.이것은 나에게 효과적인 것 같습니다.

private void NumericKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
    bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9);
    bool isNumeric =((e.Key >= Key.D0 && e.Key <= Key.D9) && (e.KeyboardDevice.Modifiers == ModifierKeys.None));
    bool isDecimal = ((e.Key == Key.OemPeriod || e.Key == Key.Decimal) && (((TextBox)sender).Text.IndexOf('.') < 0));
    e.Handled = !(isNumPadNumeric || isNumeric || isDecimal);
}
public class NumericTextBox : TextBox
{
    public NumericTextBox()
        : base()
    {
        DataObject.AddPastingHandler(this, new DataObjectPastingEventHandler(CheckPasteFormat));
    }

    private Boolean CheckFormat(string text)
    {
        short val;
        return Int16.TryParse(text, out val);
    }

    private void CheckPasteFormat(object sender, DataObjectPastingEventArgs e)
    {
        var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);

        if (isText)
        {
            var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
            if (CheckFormat(text))
            {
                return;
            }
        }

        e.CancelCommand();
    }

    protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
    {
        if (!CheckFormat(e.Text))
        {
            e.Handled = true;
        }
        else
        {
            base.OnPreviewTextInput(e);
        }
    }
}

또한 적절한 종속성 속성을 제공하여 구문 분석 동작을 사용자 지정할 수 있습니다.

이 답변 중 몇 가지 아이디어를 결합하여 NumericTextBox를 만들었습니다.

  • 소수를 처리합니다.
  • 입력 한 '-'또는 '.' 유효합니다
  • 붙여넣은 값을 처리합니다.

포함되어야 할 다른 로직이 생각나면 자유롭게 업데이트하시기 바랍니다.

public class NumericTextBox : TextBox
{
    public NumericTextBox()
    {
        DataObject.AddPastingHandler(this, OnPaste);
    }

    private void OnPaste(object sender, DataObjectPastingEventArgs dataObjectPastingEventArgs)
    {
        var isText = dataObjectPastingEventArgs.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);

        if (isText)
        {
            var text = dataObjectPastingEventArgs.SourceDataObject.GetData(DataFormats.Text) as string;
            if (IsTextValid(text))
            {
                return;
            }
        }

        dataObjectPastingEventArgs.CancelCommand();
    }

    private bool IsTextValid(string enteredText)
    {
        if (!enteredText.All(c => Char.IsNumber(c) || c == '.' || c == '-'))
        {
            return false;
        }

        //We only validation against unselected text since the selected text will be replaced by the entered text
        var unselectedText = this.Text.Remove(SelectionStart, SelectionLength);

        if (enteredText == "." && unselectedText.Contains("."))
        {
            return false;
        }

        if (enteredText == "-" && unselectedText.Length > 0)
        {
            return false;
        }

        return true;
    }

    protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
    {
        e.Handled = !IsTextValid(e.Text);
        base.OnPreviewTextInput(e);
    }
}

사용자가 데이터를 사용하기 전에 커밋하는 경우 데이터 유효성 검사를 사용해 볼 수도 있습니다.그렇게 하는 것이 열쇠를 만지작거리는 것보다 훨씬 간단하고 깔끔하다는 것을 알았습니다.

그렇지 않으면 언제든지 붙여넣기를 비활성화할 수도 있습니다!

내 버전 아르크투루스 답변, 정수/단위/십진수/바이트(색상용) 또는 사용하려는 기타 숫자 형식으로 작업하는 데 사용되는 변환 방법을 변경할 수 있으며 복사/붙여넣기에서도 작동합니다.

protected override void OnPreviewTextInput( System.Windows.Input.TextCompositionEventArgs e )
{
    try
    {
        if ( String.IsNullOrEmpty( SelectedText ) )
        {
            Convert.ToDecimal( this.Text.Insert( this.CaretIndex, e.Text ) );
        }
        else
        {
            Convert.ToDecimal( this.Text.Remove( this.SelectionStart, this.SelectionLength ).Insert( this.SelectionStart, e.Text ) );
        }
    }
    catch
    {
        // mark as handled if cannot convert string to decimal
        e.Handled = true;
    }

    base.OnPreviewTextInput( e );
}

NB테스트되지 않은 코드.

텍스트 상자를 지울 때 바인딩이 0으로 업데이트되도록 하려면 이를 기본 솔루션에 추가하세요.

protected override void OnPreviewKeyUp(System.Windows.Input.KeyEventArgs e)
{
    base.OnPreviewKeyUp(e);

    if (BindingOperations.IsDataBound(this, TextBox.TextProperty))
    {
        if (this.Text.Length == 0)
        {
            this.SetValue(TextBox.TextProperty, "0");
            this.SelectAll();
        }
    }
}

미쳤다고 부르세요. TextBox 컨트롤의 양쪽에 더하기 및 빼기 버튼을 배치하고 단순히 TextBox가 커서 포커스를 받지 못하도록 방지하여 저렴한 NumericUpDown 컨트롤을 만드는 것은 어떨까요?

private void txtNumericValue_PreviewKeyDown(object sender, KeyEventArgs e)
{
    KeyConverter converter = new KeyConverter();

    string key = converter.ConvertToString(e.Key);

    if (key != null && key.Length == 1)
    {
        e.Handled = Char.IsDigit(key[0]) == false;
    }
}

이것이 제가 찾은 가장 쉬운 기술입니다.단점은 TextBox의 상황에 맞는 메뉴가 여전히 붙여넣기를 통해 숫자가 아닌 항목을 허용한다는 것입니다.이 문제를 신속하게 해결하기 위해 간단히 속성/속성을 추가했습니다.ContextMenu="{x:Null}"을 TextBox에 추가하여 비활성화합니다.이상적이지는 않지만 내 시나리오에서는 충분합니다.

분명히 추가 허용 값을 포함하기 위해 테스트에 몇 가지 키/문자를 더 추가할 수 있습니다(예:'.', '$' 등...)

Private Sub Value1TextBox_PreviewTextInput(ByVal sender As Object, ByVal e As TextCompositionEventArgs) Handles Value1TextBox.PreviewTextInput
    Try
        If Not IsNumeric(e.Text) Then
            e.Handled = True
        End If
    Catch ex As Exception
    End Try
End Sub

나를 위해 일했습니다.

다음과 같은 것을 사용할 수 없습니까?

int numericValue = 0;

if (false == int.TryParse(yourInput, out numericValue))
{
    // handle non-numeric input
}
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
{
    string sVal = e.Text;
    int val = 0;

    if (sVal != null && sVal.Length > 0)
    {
        if (int.TryParse(sVal, out val))
        {
            e.Handled = false;
        }
        else
        {
            e.Handled = true;
        }
    }
}

다음과 같은 변환기를 사용할 수도 있습니다.

public class IntegerFormatConverter : IValueConverter
{
    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int result;
        int.TryParse(value.ToString(), out result);
        return result;
    }

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int result;
        int.TryParse(value.ToString(), out result);
        return result;
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top