Làm cách nào để có được TextBox chỉ chấp nhận đầu vào số trong WPF?


334

Tôi đang tìm cách chấp nhận chữ số và dấu thập phân, nhưng không có dấu.

Tôi đã xem các mẫu bằng cách sử dụng điều khiển NumericUpDown cho Windows Forms và mẫu điều khiển tùy chỉnh NumericUpDown này của Microsoft . Nhưng cho đến nay có vẻ như NumericUpDown (được hỗ trợ bởi WPF hay không) sẽ không cung cấp chức năng mà tôi muốn. Cách ứng dụng của tôi được thiết kế, không ai trong tâm trí của họ sẽ muốn lộn xộn với các mũi tên. Chúng không có ý nghĩa thực tế nào, trong bối cảnh ứng dụng của tôi.

Vì vậy, tôi đang tìm kiếm một cách đơn giản để làm cho TextBox WPF tiêu chuẩn chỉ chấp nhận các ký tự mà tôi muốn. Điều này có thể không? Có thực tế không?

Câu trả lời:


417

Thêm một sự kiện nhập văn bản xem trước. Giống như vậy : <TextBox PreviewTextInput="PreviewTextInput" />.

Sau đó, bên trong thiết lập e.Handlednếu văn bản không được phép.e.Handled = !IsTextAllowed(e.Text);

Tôi sử dụng một biểu thức chính quy đơn giản IsTextAllowedđể xem liệu tôi có nên cho phép những gì họ đã nhập không. Trong trường hợp của tôi, tôi chỉ muốn cho phép số, dấu chấm và dấu gạch ngang.

private static readonly Regex _regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
private static bool IsTextAllowed(string text)
{
    return !_regex.IsMatch(text);
}

Nếu bạn muốn ngăn việc dán dữ liệu không chính xác, hãy kết nối DataObject.Pastingsự kiện DataObject.Pasting="TextBoxPasting"như được hiển thị ở đây (đoạn trích):

// Use the DataObject.Pasting Handler 
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
    if (e.DataObject.GetDataPresent(typeof(String)))
    {
        String text = (String)e.DataObject.GetData(typeof(String));
        if (!IsTextAllowed(text))
        {
            e.CancelCommand();
        }
    }
    else
    {
        e.CancelCommand();
    }
}

5
Regex của bạn không cho phép ký hiệu khoa học (1e5) nếu điều đó quan trọng.
Ron Warholic

14
Lưu ý rằng câu trả lời này chỉ kiểm tra những gì bạn nhập, vì vậy bạn có thể nhập 3-.3
David Sykes vào

153
Điểm của câu trả lời không phải là xác định Regex hoàn hảo, đó là chỉ ra cách sử dụng WPF để lọc những gì ai đó gõ.
Ray

27
[Space] không kích hoạt sự kiện PreviewTextInput.
peterG

5
Một cái gì đó giống như double.TryParse()có thể sẽ được thực hiện với cùng một số dòng và linh hoạt hơn.
Thomas Weller

190

Trình xử lý sự kiện đang xem trước nhập văn bản. Ở đây một biểu thức chính quy chỉ khớp với kiểu nhập văn bản nếu nó không phải là số và sau đó nó không được thực hiện để nhập hộp văn bản.

Nếu bạn chỉ muốn các chữ cái thì thay thế biểu thức thông thường như [^a-zA-Z].

XAML

<TextBox Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>

TẬP TIN XAML.CS

using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
    Regex regex = new Regex("[^0-9]+");
    e.Handled = regex.IsMatch(e.Text);
}

1
Trình xử lý sự kiện là xem trước văn bản nhập. Ở đây biểu thức chính quy chỉ khớp với kiểu nhập văn bản nếu nó không phải là số, thì nó không được thực hiện để nhập hộp văn bản. Nếu bạn chỉ muốn bảng chữ cái thì thay thế biểu thức thông thường là [^ a-zA-Z].
Kishor

Còn số, số thập phân và toán tử thì sao?
Jason Ebersey

Xin vui lòng cho tôi biết làm thế nào để sử dụng nó khi khai báo trong một số lớp STATIC khác và áp dụng cho hộp văn bản?
SHEKHAR SHETE

3
Tôi thích cái này hơn câu trả lời thực tế, ngắn gọn và đơn giản. Câu trả lời thực tế cần hai phương pháp để có được kết quả giống nhau.
Sliver

1
@Jagd Câu trả lời được đề xuất là phân chia mối quan tâm tốt hơn. Tuy nhiên, bạn cũng có thể đặt nhiều hộp văn bản cho xác thực này. chỉ cần thêm PreviewTextInput = "NumberValidationTextBox". (giống như câu trả lời khác!)
Sliver

84

Tôi đã sử dụng một số thứ đã có ở đây và tự tạo ra một khuynh hướng cho nó bằng cách sử dụng một hành vi để tôi không phải truyền mã này trong suốt hàng tấn Lượt xem ...

public class AllowableCharactersTextBoxBehavior : Behavior<TextBox>
{
    public static readonly DependencyProperty RegularExpressionProperty =
         DependencyProperty.Register("RegularExpression", typeof(string), typeof(AllowableCharactersTextBoxBehavior),
         new FrameworkPropertyMetadata(".*"));
    public string RegularExpression
    {
        get
        {
            return (string)base.GetValue(RegularExpressionProperty);
        }
        set
        {
            base.SetValue(RegularExpressionProperty, value);
        }
    }

    public static readonly DependencyProperty MaxLengthProperty =
        DependencyProperty.Register("MaxLength", typeof(int), typeof(AllowableCharactersTextBoxBehavior),
        new FrameworkPropertyMetadata(int.MinValue));
    public int MaxLength
    {
        get
        {
            return (int)base.GetValue(MaxLengthProperty);
        }
        set
        {
            base.SetValue(MaxLengthProperty, value);
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewTextInput += OnPreviewTextInput;
        DataObject.AddPastingHandler(AssociatedObject, OnPaste);
    }

    private void OnPaste(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(DataFormats.Text))
        {
            string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));

