66

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;
}
4

17 に答える 17

58

どうですか:

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;
}
于 2008-08-11T13:47:12.253 に答える
14

これが私のやり方です。正規表現を使用して、ボックスに表示されるテキストが数値かどうかを確認します。

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}"/>

これはカスタム プロパティでも使用できることに注意してください。ボックス内の値が無効な場合に例外をスローするだけで、コントロールが赤い境界線で強調表示されます。赤い枠の右上をクリックすると、例外メッセージがポップアップ表示されます。

于 2009-12-17T18:43:18.287 に答える
11

添付のプロパティを使用して、ユーザーが上下のキーを使用してテキストボックスの値を変更できるようにしました。それを使用するには、あなたはただ使用します

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

これは、この質問で言及されている検証の問題には実際には対処していませんが、数値のアップ/ダウンコントロールがないことについて私が行っていることには対処しています。少し使ってみると、古い数値のアップ/ダウンコントロールよりも実際に気に入っているのではないかと思います。

コードは完璧ではありませんが、処理に必要なケースを処理します。

  • Up矢印、Down矢印
  • Shift + Up矢印、Shift + Down矢印
  • Page UpPage Down
  • Convertertextプロパティのバインド

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();
        }
    }
}
于 2008-08-26T13:20:52.643 に答える
11

ここで回答としてマークされた返信を、LINQ式を使用して基本的に2行に簡素化することにしました。

e.Handled = !e.Text.All(Char.IsNumber);
base.OnPreviewTextInput(e);
于 2010-05-13T03:02:38.127 に答える
4

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);
}
于 2011-04-05T02:10:35.127 に答える
4

カスタムを使用して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;
    }

次に、a を数値プロパティにバインドすると、新しいカスタム クラスがコレクションTextBoxに追加されます。Binding.ValidationRules以下の例では、検証ルールがTextBox.Text変更されるたびにチェックされます。

<TextBox>
    <TextBox.Text>
        <Binding Path="MyNumericProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:DoubleValidation/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>
于 2012-01-25T05:33:47.673 に答える
3
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);
        }
    }
}

さらに、適切な依存関係プロパティを提供することで、解析動作をカスタマイズできます。

于 2010-04-20T06:47:01.890 に答える
3

これらの回答のいくつかからのアイデアを組み合わせて、 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);
    }
}
于 2013-05-29T07:02:26.503 に答える
2

Add this to the main solution to make sure the the binding is updated to zero when the textbox is cleared.

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();
        }
    }
}
于 2010-05-04T10:35:57.733 に答える
2

ユーザーがデータを使用する前にコミットする場合は、データ検証を使用することもできます。私が見つけたそれを行うことは、キーをいじるよりもかなり単純でクリーンです。

それ以外の場合は、いつでも貼り付けを無効にすることもできます!

于 2008-08-12T05:14:52.347 に答える
2

私のバージョンのArcturusの回答では、int / uint / decimal / byte (色の場合) またはその他の使用したい数値形式を操作するために使用される変換方法を変更でき、コピー / 貼り付けでも機能します

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 テストされていないコード。

于 2011-02-03T17:47:58.597 に答える
1
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

私のために働いた。

于 2010-01-23T11:26:35.977 に答える
1
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;
        }
    }
}
于 2010-01-29T22:55:52.970 に答える
1

次のようなものだけを使用することはできませんか?

int numericValue = 0;

if (false == int.TryParse(yourInput, out numericValue))
{
    // handle non-numeric input
}
于 2008-09-11T22:13:11.153 に答える
1
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 のコンテキスト メニューで Paste を介して数値以外を使用できることです。これをすばやく解決するために、属性/プロパティ: ContextMenu="{x:Null}" を TextBox に追加して無効にしました。理想的ではありませんが、私のシナリオでは十分です。

明らかに、テストにさらにいくつかのキー/文字を追加して、追加の許容値を含めることができます (例: '.'、'$' など...)

于 2009-02-18T15:40:15.910 に答える
1

頭がおかしくなりそうですが、TextBox コントロールの両側にプラス ボタンとマイナス ボタンを配置して、TextBox がカーソル フォーカスを受け取らないようにして、独自の安価な NumericUpDown コントロールを作成してみませんか?

于 2008-08-08T17:27:04.073 に答える
1

次のようなコンバーターも使用できます。

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;
    }
}
于 2012-08-08T05:14:09.840 に答える