            if (!IsValid(text, true))
            {
                e.CancelCommand();
            }
        }
        else
        {
            e.CancelCommand();
        }
    }

    void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        e.Handled = !IsValid(e.Text, false);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
        DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
    }

    private bool IsValid(string newText, bool paste)
    {
        return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);
    }

    private bool ExceedsMaxLength(string newText, bool paste)
    {
        if (MaxLength == 0) return false;

        return LengthOfModifiedText(newText, paste) > MaxLength;
    }

    private int LengthOfModifiedText(string newText, bool paste)
    {
        var countOfSelectedChars = this.AssociatedObject.SelectedText.Length;
        var caretIndex = this.AssociatedObject.CaretIndex;
        string text = this.AssociatedObject.Text;

        if (countOfSelectedChars > 0 || paste)
        {
            text = text.Remove(caretIndex, countOfSelectedChars);
            return text.Length + newText.Length;
        }
        else
        {
            var insert = Keyboard.IsKeyToggled(Key.Insert);

            return insert && caretIndex < text.Length ? text.Length : text.Length + newText.Length;
        }
    }
}

Đây là mã xem có liên quan:

<TextBox MaxLength="50" TextWrapping="Wrap" MaxWidth="150" Margin="4"
 Text="{Binding Path=FileNameToPublish}" >
     <interactivity:Interaction.Behaviors>
         <v:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9.\-]+$" MaxLength="50" />
     </interactivity:Interaction.Behaviors>
</TextBox>

1
Lấy cảm hứng từ giải pháp tuyệt vời này, tôi đã thực hiện một số cải tiến. Vui lòng kiểm tra nó dưới đây trong chủ đề.
Alex Klaus

2
Chào. Tôi biết điều này hơi muộn nhưng tôi đang cố gắng thực hiện nhưng tôi vẫn bị lỗi. Tôi đoán tôi đang thiếu một số tài liệu tham khảo. Có bất kỳ ý nghĩa nào được gõ vào ngoài các mặc định sau khi bạn tạo một lớp không?
Ưu đãi

1
@ Offerer Có, đảm bảo bao gồm xmlns: tương tác = " schemas.microsoft.com/expression/2010/interactivity " ở đầu cửa sổ xaml của bạn.
WiteCastle

Biểu hiện bây giờ đã lỗi thời. Mặc dù cách tiếp cận này rõ ràng, nhưng nó sử dụng mã không còn được duy trì.
Robert Baker

1
Vì vậy, nếu bạn chỉnh sửa hàm IsValid để trả về! ExceedsMaxLạng (newText, dán) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RoutExpression); sau đó điều này sẽ đánh giá toàn bộ chuỗi. Btw - Thích lựa chọn này với các hành vi !!
Rogala

59

Đây là một giải pháp cải tiến cho câu trả lời của WilP . Những cải tiến của tôi là:

  • Cải thiện hành vi trên các nút DelBackspace
  • Đã thêm thuộc EmptyValuetính, nếu chuỗi trống không phù hợp
  • Đã sửa một số lỗi chính tả
/// <summary>
///     Regular expression for Textbox with properties: 
///         <see cref="RegularExpression"/>, 
///         <see cref="MaxLength"/>,
///         <see cref="EmptyValue"/>.
/// </summary>
public class TextBoxInputRegExBehaviour : Behavior<TextBox>
{
    #region DependencyProperties
    public static readonly DependencyProperty RegularExpressionProperty =
        DependencyProperty.Register("RegularExpression", typeof(string), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(".*"));

    public string RegularExpression
    {
        get { return (string)GetValue(RegularExpressionProperty); }
        set { SetValue(RegularExpressionProperty, value); }
    }

    public static readonly DependencyProperty MaxLengthProperty =
        DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBoxInputRegExBehaviour),
                                        new FrameworkPropertyMetadata(int.MinValue));

    public int MaxLength
    {
        get { return (int)GetValue(MaxLengthProperty); }
        set { SetValue(MaxLengthProperty, value); }
    }

    public static readonly DependencyProperty EmptyValueProperty =
        DependencyProperty.Register("EmptyValue", typeof(string), typeof(TextBoxInputRegExBehaviour), null);

    public string EmptyValue
    {
        get { return (string)GetValue(EmptyValueProperty); }
        set { SetValue(EmptyValueProperty, value); }
    }
    #endregion

    /// <summary>
    ///     Attach our behaviour. Add event handlers
    /// </summary>
    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PreviewTextInput += PreviewTextInputHandler;
        AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler;
        DataObject.AddPastingHandler(AssociatedObject, PastingHandler);
    }

    /// <summary>
    ///     Deattach our behaviour. remove event handlers
    /// </summary>
    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.PreviewTextInput -= PreviewTextInputHandler;
        AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler;
        DataObject.RemovePastingHandler(AssociatedObject, PastingHandler);
    }

    #region Event handlers [PRIVATE] --------------------------------------

    void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
    {
        string text;
        if (this.AssociatedObject.Text.Length < this.AssociatedObject.CaretIndex)
            text = this.AssociatedObject.Text;
        else
        {
            //  Remaining text after removing selected text.
            string remainingTextAfterRemoveSelection;

            text = TreatSelectedText(out remainingTextAfterRemoveSelection)
                ? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text)
                : AssociatedObject.Text.Insert(this.AssociatedObject.CaretIndex, e.Text);
        }

        e.Handled = !ValidateText(text);
    }

    /// <summary>
    ///     PreviewKeyDown event handler
    /// </summary>
    void PreviewKeyDownHandler(object sender, KeyEventArgs e)
    {
        if (string.IsNullOrEmpty(this.EmptyValue))
            return;

        string text = null;

        // Handle the Backspace key
        if (e.Key == Key.Back)
        {
            if (!this.TreatSelectedText(out text))
            {
                if (AssociatedObject.SelectionStart > 0)
                    text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1);
            }
        }
        // Handle the Delete key
        else if (e.Key == Key.Delete)
        {
            // If text was selected, delete it
            if (!this.TreatSelectedText(out text) && this.AssociatedObject.Text.Length > AssociatedObject.SelectionStart)
            {
                // Otherwise delete next symbol
                text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1);
            }
        }

        if (text == string.Empty)
        {
            this.AssociatedObject.Text = this.EmptyValue;
            if (e.Key == Key.Back)
                AssociatedObject.SelectionStart++;
            e.Handled = true;
        }
    }

    private void PastingHandler(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(DataFormats.Text))
        {
            string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));

            if (!ValidateText(text))
                e.CancelCommand();
        }
        else
            e.CancelCommand();
    }
    #endregion Event handlers [PRIVATE] -----------------------------------

    #region Auxiliary methods [PRIVATE] -----------------------------------

    /// <summary>
    ///     Validate certain text by our regular expression and text length conditions
    /// </summary>
    /// <param name="text"> Text for validation </param>
    /// <returns> True - valid, False - invalid </returns>
    private bool ValidateText(string text)
    {
        return (new Regex(this.RegularExpression, RegexOptions.IgnoreCase)).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength);
    }

    /// <summary>
    ///     Handle text selection
    /// </summary>
    /// <returns>true if the character was successfully removed; otherwise, false. </returns>
    private bool TreatSelectedText(out string text)
    {
        text = null;
        if (AssociatedObject.SelectionLength <= 0) 
            return false;

        var length = this.AssociatedObject.Text.Length;
        if (AssociatedObject.SelectionStart >= length)
            return true;

        if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length)
            AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart;

        text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength);
        return true;
    }
    #endregion Auxiliary methods [PRIVATE] --------------------------------
}

Cách sử dụng khá đơn giản:

<i:Interaction.Behaviors>
    <behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>

1
Giải pháp này là khá tốt. Nhưng bạn đã mắc một lỗi nhỏ: Khi không cài đặt MaxLengthconditon của bạn (this.MaxLength == 0 || text.Length <= this.MaxLength)luôn trả về falsekhi kiểm tra văn bản mới. Điều này tốt hơn nên (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)kể từ khi bạn đặt int.MinValuelàm giá trị mặc định cho MaxLength.
Christoph Meißner

1
Cảm ơn bạn @Christoph, vâng, bạn đúng. Tôi đã sửa đổi câu trả lời của mình.
Alex Klaus

@AlexKlaus điều này hoạt động tuyệt vời cho đến khi tôi thêm UpdateSourceTrigger=PropertyChangedvào ràng buộc. Bất kỳ ý tưởng làm thế nào để làm cho mã này hoạt động khi thay đổi UpdateSourceTriggerđược đặt thành PropertyChanged? Cảm ơn bạn đã chia sẻ mã này.
Thiếu niên

32

Đây là một cách rất đơn giản và dễ dàng để làm điều này bằng cách sử dụng MVVM.

Liên kết textBox của bạn với một thuộc tính số nguyên trong mô hình xem và điều này sẽ hoạt động như một viên ngọc ... nó thậm chí sẽ hiển thị xác thực khi một số nguyên không được nhập vào hộp văn bản.

Mã XAML:

<TextBox x:Name="contactNoTxtBox"  Text="{Binding contactNo}" />

Xem mã mô hình:

private long _contactNo;
public long contactNo
{
    get { return _contactNo; }
    set
    {
        if (value == _contactNo)
            return;
        _contactNo = value;
        OnPropertyChanged();
    }
}

Nhưng câu hỏi chứa "Tôi đang tìm cách chấp nhận chữ số và dấu thập phân" . Là dấu thập phân được chấp nhận cho câu trả lời này?
Peter Mortensen

Tôi đã cố gắng thay đổi longđể float, nhưng nó đã không làm việc hoàn toàn đúng với xác nhận ngay lập tức. Tôi đã thêm vào UpdateSourceTrigger="PropertyChanged"liên kết, vì vậy nó sẽ thực hiện xác nhận khi mỗi ký tự được nhập và không còn có thể gõ '.' trong TextBox trừ khi có một ký tự không hợp lệ (phải nhập "1x.234" rồi xóa 'x'). Nó cũng cảm thấy một chút chậm chạp trong chế độ này. Điều này dường như được sử dụng System.Number.ParseSingle()để thực hiện công việc, vì vậy nó chấp nhận một loạt các ký hiệu.
fadden

@wolle có thể không được bình chọn vì nó không giải thích cách xác thực hoạt động.
Paul McCarthy

26

Thêm vào một QUY TẮC XÁC ĐỊNH để khi văn bản thay đổi, hãy kiểm tra để xác định xem dữ liệu có phải là số không và nếu có, cho phép xử lý tiếp tục và nếu không, sẽ nhắc người dùng chỉ chấp nhận dữ liệu số trong trường đó.

Đọc thêm về Xác thực trong Windows Presentation Foundation


6
Đây không thực sự là một câu trả lời cho các tiêu chuẩn SO.
Robert Baker

Đây có vẻ là cách .net để làm điều đó.
Telemat

1
Các sự chính xác câu trả lời: xác nhận nên có ít mô hình viene hoặc cấp mô hình. Ngoài ra, bạn có thể chỉ cần liên kết với một loại số như doublevà điều đó đã cung cấp cho bạn một xác nhận tiêu chuẩn.

24

Bộ công cụ WPF mở rộng có một: NumericUpDown nhập mô tả hình ảnh ở đây


Tôi đã thử điều khiển này nhưng nó gây ra sự cố khi sử dụng công cụ quay vòng với UpdateSourceTrigger = PropertyChanged và nói chung người dùng khó nhập ký hiệu khoa học.
Menno Deij - van Rijswijk

5
Xin lưu ý rằng NumericUpDownbây giờ đã lỗi thời. bạn có thể sử dụng DecimalUpDowntừ bộ công cụ cập nhật Phiên bản cộng đồng WPF Toolkit ™ mở rộng
itho

20

Cũng có thể đơn giản thực hiện quy tắc xác thực và áp dụng quy tắc này cho TextBox:

  <TextBox>
    <TextBox.Text>
      <Binding Path="OnyDigitInput" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
          <conv:OnlyDigitsValidationRule />
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>

Với việc thực hiện quy tắc như sau (sử dụng cùng một Regex như đề xuất trong các câu trả lời khác):

public class OnlyDigitsValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        var validationResult = new ValidationResult(true, null);

        if(value != null)
        {
            if (!string.IsNullOrEmpty(value.ToString()))
            {
                var regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
                var parsingOk = !regex.IsMatch(value.ToString());
                if (!parsingOk)
                {
                    validationResult = new ValidationResult(false, "Illegal Characters, Please Enter Numeric Value");
                }
            }
        }

        return validationResult;
    }
}

Nếu bạn muốn nhập chữ số thập phân, không trả về "hợp lệ" khi Văn bản kết thúc bằng "." Vui lòng tham khảo stackoverflow.com/a/27838893/417939
YantingChen

14

Ở đây tôi có một giải pháp đơn giản lấy cảm hứng từ câu trả lời của Ray . Điều này là đủ để xác định bất kỳ hình thức số.

Giải pháp này cũng có thể dễ dàng sửa đổi nếu bạn chỉ muốn số dương, giá trị nguyên hoặc giá trị chính xác đến số vị trí thập phân tối đa, v.v.


Như được đề xuất trong câu trả lời của Ray , trước tiên bạn cần thêm một PreviewTextInputsự kiện:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Sau đó đặt đoạn mã sau vào mã phía sau:

private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
    var textBox = sender as TextBox;
    // Use SelectionStart property to find the caret position.
    // Insert the previewed text into the existing text in the textbox.
    var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);

    double val;
    // If parsing is successful, set Handled to false
    e.Handled = !double.TryParse(fullText, out val);
}

4
tôi thích câu trả lời này rất nhiều, đơn giản và hiệu quả +
Pulle

thần và đơn giản nhưng xấu xí đến nỗi nó cho phép không gian
Momo

2
Điều này vẫn cho phép ai đó chỉ cần dán một chuỗi vào hộp văn bản
FCin

8

Tôi cho phép số bàn phím số và khoảng lùi:

    private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        int key = (int)e.Key;

        e.Handled = !(key >= 34 && key <= 43 || 
                      key >= 74 && key <= 83 || 
                      key == 2);
    }

8
Tôi khuyên bạn nên sử dụng các giá trị enum thay vì Số ma thuật :var keyEnum = (System.Windows.Input.Key) e.Key; e.Handled = !(keyEnum >= System.Windows.Input.Key.D0 && keyEnum <= System.Windows.Input.Key.D9 || keyEnum >= System.Windows.Input.Key.NumPad0 && keyEnum <= System.Windows.Input.Key.NumPad9 || keyEnum == System.Windows.Input.Key.Back);
itho

7

Tôi sẽ cho rằng:

  1. TextBox của bạn mà bạn muốn cho phép nhập số chỉ có thuộc tính Text ban đầu được đặt thành một số giá trị số hợp lệ (ví dụ: 2.7172).

  2. Hộp văn bản của bạn là con của cửa sổ chính của bạn

  3. Cửa sổ chính của bạn là của lớp Window1

  4. Tên TextBox của bạn là sốTB

Ý kiến ​​cơ bản:

  1. Thêm: private string previousText;vào lớp cửa sổ chính của bạn (Window1)

  2. Thêm: previousText = numericTB.Text;vào hàm tạo cửa sổ chính của bạn

  3. Tạo một trình xử lý cho sự kiện numTB.TextChanged giống như thế này:

    private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
    {
        double num = 0;
        bool success = double.TryParse(((TextBox)sender).Text, out num);
        if (success & num >= 0)
            previousText = ((TextBox)sender).Text;
        else
            ((TextBox)sender).Text = previousText;
    }

Điều này sẽ tiếp tục đặt trướcText thành numTB.Text miễn là nó hợp lệ và đặt numTB.Text thành giá trị hợp lệ cuối cùng của nó nếu người dùng viết một cái gì đó mà bạn không thích. Tất nhiên, đây chỉ là ý tưởng cơ bản, và nó chỉ là "chống lại kẻ ngốc", không phải là "bằng chứng ngốc". Nó không xử lý trường hợp người dùng lộn xộn với không gian, ví dụ. Vì vậy, đây là một giải pháp hoàn chỉnh mà tôi nghĩ là "bằng chứng ngốc", và nếu tôi sai xin vui lòng cho tôi biết:

  1. Nội dung của tệp Window1.xaml của bạn:

    <Window x:Class="IdiotProofNumericTextBox.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <TextBox Height="30" Width="100" Name="numericTB" TextChanged="numericTB_TextChanged"/>
        </Grid>
    </Window>
  2. Nội dung tệp Window.xaml.cs của bạn:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace IdiotProofNumericTextBox
    {
        public partial class Window1 : Window
        {
            private string previousText;
    
            public Window1()
            {
                InitializeComponent();
                previousText = numericTB.Text;
            }
    
            private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
            {
                if (string.IsNullOrEmpty(((TextBox)sender).Text))
                    previousText = "";
                else
                {
                    double num = 0;
                    bool success = double.TryParse(((TextBox)sender).Text, out num);
                    if (success & num >= 0)
                    {
                        ((TextBox)sender).Text.Trim();
                        previousText = ((TextBox)sender).Text;
                    }
                    else
                    {
                        ((TextBox)sender).Text = previousText;
                        ((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length;
                    }
                }
            }
        }
    }

Và đó là nó. Nếu bạn có nhiều TextBox thì tôi khuyên bạn nên tạo CustomControl kế thừa từ TextBox, vì vậy bạn có thể gói trước Texext và numTB_TextChanged trong một tệp riêng.


Wow điều này thật tuyệt! Làm thế nào tôi có thể cho phép một biểu tượng tiêu cực ở phía trước mặc dù?
theNoobGuy

6

Đây là mã duy nhất cần thiết:

void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}

Điều này chỉ cho phép các số được nhập vào hộp văn bản.

Để cho phép dấu thập phân hoặc dấu trừ, bạn có thể thay đổi biểu thức chính quy thành [^0-9.-]+.


1
Giải pháp rất tốt ngoại trừ một cú đánh nhỏ: Nó sẽ không ngăn bạn vào khoảng trắng, vì chúng không kích hoạt sự kiện PreviewTextInput.
Tim Pohlmann

Backspace không bắn nó là tốt.
Cầu thủ chạy cánh Sendon

6

Nếu bạn không muốn viết nhiều mã để thực hiện một chức năng cơ bản (tôi không biết tại sao mọi người thực hiện các phương thức dài), bạn chỉ có thể làm điều này:

  1. Thêm không gian tên:

    using System.Text.RegularExpressions;
  2. Trong XAML, đặt thuộc tính TextChanged:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
  3. Trong WPF theo phương thức txt1_TextChanged, thêm Regex.Replace:

    private void txt1_TextChanged(object sender, TextChangedEventArgs e)
    {
        txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
    }

2
Sẽ sạch sẽ hơn nhiều khi sử dụng Hành vi hoặc Đính kèm. Không có sự thay đổi mã phía sau trong mỗi chế độ xem / cho mỗi hộp văn bản
Sir Rufo

Có thể làm việc nhưng xấu xí vì củ cà rốt sẽ nhảy vào vị trí phía trước của hộp văn bản
Momo

6

Một cách tiếp cận khác sẽ sử dụng một hành vi đính kèm, tôi đã triển khai lớp TextBoxHelper tùy chỉnh của mình , có thể được sử dụng trên các hộp văn bản trên toàn dự án của tôi. Bởi vì tôi cho rằng việc đăng ký các sự kiện cho mọi hộp văn bản và trong mỗi tệp XAML riêng lẻ cho mục đích này có thể tốn thời gian.

Lớp TextBoxHelper mà tôi triển khai có các tính năng sau:

  • Chỉ lọc và chấp nhận các số ở định dạng Double , Int , UintNatural
  • Lọc và chỉ chấp nhận Thậm chí hoặc Odd số
  • Xử lý xử lý dán sự kiện để ngăn dán văn bản không hợp lệ vào hộp văn bản số của chúng tôi
  • Có thể đặt Giá trị mặc định sẽ được sử dụng để ngăn dữ liệu không hợp lệ làm lần chụp cuối cùng bằng cách đăng ký vào hộp văn bản Sự kiện TextChanged

Đây là cách triển khai lớp TextBoxHelper:

public static class TextBoxHelper
{
    #region Enum Declarations

    public enum NumericFormat
    {
        Double,
        Int,
        Uint,
        Natural
    }

    public enum EvenOddConstraint
    {
        All,
        OnlyEven,
        OnlyOdd
    }

    #endregion

    #region Dependency Properties & CLR Wrappers

    public static readonly DependencyProperty OnlyNumericProperty =
        DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper),
            new PropertyMetadata(null, DependencyPropertiesChanged));
    public static void SetOnlyNumeric(TextBox element, NumericFormat value) =>
        element.SetValue(OnlyNumericProperty, value);
    public static NumericFormat GetOnlyNumeric(TextBox element) =>
        (NumericFormat) element.GetValue(OnlyNumericProperty);


    public static readonly DependencyProperty DefaultValueProperty =
        DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper),
            new PropertyMetadata(null, DependencyPropertiesChanged));
    public static void SetDefaultValue(TextBox element, string value) =>
        element.SetValue(DefaultValueProperty, value);
    public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty);


    public static readonly DependencyProperty EvenOddConstraintProperty =
        DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper),
            new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged));
    public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) =>
        element.SetValue(EvenOddConstraintProperty, value);
    public static EvenOddConstraint GetEvenOddConstraint(TextBox element) =>
        (EvenOddConstraint)element.GetValue(EvenOddConstraintProperty);

    #endregion

    #region Dependency Properties Methods

    private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is TextBox textBox))
            throw new Exception("Attached property must be used with TextBox.");

        switch (e.Property.Name)
        {
            case "OnlyNumeric":
            {
                var castedValue = (NumericFormat?) e.NewValue;

                if (castedValue.HasValue)
                {
                    textBox.PreviewTextInput += TextBox_PreviewTextInput;
                    DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler);
                }
                else
                {
                    textBox.PreviewTextInput -= TextBox_PreviewTextInput;
                    DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler);
                }

                break;
            }

            case "DefaultValue":
            {
                var castedValue = (string) e.NewValue;

                if (castedValue != null)
                {
                    textBox.TextChanged += TextBox_TextChanged;
                }
                else
                {
                    textBox.TextChanged -= TextBox_TextChanged;
                }

                break;
            }
        }
    }

    #endregion

    private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        var textBox = (TextBox)sender;

        string newText;

        if (textBox.SelectionLength == 0)
        {
            newText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
        }
        else
        {
            var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength);

            newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text);
        }

        var evenOddConstraint = GetEvenOddConstraint(textBox);

        switch (GetOnlyNumeric(textBox))
        {
            case NumericFormat.Double:
            {
                if (double.TryParse(newText, out double number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;
                    }
                }
                else
                    e.Handled = true;

                break;
            }

            case NumericFormat.Int:
            {
                if (int.TryParse(newText, out int number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;
                    }
                }
                else
                    e.Handled = true;

                break;
            }

            case NumericFormat.Uint:
            {
                if (uint.TryParse(newText, out uint number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                e.Handled = true;
                            else
                                e.Handled = false;

                            break;
                    }
                }
                else
                    e.Handled = true;

                break;
            }

            case NumericFormat.Natural:
            {
                if (uint.TryParse(newText, out uint number))
                {
                    if (number == 0)
                        e.Handled = true;
                    else
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.Handled = true;
                                else
                                    e.Handled = false;

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.Handled = true;
                                else
                                    e.Handled = false;

                                break;
                        }
                    }
                }
                else
                    e.Handled = true;

                break;
            }
        }
    }

    private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e)
    {
        var textBox = (TextBox)sender;

        if (e.DataObject.GetDataPresent(typeof(string)))
        {
            var clipboardText = (string) e.DataObject.GetData(typeof(string));

            var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText);

            var evenOddConstraint = GetEvenOddConstraint(textBox);

            switch (GetOnlyNumeric(textBox))
            {
                case NumericFormat.Double:
                {
                    if (double.TryParse(newText, out double number))
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.CancelCommand();

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.CancelCommand();

                                break;
                        }
                    }
                    else
                        e.CancelCommand();

                    break;
                }

                case NumericFormat.Int:
                {
                    if (int.TryParse(newText, out int number))
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.CancelCommand();

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.CancelCommand();


                                break;
                        }
                    }
                    else
                        e.CancelCommand();

                    break;
                }

                case NumericFormat.Uint:
                {
                    if (uint.TryParse(newText, out uint number))
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    e.CancelCommand();

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    e.CancelCommand();


                                break;
                        }
                    }
                    else
                        e.CancelCommand();

                    break;
                }

                case NumericFormat.Natural:
                {
                    if (uint.TryParse(newText, out uint number))
                    {
                        if (number == 0)
                            e.CancelCommand();
                        else
                        {
                            switch (evenOddConstraint)
                            {
                                case EvenOddConstraint.OnlyEven:

                                    if (number % 2 != 0)
                                        e.CancelCommand();

                                    break;

                                case EvenOddConstraint.OnlyOdd:

                                    if (number % 2 == 0)
                                        e.CancelCommand();

                                    break;
                            }
                        }
                    }
                    else
                    {
                        e.CancelCommand();
                    }

                    break;
                }
            }
        }
        else
        {
            e.CancelCommand();
        }
    }

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

        var defaultValue = GetDefaultValue(textBox);

        var evenOddConstraint = GetEvenOddConstraint(textBox);

        switch (GetOnlyNumeric(textBox))
        {
            case NumericFormat.Double:
            {
                if (double.TryParse(textBox.Text, out double number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                textBox.Text = defaultValue;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                textBox.Text = defaultValue;

                            break;
                    }
                }
                else
                    textBox.Text = defaultValue;

                break;
            }

            case NumericFormat.Int:
            {
                if (int.TryParse(textBox.Text, out int number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                textBox.Text = defaultValue;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                textBox.Text = defaultValue;

                            break;
                    }
                }
                else
                    textBox.Text = defaultValue;

                break;
            }

            case NumericFormat.Uint:
            {
                if (uint.TryParse(textBox.Text, out uint number))
                {
                    switch (evenOddConstraint)
                    {
                        case EvenOddConstraint.OnlyEven:

                            if (number % 2 != 0)
                                textBox.Text = defaultValue;

                            break;

                        case EvenOddConstraint.OnlyOdd:

                            if (number % 2 == 0)
                                textBox.Text = defaultValue;

                            break;
                    }
                }
                else
                    textBox.Text = defaultValue;

                break;
            }

            case NumericFormat.Natural:
            {
                if (uint.TryParse(textBox.Text, out uint number))
                {
                    if(number == 0)
                        textBox.Text = defaultValue;
                    else
                    {
                        switch (evenOddConstraint)
                        {
                            case EvenOddConstraint.OnlyEven:

                                if (number % 2 != 0)
                                    textBox.Text = defaultValue;

                                break;

                            case EvenOddConstraint.OnlyOdd:

                                if (number % 2 == 0)
                                    textBox.Text = defaultValue;

                                break;
                        }
                    }
                }
                else
                {
                    textBox.Text = defaultValue;
                }

                break;
            }
        }
    }
}

Và đây là một số ví dụ về cách sử dụng dễ dàng của nó:

<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
         viewHelpers:TextBoxHelper.DefaultValue="1"/>

Hoặc là

<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
         viewHelpers:TextBoxHelper.DefaultValue="3"
         viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>

Lưu ý rằng TextBoxHelper của tôi nằm trong bí danh viewHelpers xmlns.

Tôi hy vọng rằng việc thực hiện này giúp giảm bớt một số công việc của người khác :)


1
Điều này thật tuyệt khi sử dụng hộp văn bản bên trong DataTemplate, cảm ơn!
NucS

@NucS Bạn rất được chào đón NucS thân yêu
Amir Mahdi Nassiri

3
Câu trả lời tuyệt vời nhưng tôi thấy phương pháp của bạn khó đọc. Có lẽ bạn nên chia chúng thành những cái nhỏ hơn. Xem độ dài lý tưởng của một phương pháp cho bạn là gì?
Anthony

Cảm ơn bạn đã phản hồi mang tính xây dựng @Anthony
Amir Mahdi Nassiri

4
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

trong sự kiện keydown xem trước của hộp văn bản.


3
Không cho phép backspace mặc dù.
sventevit

2
Backspace là 2, tab là 3
Daniel

6
-1 bởi vì theo kinh nghiệm của tôi, loại mánh khóe thông minh này cuối cùng đã cắn vào mông bạn, như một số nhà bình luận khác đã lưu ý.
DonkeyMaster

Mũi tên trái là 23, Mũi tên phải là 25.
Aaron

4
PreviewTextInput += (s, e) =>
{
    e.Handled = !e.Text.All(char.IsDigit);
};

2
điều này cũng sẽ không chấp nhận dấu chấm ., vì e.Textchỉ trả về ký tự đầu vào cuối cùng và một dấu chấm sẽ không IsDigitkiểm tra.
Anthony

4

Đối với những người tìm kiếm cách triển khai nhanh chóng và rất đơn giản cho loại vấn đề này chỉ sử dụng số nguyên và số thập phân, trong tệp XAML của bạn, hãy thêm một thuộc PreviewTextInputtính vào tệp của bạn TextBoxvà sau đó trong tệp xaml.cs của bạn sử dụng:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !char.IsDigit(e.Text.Last()) && !e.Text.Last() == '.';
}

Việc kiểm tra toàn bộ chuỗi mỗi lần là không cần thiết, trừ khi, như những người khác đã đề cập, bạn đang làm gì đó với ký hiệu khoa học (mặc dù, nếu bạn thêm một số ký tự như 'e', ​​một biểu tượng / ký tự regex đơn giản là thực sự đơn giản và minh họa trong các câu trả lời khác). Nhưng đối với các giá trị dấu phẩy động đơn giản, giải pháp này sẽ đủ.

Được viết dưới dạng một lớp lót với biểu thức lambda:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == '.');

3

Chúng tôi có thể xác nhận sự kiện thay đổi hộp văn bản. Việc thực hiện sau đây ngăn đầu vào nhấn phím ngoài số và một dấu thập phân.

private void textBoxNumeric_TextChanged(object sender, TextChangedEventArgs e) 
{         
      TextBox textBox = sender as TextBox;         
      Int32 selectionStart = textBox.SelectionStart;         
      Int32 selectionLength = textBox.SelectionLength;         
      String newText = String.Empty;         
      int count = 0;         
      foreach (Char c in textBox.Text.ToCharArray())         
      {             
         if (Char.IsDigit(c) || Char.IsControl(c) || (c == '.' && count == 0))             
         {                 
            newText += c;                 
            if (c == '.')                     
              count += 1;             
         }         
     }         
     textBox.Text = newText;         
     textBox.SelectionStart = selectionStart <= textBox.Text.Length ? selectionStart :        textBox.Text.Length;     
} 

nó không hoạt động với tôi :)
david2020

3

Còn cái này thì sao? Hoạt động tốt cho tôi. Hy vọng tôi đã không bỏ lỡ bất kỳ trường hợp cạnh ...

MyTextBox.PreviewTextInput += (sender, args) =>
{
    if (!int.TryParse(args.Text, out _))
    {
        args.Handled = true;
    }
};

DataObject.AddPastingHandler(MyTextBox, (sender, args) =>
{
    var isUnicodeText = args.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
    if (!isUnicodeText)
    {
        args.CancelCommand();
    }

    var data = args.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
    if (!int.TryParse(data, out _))
    {
        args.CancelCommand();
    }
});

công việc này cảm ơn bạn
Aljohn Yamaro

2

Trong Windows Forms thật dễ dàng; bạn có thể thêm một sự kiện cho KeyPress và mọi thứ hoạt động dễ dàng. Tuy nhiên, trong WPF sự kiện đó không có ở đó. Nhưng có một cách dễ dàng hơn nhiều cho nó.

TextBox WPF có sự kiện TextChanged chung cho mọi thứ. Nó bao gồm dán, gõ và bất cứ điều gì có thể đến với tâm trí của bạn.

Vì vậy, bạn có thể làm một cái gì đó như thế này:

XAML:

<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>

MÃ ẨN:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
    string s = Regex.Replace(((TextBox)sender).Text, @"[^\d.]", "");
    ((TextBox)sender).Text = s;
}

Điều này cũng chấp nhận ., nếu bạn không muốn, chỉ cần xóa nó khỏi regexcâu lệnh @[^\d].

Lưu ý : Sự kiện này có thể được sử dụng trên nhiều TextBox vì nó sử dụng senderVăn bản của đối tượng. Bạn chỉ viết sự kiện một lần và có thể sử dụng nó cho nhiều TextBox.


2

Bây giờ tôi biết câu hỏi này có một câu trả lời được chấp nhận , nhưng cá nhân tôi, tôi thấy nó hơi khó hiểu và tôi tin rằng nó sẽ dễ dàng hơn thế. Vì vậy, tôi sẽ cố gắng chứng minh làm thế nào tôi có thể làm việc tốt nhất có thể:

Trong Windows Forms , có một sự kiện được gọi KeyPresslà hoàn toàn tốt cho loại nhiệm vụ này. Nhưng điều đó không tồn tại trong WPF , vì vậy thay vào đó, chúng tôi sẽ sử dụng PreviewTextInputsự kiện này. Ngoài ra, để xác thực, tôi tin rằng người ta có thể sử dụng một foreachvòng lặp để textbox.Textkiểm tra và kiểm tra xem nó có khớp không ;) điều kiện, nhưng thành thật mà nói, đây là những biểu thức thông thường dành cho.

Một điều nữa trước khi chúng ta đi sâu vào mật mã . Để sự kiện bị sa thải, người ta có thể làm hai việc:

  1. Sử dụng XAML để báo cho chương trình biết chức năng nào sẽ gọi: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Làm điều đó trong trường Loadedhợp của biểu mẫu (mà textBox nằm trong): textBox.PreviewTextInput += onlyNumeric;

Tôi nghĩ rằng phương pháp thứ hai tốt hơn bởi vì trong những tình huống như thế này, bạn sẽ hầu như được yêu cầu áp dụng cùng một điều kiện ( regex) cho nhiều hơn một TextBoxbạn không muốn lặp lại chính mình! .

Cuối cùng, đây là cách bạn làm điều đó:

private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
    string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
    Regex regex = new Regex(onlyNumeric);
    e.Handled = !regex.IsMatch(e.Text);
}

2

Đây là phiên bản của tôi về nó. Nó dựa trên một ValidatingTextBoxlớp cơ sở chỉ hoàn tác những gì đã được thực hiện nếu nó không "hợp lệ". Nó hỗ trợ dán, cắt, xóa, xóa lùi, +, - v.v.

Đối với số nguyên 32 bit, có một lớp Int32TextBox chỉ so sánh với một số nguyên. Tôi cũng đã thêm các lớp xác nhận dấu phẩy động.

public class ValidatingTextBox : TextBox
{
    private bool _inEvents;
    private string _textBefore;
    private int _selectionStart;
    private int _selectionLength;

    public event EventHandler<ValidateTextEventArgs> ValidateText;

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (_inEvents)
            return;

        _selectionStart = SelectionStart;
        _selectionLength = SelectionLength;
        _textBefore = Text;
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        if (_inEvents)
            return;

        _inEvents = true;
        var ev = new ValidateTextEventArgs(Text);
        OnValidateText(this, ev);
        if (ev.Cancel)
        {
            Text = _textBefore;
            SelectionStart = _selectionStart;
            SelectionLength = _selectionLength;
        }
        _inEvents = false;
    }

    protected virtual void OnValidateText(object sender, ValidateTextEventArgs e) => ValidateText?.Invoke(this, e);
}

public class ValidateTextEventArgs : CancelEventArgs
{
    public ValidateTextEventArgs(string text) => Text = text;

    public string Text { get; }
}

public class Int32TextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !int.TryParse(e.Text, out var value);
}

public class Int64TextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !long.TryParse(e.Text, out var value);
}

public class DoubleTextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !double.TryParse(e.Text, out var value);
}

public class SingleTextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !float.TryParse(e.Text, out var value);
}

public class DecimalTextBox : ValidatingTextBox
{
    protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !decimal.TryParse(e.Text, out var value);
}

Lưu ý 1: Khi sử dụng liên kết WPF, bạn phải đảm bảo rằng bạn sử dụng lớp phù hợp với loại thuộc tính bị ràng buộc nếu không, điều này có thể dẫn đến kết quả lạ.

Lưu ý 2: Khi sử dụng các lớp dấu phẩy động với liên kết WPF, hãy đảm bảo ràng buộc sử dụng văn hóa hiện tại để khớp với phương thức TryPude mà tôi đã sử dụng.



1

Sử dụng:

Private Sub DetailTextBox_PreviewTextInput( _
  ByVal sender As Object, _
  ByVal e As System.Windows.Input.TextCompositionEventArgs) _
  Handles DetailTextBox.PreviewTextInput

    If _IsANumber Then
        If Not Char.IsNumber(e.Text) Then
            e.Handled = True
        End If
    End If
End Sub

Một lời giải thích sẽ theo thứ tự.
Peter Mortensen

1

Tôi đã làm việc với một hộp không liên kết cho một dự án đơn giản mà tôi đang thực hiện, vì vậy tôi không thể sử dụng phương pháp ràng buộc tiêu chuẩn. Do đó, tôi đã tạo ra một bản hack đơn giản mà những người khác có thể thấy khá tiện dụng bằng cách mở rộng điều khiển TextBox hiện có:

namespace MyApplication.InterfaceSupport
{
    public class NumericTextBox : TextBox
    {


        public NumericTextBox() : base()
        {
            TextChanged += OnTextChanged;
        }


        public void OnTextChanged(object sender, TextChangedEventArgs changed)
        {
            if (!String.IsNullOrWhiteSpace(Text))
            {
                try
                {
                    int value = Convert.ToInt32(Text);
                }
                catch (Exception e)
                {
                    MessageBox.Show(String.Format("{0} only accepts numeric input.", Name));
                    Text = "";
                }
            }
        }


        public int? Value
        {
            set
            {
                if (value != null)
                {
                    this.Text = value.ToString();
                }
                else 
                    Text = "";
            }
            get
            {
                try
                {
                    return Convert.ToInt32(this.Text);
                }
                catch (Exception ef)
                {
                    // Not numeric.
                }
                return null;
            }
        }
    }
}

Rõ ràng, đối với một loại nổi, bạn sẽ muốn phân tích nó như là một float và như vậy. Các nguyên tắc tương tự được áp dụng.

Sau đó, trong tệp XAML, bạn cần bao gồm không gian tên có liên quan:

<UserControl x:Class="MyApplication.UserControls.UnParameterisedControl"
             [ Snip ]
             xmlns:interfaceSupport="clr-namespace:MyApplication.InterfaceSupport"
             >

Sau đó, bạn có thể sử dụng nó như một điều khiển thông thường:

<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >

1

Sau khi sử dụng một số giải pháp ở đây một thời gian, tôi đã tự mình phát triển hoạt động tốt cho thiết lập MVVM của mình. Lưu ý rằng nó không năng động như một số người khác theo nghĩa vẫn cho phép người dùng nhập các ký tự sai, nhưng nó chặn họ nhấn nút và do đó làm bất cứ điều gì. Điều này phù hợp với chủ đề của tôi về các nút màu xám khi các hành động không thể được thực hiện.

Tôi có một TextBoxngười dùng phải nhập một số trang tài liệu sẽ được in:

<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>

... với tài sản ràng buộc này:

private string _numberPagesToPrint;
public string NumberPagesToPrint
{
    get { return _numberPagesToPrint; }
    set
    {
        if (_numberPagesToPrint == value)
        {
            return;
        }

        _numberPagesToPrint = value;
        OnPropertyChanged("NumberPagesToPrint");
    }
}

Tôi cũng có một nút:

<Button Template="{DynamicResource CustomButton_Flat}" Content="Set"
        Command="{Binding SetNumberPagesCommand}"/>

... với lệnh này ràng buộc:

private RelayCommand _setNumberPagesCommand;
public ICommand SetNumberPagesCommand
{
    get
    {
        if (_setNumberPagesCommand == null)
        {
            int num;
            _setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(),
                () => Int32.TryParse(NumberPagesToPrint, out num));
        }

        return _setNumberPagesCommand;
    }
}

Và sau đó có phương pháp SetNumberOfPages(), nhưng nó không quan trọng cho chủ đề này. Nó hoạt động tốt trong trường hợp của tôi vì tôi không phải thêm bất kỳ mã nào vào tệp phía sau mã của View và nó cho phép tôi kiểm soát hành vi bằng cách sử dụng thuộc Commandtính.



1

Trong ứng dụng WPF, bạn có thể xử lý việc này bằng cách xử lý TextChangedsự kiện:

void arsDigitTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
    Regex regex = new Regex("[^0-9]+");
    bool handle = regex.IsMatch(this.Text);
    if (handle)
    {
        StringBuilder dd = new StringBuilder();
        int i = -1;
        int cursor = -1;
        foreach (char item in this.Text)
        {
            i++;
            if (char.IsDigit(item))
                dd.Append(item);
            else if(cursor == -1)
                cursor = i;
        }
        this.Text = dd.ToString();

        if (i == -1)
            this.SelectionStart = this.Text.Length;
        else
            this.SelectionStart = cursor;
    }
}

1

Đối với các nhà phát triển muốn các trường văn bản của họ chỉ chấp nhận các số không dấu như cổng ổ cắm, v.v.

WPF

<TextBox PreviewTextInput="Port_PreviewTextInput" MaxLines="1"/>

C #

private void Port_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !int.TryParse(e.Text, out int x);
}

2
Lưu ý rằng nếu bạn thực sự muốn sử dụng phương pháp này với trường cổng socket; Bạn cần kiểm tra xem số nguyên nhỏ hơn hoặc bằng 65535. Nếu nó lớn hơn thì đó không phải là một cổng hợp lệ. Ngoài ra, cài đặt TextBox.MaxLengththành 5sẽ giúp lập trình hoặc trong XAML .
Beyondo

0

Đây là những gì tôi sẽ sử dụng để có được một hộp văn bản WPF chấp nhận các chữ số và dấu thập phân:

class numericTextBox : TextBox
{
    protected override void OnKeyDown(KeyEventArgs e)
    {
        bool b = false;
        switch (e.Key)
        {
            case Key.Back: b = true; break;
            case Key.D0: b = true; break;
            case Key.D1: b = true; break;
            case Key.D2: b = true; break;
            case Key.D3: b = true; break;
            case Key.D4: b = true; break;
            case Key.D5: b = true; break;
            case Key.D6: b = true; break;
            case Key.D7: b = true; break;
            case Key.D8: b = true; break;
            case Key.D9: b = true; break;
            case Key.OemPeriod: b = true; break;
        }
        if (b == false)
        {
            e.Handled = true;
        }
        base.OnKeyDown(e);
    }
}

Đặt mã vào một tệp lớp mới, thêm

using System.Windows.Controls;
using System.Windows.Input;

ở đầu tập tin và xây dựng giải pháp. Điều khiển numTextBox sau đó sẽ xuất hiện ở đầu hộp công cụ.


1
Xem giải pháp NHIỀU dễ dàng hơn trước đó bằng NumberValidationTextBox và các biểu thức thông thường. Chuyện này thật vớ vẩn.
Scott Shaw-Smith

@ ScottShaw-Smith Có thể giải pháp được chấp nhận là ít mã hơn, nhưng nó không nhanh hơn giải pháp này. Luôn có một số dự án đòi hỏi nhiều sức mạnh xử lý hơn là sử dụng regex.
Beyondo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.