ฉันจะรับกล่องข้อความเพื่อรับเฉพาะอินพุตที่เป็นตัวเลขใน WPF ได้อย่างไร


335

ฉันกำลังมองหาที่จะยอมรับตัวเลขและจุดทศนิยม แต่ไม่มีสัญญาณ

ฉันมองไปที่กลุ่มตัวอย่างใช้การควบคุม NumericUpDown สำหรับ Windows แบบฟอร์มและตัวอย่างของการควบคุม NumericUpDown ที่กำหนดเองจากไมโครซอฟท์นี้ แต่จนถึงตอนนี้ดูเหมือนว่า NumericUpDown (รองรับโดย WPF หรือไม่) จะไม่ให้ฟังก์ชั่นที่ฉันต้องการ วิธีการออกแบบแอปพลิเคชันของฉันไม่มีใครในใจที่ถูกต้องจะไปยุ่งกับลูกศร พวกเขาไม่สมเหตุสมผลในบริบทของใบสมัครของฉัน

ดังนั้นฉันกำลังมองหาวิธีง่ายๆในการสร้างกล่องข้อความ WPF มาตรฐานยอมรับเฉพาะอักขระที่ฉันต้องการ เป็นไปได้ไหม มันใช้งานได้จริงหรือ

คำตอบ:


418

เพิ่มเหตุการณ์ป้อนข้อความตัวอย่าง <TextBox PreviewTextInput="PreviewTextInput" />เพื่อต้องการ:

จากนั้นเข้าไปข้างในเพื่อตั้งค่าe.Handledถ้าไม่อนุญาตให้ใช้ข้อความe.Handled = !IsTextAllowed(e.Text);

ฉันใช้วิธีการ regex อย่างง่ายIsTextAllowedเพื่อดูว่าฉันควรอนุญาตสิ่งที่พวกเขาพิมพ์หรือไม่ ในกรณีของฉันฉันต้องการอนุญาตตัวเลขจุดและเครื่องหมายขีดกลางเท่านั้น

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

หากคุณต้องการป้องกันการวางข้อมูลที่ไม่ถูกต้องเชื่อมโยงDataObject.Pastingเหตุการณ์DataObject.Pasting="TextBoxPasting"ดังแสดงในที่นี้ (คัดลอกโค้ด):

// 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 ของคุณไม่อนุญาตให้ใช้เครื่องหมายทางวิทยาศาสตร์ (1e5) หากเป็นสิ่งสำคัญ
Ron Warholic

14
โปรดทราบว่าคำตอบนี้จะตรวจสอบสิ่งที่คุณพิมพ์เท่านั้นดังนั้นคุณสามารถป้อน 3-.3
David Sykes

153
จุดคำตอบไม่ได้ระบุ Regex ที่สมบูรณ์แบบมันคือการแสดงวิธีการใช้ WPF เพื่อกรองสิ่งที่มีคนประเภท
เรย์

27
[Space] ไม่เปิดใช้งานเหตุการณ์ PreviewTextInput
peterG

5
บางสิ่งเช่นdouble.TryParse()นี้อาจถูกนำไปใช้กับจำนวนบรรทัดที่เท่ากันและมีความยืดหยุ่นมากกว่า
โทมัสเวลเลอร์

190

ตัวจัดการเหตุการณ์กำลังแสดงตัวอย่างข้อความ นี่คือนิพจน์ทั่วไปที่ตรงกับการป้อนข้อความเฉพาะในกรณีที่ไม่ใช่ตัวเลขจากนั้นจะไม่ถูกนำไปใส่ในกล่องข้อความ

[^a-zA-Z]หากคุณต้องการตัวอักษรแล้วแทนที่การแสดงออกปกติเป็น

XAML

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

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
ตัวจัดการเหตุการณ์คือการป้อนข้อความตัวอย่าง ที่นี่การแสดงออกปกติตรงกับการป้อนข้อความเฉพาะในกรณีที่มันไม่ได้เป็นตัวเลขแล้วมันจะไม่ทำกับกล่องข้อความรายการ หากคุณต้องการตัวอักษรเท่านั้นให้แทนที่นิพจน์ปกติเป็น [^ a-zA-Z]
Kishor

ตัวเลขทศนิยมและตัวดำเนินการเกี่ยวกับอะไร
Jason Ebersey

โปรดแจ้งให้ฉันทราบวิธีใช้เมื่อประกาศในคลาส STATIC อื่นและใช้กับกล่องข้อความ
SHEKHAR SHETE

3
ฉันชอบสิ่งนี้มากกว่าคำตอบจริงสั้นและเรียบง่าย คำตอบที่แท้จริงต้องใช้สองวิธีเพื่อให้ได้ผลลัพธ์เดียวกัน
Sliver

1
@Jagd คำตอบที่แนะนำคือการแยกความกังวลที่ดีกว่า อย่างไรก็ตามคุณสามารถตั้งค่ากล่องข้อความได้มากในการตรวจสอบนี้เช่นกัน เพียงเพิ่มตัวอย่าง PreviewTextInput = "NumberValidationTextBox" (เช่นเดียวกับคำตอบอื่น ๆ !)
เศษไม้

84

ฉันใช้สิ่งที่มีอยู่แล้วในที่นี้และใช้การกระเพื่อมของฉันเองโดยใช้พฤติกรรมดังนั้นฉันจึงไม่ต้องเผยแพร่รหัสนี้ตลอดการดู ...

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

นี่คือรหัสมุมมองที่เกี่ยวข้อง:

<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
แรงบันดาลใจจากโซลูชันที่ยอดเยี่ยมนี้ฉันใช้การปรับปรุงบางอย่าง โปรดตรวจสอบด้านล่างในหัวข้อ
Alex Klaus

2
สวัสดี ฉันรู้ว่ามันช้าไปหน่อย แต่ฉันพยายามที่จะใช้มัน แต่ฉันยังคงได้รับข้อผิดพลาด ฉันเดาว่าฉันไม่มีข้อมูลอ้างอิงบางอย่าง มีผู้ใดบ้างที่ตั้งใจจะพิมพ์เป็นอย่างอื่นนอกเหนือจากค่าเริ่มต้นหลังจากที่คุณสร้างชั้นเรียนหรือไม่?
เสนอ

1
@Offer ใช่ตรวจสอบให้แน่ใจว่าได้รวม xmlns: interactivity = " schemas.microsoft.com/expression/2010/interactivity " ที่ด้านบนของหน้าต่าง xaml ของคุณ
WiteCastle

นิพจน์ล้าสมัยแล้ว ในขณะที่วิธีการนี้สะอาด แต่ก็ใช้รหัสที่ไม่ได้รับการบำรุงรักษาอีกต่อไป
Robert Baker

1
ดังนั้นหากคุณแก้ไขฟังก์ชัน IsValid เพื่อส่งคืน! ExceedsMaxLength (newText, paste) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), Regular Expression); แล้วนี่จะประเมินสตริงทั้งหมด Btw - รักตัวเลือกนี้พร้อมกับพฤติกรรม !!
Rogala

59

นี่เป็นคำตอบที่ดีขึ้นของคำตอบของWilP การปรับปรุงของฉันคือ:

  • ปรับปรุงพฤติกรรมบนปุ่มDelและBackspace
  • เพิ่มEmptyValueคุณสมบัติแล้วถ้าสตริงว่างไม่เหมาะสม
  • แก้ไขคำผิดเล็กน้อย
/// <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] --------------------------------
}

การใช้งานค่อนข้างตรงไปตรงมา:

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

1
วิธีนี้ดีกว่านะ แต่คุณทำผิดพลาดเล็กน้อย: เมื่อไม่ได้ตั้งค่าMaxLengthconditon ของคุณ(this.MaxLength == 0 || text.Length <= this.MaxLength)กลับมาเสมอfalseเมื่อทดสอบข้อความใหม่ ที่ดีกว่านี้ควรจะเป็น(this.MaxLength == int.MinValue || text.Length <= this.MaxLength)ตั้งแต่คุณตั้งเป็นค่าเริ่มต้นสำหรับint.MinValue MaxLength
Christoph Meißner

1
ขอบคุณ @Christoph ใช่คุณพูดถูก ฉันแก้ไขคำตอบแล้ว
Alex Klaus

@AlexKlaus มันใช้งานได้ดีจนกระทั่งฉันเพิ่มUpdateSourceTrigger=PropertyChangedการเชื่อมโยง แนวคิดใดที่จะทำให้โค้ดนี้ทำงานเมื่อเปลี่ยนการUpdateSourceTriggerตั้งค่าเป็นPropertyChanged? ขอบคุณที่แบ่งปันรหัสนี้
จูเนียร์

32

นี่เป็นวิธีที่ง่ายและสะดวกในการทำเช่นนี้โดยใช้ MVVM

ผูกกล่องข้อความของคุณด้วยคุณสมบัติจำนวนเต็มในรูปแบบมุมมองและสิ่งนี้จะทำงานเหมือนอัญมณี ... มันจะแสดงการตรวจสอบความถูกต้องเมื่อป้อนจำนวนที่ไม่ใช่จำนวนเต็มลงในกล่องข้อความ

รหัส XAML:

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

ดูรหัสรุ่น:

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

แต่คำถามที่มีอยู่"ฉันกำลังมองหาที่จะยอมรับและตัวเลขจุดทศนิยม" จุดทศนิยมได้รับการยอมรับสำหรับคำตอบนี้หรือไม่?
Peter Mortensen

ฉันพยายามเปลี่ยนlongเป็นfloatแต่มันก็ไม่ทำงานค่อนข้างถูกต้องด้วยการตรวจสอบทันที ฉันเพิ่มUpdateSourceTrigger="PropertyChanged"การรวมดังนั้นมันจะทำการตรวจสอบความถูกต้องตามที่ตัวละครแต่ละตัวถูกพิมพ์และไม่สามารถพิมพ์ '.' อีกต่อไป ในกล่องข้อความเว้นแต่มีอักขระที่ผิดกฎหมายอยู่ (ต้องพิมพ์ "1x.234" แล้วลบ 'x') นอกจากนี้ยังให้ความรู้สึกอืดเล็กน้อยในโหมดนี้ สิ่งนี้ดูเหมือนจะใช้System.Number.ParseSingle()ในการทำงานดังนั้นจึงยอมรับความหลากหลายของสัญกรณ์
ทำให้โกรธที่

@wolle อาจไม่ได้รับการโหวตเพราะไม่ได้อธิบายว่าการตรวจสอบการทำงานเป็นอย่างไร
Paul McCarthy

26

เพิ่ม VALIDATION RULE เพื่อให้เมื่อข้อความเปลี่ยนแปลงตรวจสอบเพื่อดูว่าข้อมูลเป็นตัวเลขหรือไม่และอนุญาตการประมวลผลเพื่อดำเนินการต่อหรือไม่หากไม่ให้ผู้ใช้แจ้งให้ทราบว่ามีเพียงข้อมูลตัวเลขที่ได้รับการยอมรับในฟิลด์นั้น

อ่านเพิ่มเติมในการตรวจสอบใน Windows Presentation Foundation


6
นี่ไม่ใช่คำตอบสำหรับมาตรฐาน SO จริงๆ
Robert Baker

นี่น่าจะเป็นวิธี. net ในการทำมัน
Telemat

1
เป็นที่ถูกต้องคำตอบ: การตรวจสอบควรจะอยู่ในรูปแบบ Viene หรือระดับรุ่น ยิ่งไปกว่านั้นคุณสามารถผูกกับประเภทที่เป็นตัวเลขdoubleและที่ให้การตรวจสอบมาตรฐานกับคุณอยู่แล้ว

24

ชุดเครื่องมือ WPF ที่ถูกครอบครองมีหนึ่ง: NumericUpDown ป้อนคำอธิบายรูปภาพที่นี่


ฉันได้ลองใช้ตัวควบคุมนี้แล้ว แต่มันทำให้เกิดปัญหาเมื่อใช้สปินเนอร์กับ UpdateSourceTrigger = PropertyChanged และโดยทั่วไปแล้วเป็นเรื่องยากสำหรับผู้ใช้ในการป้อนสัญลักษณ์ทางวิทยาศาสตร์
Menno Deij - van Rijswijk

5
โปรดแจ้งให้ทราบว่าNumericUpDownขณะนี้ล้าสมัยแล้ว คุณสามารถใช้DecimalUpDownจากเครื่องมือที่มีการปรับปรุงขยาย WPF Toolkit ™ Community Edition
itsho

20

สามารถใช้กฎการตรวจสอบความถูกต้องและนำไปใช้กับกล่องข้อความ:

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

ด้วยการใช้กฎดังต่อไปนี้ (ใช้ Regex เดียวกันกับที่เสนอในคำตอบอื่น ๆ ):

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

หากคุณต้องการป้อนตัวเลขทศนิยมห้ามส่งคืน "ถูกต้อง" เมื่อข้อความลงท้ายด้วย " โปรดดูstackoverflow.com/a/27838893/417939
YantingChen

14

นี่ฉันมีวิธีที่ง่ายแรงบันดาลใจจากคำตอบของเรย์ นี่ควรจะเพียงพอที่จะระบุรูปแบบหมายเลขใด ๆ

โซลูชันนี้ยังสามารถแก้ไขได้อย่างง่ายดายหากคุณต้องการเพียงจำนวนบวกค่าจำนวนเต็มหรือค่าที่ถูกต้องกับจำนวนทศนิยมสูงสุดเป็นต้น


ตามที่แนะนำในคำตอบของ Rayคุณต้องเพิ่มPreviewTextInputกิจกรรมก่อน:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

จากนั้นใส่รหัสต่อไปนี้ในโค้ดด้านหลัง:

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
ฉันชอบคำตอบนี้มากง่ายและมีประสิทธิภาพ +
Pulle

พระเจ้าและเรียบง่าย แต่มันน่าเกลียดที่จะช่วยให้มีที่ว่าง
Momo

2
สิ่งนี้ยังช่วยให้ใครบางคนเพียงแค่วางสตริงลงในกล่องข้อความ
FCin

8

ฉันอนุญาตให้ใช้แป้นตัวเลขและ backspace:

    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
ฉันขอแนะนำให้ใช้ค่า enum แทนMagic Numbers :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);
itsho

7

ฉันจะสมมติว่า:

  1. กล่องข้อความของคุณที่คุณต้องการอนุญาตให้ป้อนตัวเลขเท่านั้นตั้งค่าคุณสมบัติข้อความเริ่มต้นเป็นค่าตัวเลขที่ถูกต้อง (ตัวอย่างเช่น 2.7172)

  2. กล่องข้อความของคุณเป็นลูกของหน้าต่างหลักของคุณ

  3. หน้าต่างหลักของคุณคือคลาส Window1

  4. ชื่อกล่องข้อความของคุณคือตัวเลข

แนวคิดพื้นฐาน:

  1. เพิ่ม: private string previousText;ไปยังคลาสหน้าต่างหลักของคุณ (Window1)

  2. เพิ่ม: previousText = numericTB.Text;ไปยังตัวสร้างหน้าต่างหลักของคุณ

  3. สร้างตัวจัดการสำหรับกิจกรรม numericTB.TextChanged ให้เป็นดังนี้:

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

การดำเนินการนี้จะทำให้การตั้งค่า previousText เป็น numericTB.Text ตราบเท่าที่ถูกต้องและตั้งค่า numericTB.Text เป็นค่าที่ใช้ได้ครั้งสุดท้ายหากผู้ใช้เขียนสิ่งที่คุณไม่ชอบ แน่นอนว่านี่เป็นเพียงแนวคิดพื้นฐานและเป็นเพียง "ความทนทานต่อคนบ้า" ไม่ใช่ "หลักฐานที่งี่เง่า" มันไม่จัดการกรณีที่ผู้ใช้ยุ่งกับช่องว่างตัวอย่างเช่น ดังนั้นนี่คือทางออกที่สมบูรณ์ซึ่งฉันคิดว่าเป็น "หลักฐานคนงี่เง่า" และถ้าฉันผิดโปรดบอกฉัน:

  1. เนื้อหาของไฟล์ Window1.xaml ของคุณ:

    <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. เนื้อหาของไฟล์ Window.xaml.cs ของคุณ:

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

และนั่นคือมัน หากคุณมี TextBox จำนวนมากฉันขอแนะนำให้สร้าง CustomControl ที่สืบทอดมาจาก TextBox ดังนั้นคุณจึงสามารถตัดคำก่อนหน้าข้อความและ numericTB_TextChanged ในไฟล์แยกต่างหาก


ว้าวนี่เยี่ยมมาก! ฉันจะอนุญาตให้ใช้สัญลักษณ์ลบในด้านหน้าได้อย่างไร
theNoobGuy

6

นี่เป็นรหัสเดียวที่จำเป็น:

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

อนุญาตให้ป้อนตัวเลขลงในกล่องข้อความเท่านั้น

[^0-9.-]+เพื่อให้จุดทศนิยมหรือเครื่องหมายลบคุณสามารถเปลี่ยนการแสดงออกปกติไป


1
วิธีแก้ปัญหาที่ดีมากยกเว้น nitpick เล็กน้อยหนึ่งอัน: มันจะไม่หยุดคุณจากการเข้าไปในช่องว่างเนื่องจากสิ่งเหล่านี้จะไม่ทำให้เกิดเหตุการณ์
Tim Pohlmann

Backspace ไม่ทำงานเช่นกัน
Winger Sendon

6

หากคุณไม่ต้องการเขียนโค้ดจำนวนมากเพื่อทำฟังก์ชั่นพื้นฐาน (ฉันไม่รู้ว่าทำไมผู้คนถึงใช้วิธีการแบบยาว) คุณสามารถทำสิ่งนี้ได้:

  1. เพิ่มเนมสเปซ:

    using System.Text.RegularExpressions;
  2. ใน XAML ตั้งค่าคุณสมบัติ TextChanged:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
  3. ใน WPF ภายใต้วิธี txt1_TextChanged ให้เพิ่มRegex.Replace:

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

2
มันสะอาดกว่าการใช้ Behavior หรือ AttachedProperty ไม่มีการโกงรหัสในทุกมุมมอง / สำหรับทุกกล่องข้อความ
Sir Rufo

อาจทำงานได้ แต่น่าเกลียดเพราะแครอทจะกระโดดไปที่ตำแหน่งด้านหน้าของกล่องข้อความ
Momo

6

อีกวิธีหนึ่งคือใช้พฤติกรรมที่แนบมาฉันใช้คลาสTextBoxHelper ที่กำหนดเองซึ่งสามารถใช้กับกล่องข้อความทั่วโครงการของฉัน เพราะฉันคิดว่าการสมัครสมาชิกกิจกรรมสำหรับทุก textboxes และในไฟล์ XAML แต่ละไฟล์สำหรับวัตถุประสงค์นี้อาจใช้เวลานาน

คลาส TextBoxHelper ที่ฉันใช้มีคุณสมบัติเหล่านี้:

  • การกรองและยอมรับตัวเลขเฉพาะในคู่ , Int , uintและธรรมชาติรูปแบบ
  • การกรองและการยอมรับเท่านั้นแม้หรือคี่ตัวเลข
  • การจัดการวางตัวจัดการเหตุการณ์เพื่อป้องกันการวางข้อความที่ไม่ถูกต้องลงในกล่องข้อความที่เป็นตัวเลขของเรา
  • สามารถตั้งค่าเริ่มต้นซึ่งจะใช้ในการป้องกันข้อมูลที่ไม่ถูกต้องเป็นนัดสุดท้ายโดยการสมัครเป็นสมาชิก TextChanged เหตุการณ์

นี่คือการใช้งานของ 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;
            }
        }
    }
}

และนี่คือตัวอย่างของการใช้งานที่ง่าย:

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

หรือ

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

โปรดทราบว่า TextBoxHelper ของฉันอยู่ในนามแฝง viewHelpers xmlns

ฉันหวังว่าการใช้งานนี้จะทำให้งานของคนอื่นง่ายขึ้น :)


1
นี่เป็นสิ่งที่ยอดเยี่ยมเมื่อใช้กล่องข้อความใน DataTemplate ขอบคุณ!
NucS

3
คำตอบที่ดี แต่ฉันพบว่าวิธีการของคุณอ่านยาก อาจเป็นไปได้ว่าคุณควรแบ่งมันออกเป็นชิ้นเล็ก ๆ ดูความยาวในอุดมคติของวิธีการสำหรับคุณคืออะไร?
Anthony

ขอบคุณสำหรับคำติชมที่สร้างสรรค์ @Anthony
Amir Mahdi Nassiri

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

ในเหตุการณ์ keydown แสดงตัวอย่างของกล่องข้อความ


3
ไม่อนุญาต backspace
sventevit

2
Backspace คือ 2 แท็บคือ 3
Daniel

6
-1 เพราะจากประสบการณ์ของฉันเทคนิคสมาร์ทประเภทนี้ในที่สุดก็กัดคุณในตูด
DonkeyMaster

ลูกศรซ้ายคือ 23, ลูกศรขวาคือ 25
แอรอน

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

2
สิ่งนี้จะไม่ยอมรับจุด.ด้วยเนื่องจากe.Textส่งคืนเฉพาะอักขระอินพุตล่าสุดและจุดจะไม่ผ่านการIsDigitตรวจสอบ
Anthony

4

สำหรับผู้ที่มองหาการใช้งานที่ง่ายและรวดเร็วสำหรับปัญหาประเภทนี้โดยใช้จำนวนเต็มและทศนิยมในไฟล์ XAML ของคุณให้เพิ่มPreviewTextInputคุณสมบัติลงTextBoxในไฟล์ xaml.cs ของคุณ:

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

เป็นเรื่องซ้ำซ้อนในการตรวจสอบสตริงทั้งหมดทุกครั้งเว้นแต่ตามที่คนอื่นพูดถึงคุณกำลังทำบางสิ่งด้วยสัญกรณ์ทางวิทยาศาสตร์ (แม้ว่าคุณจะเพิ่มตัวละครบางตัวเช่น 'e' การเพิ่มสัญลักษณ์ / อักขระ regex อย่างง่ายคือ ง่ายมากและแสดงให้เห็นในคำตอบอื่น ๆ ) แต่สำหรับค่าทศนิยมง่ายๆวิธีนี้จะพอเพียง

เขียนเป็นหนึ่งซับด้วยสีหน้าแลมบ์ดา:

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

3

เราสามารถตรวจสอบความถูกต้องในกล่องข้อความที่มีการเปลี่ยนแปลง การใช้งานต่อไปนี้ป้องกันการป้อนข้อมูลด้วยการกดปุ่มอื่นที่ไม่ใช่ตัวเลขและทศนิยมหนึ่งตำแหน่ง

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

3

แล้วเรื่องนี้ล่ะ ทำงานได้ดีสำหรับฉัน หวังว่าฉันจะไม่พลาดกรณีขอบ ...

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();
    }
});

2

ใน Windows Forms มันง่ายมาก คุณสามารถเพิ่มกิจกรรมสำหรับ KeyPress และทุกอย่างทำงานได้อย่างง่ายดาย อย่างไรก็ตามใน WPF เหตุการณ์นั้นไม่ได้มี แต่มีวิธีที่ง่ายกว่ามากสำหรับมัน

WPF TextBox มีเหตุการณ์ TextChanged ซึ่งเป็นเรื่องปกติสำหรับทุกสิ่ง มันรวมถึงการวางการพิมพ์และสิ่งที่สามารถมาถึงใจของคุณ

ดังนั้นคุณสามารถทำสิ่งนี้:

XAML:

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

ข้างหลังโค้ด:

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

นอกจากนี้ยังยอมรับ.ถ้าคุณไม่ต้องการมันเพียงแค่เอามันออกไปจากคำสั่งที่จะเป็นregex@[^\d]

หมายเหตุ : เหตุการณ์นี้สามารถใช้กับ TextBox ได้หลายอันเนื่องจากใช้senderText ของวัตถุ คุณเขียนเหตุการณ์เพียงครั้งเดียวและสามารถใช้กับกล่องข้อความหลายกล่องได้


2

ตอนนี้ฉันรู้ว่าคำถามนี้มีคำตอบที่ยอมรับได้แต่โดยส่วนตัวแล้วฉันคิดว่ามันสับสนเล็กน้อยและฉันเชื่อว่าควรจะง่ายกว่านั้น ดังนั้นฉันจะพยายามแสดงให้เห็นว่าฉันจะทำให้ดีที่สุดเท่าที่จะทำได้:

ในWindows Formsมีเหตุการณ์ที่เรียกว่าKeyPressดีสำหรับงานประเภทนี้อย่างสมบูรณ์ แต่นั่นไม่ได้มีอยู่ในWPFดังนั้นเราจะใช้PreviewTextInputเหตุการณ์แทน นอกจากนี้สำหรับการตรวจสอบผมเชื่อว่าหนึ่งสามารถใช้foreachห่วงผ่านtextbox.Textและตรวจสอบว่าตรงกับ ;) สภาพ แต่อย่างสุจริตนี้เป็นสิ่งที่แสดงออกปกติมีการ

อีกสิ่งหนึ่งก่อนที่เราจะดำน้ำในรหัสศักดิ์สิทธิ์ สำหรับเหตุการณ์ที่จะถูกไล่ออกเราสามารถทำสองสิ่ง:

  1. ใช้ XAML เพื่อบอกโปรแกรมที่เรียกใช้ฟังก์ชัน: <PreviewTextInput="textBox_PreviewTextInput/>
  2. ทำในLoadedกรณีของแบบฟอร์ม (ซึ่ง textBox อยู่): textBox.PreviewTextInput += onlyNumeric;

ฉันคิดว่าวิธีที่สองดีกว่าเพราะในสถานการณ์เช่นนี้ส่วนใหญ่คุณจะต้องใช้เงื่อนไขเดียวกัน ( regex) กับมากกว่าหนึ่งTextBoxและคุณไม่ต้องการทำซ้ำตัวเอง! .

ท้ายที่สุดนี่คือวิธีที่คุณจะทำ:

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

นี่คือเวอร์ชั่นของฉัน มันขึ้นอยู่กับฐานValidatingTextBoxคลาสที่เลิกทำในสิ่งที่ทำถ้ามันไม่ "ถูกต้อง" รองรับการวาง, ตัด, ลบ, backspace, +, - ฯลฯ

สำหรับจำนวนเต็ม 32 บิตมีคลาส Int32TextBox ที่เพิ่งเปรียบเทียบกับ int ฉันได้เพิ่มคลาสการตรวจสอบจุดลอยตัวด้วย

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

หมายเหตุ 1: เมื่อใช้การรวม WPF คุณต้องแน่ใจว่าคุณใช้คลาสที่เหมาะกับประเภทคุณสมบัติที่ถูกผูกไว้มิฉะนั้นอาจทำให้เกิดผลลัพธ์ที่แปลก

หมายเหตุ 2: เมื่อใช้คลาสจุดลอยตัวที่มีการเชื่อม WPF ตรวจสอบให้แน่ใจว่าการเชื่อมโยงใช้วัฒนธรรมปัจจุบันเพื่อจับคู่กับวิธี TryParse ที่ฉันใช้



1

ใช้:

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

คำอธิบายจะอยู่ในลำดับ
Peter Mortensen

1

ฉันกำลังทำงานกับกล่องที่ไม่ได้ผูกไว้สำหรับโครงการง่ายๆที่ฉันกำลังทำงานอยู่ดังนั้นฉันจึงไม่สามารถใช้วิธีการเชื่อมโยงมาตรฐาน ดังนั้นฉันจึงสร้างแฮ็คอย่างง่ายที่คนอื่นอาจพบว่ามีประโยชน์โดยเพียงแค่ขยายส่วนควบคุมกล่องข้อความที่มีอยู่:

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

เห็นได้ชัดว่าสำหรับประเภทลอยคุณจะต้องแยกมันเป็นลอยและอื่น ๆ ใช้หลักการเดียวกัน

จากนั้นในไฟล์ XAML คุณต้องรวมเนมสเปซที่เกี่ยวข้อง:

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

หลังจากนั้นคุณสามารถใช้มันเป็นตัวควบคุมปกติ:

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

1

หลังจากใช้วิธีแก้ปัญหาที่นี่มาระยะหนึ่งฉันได้พัฒนาตัวเองซึ่งทำงานได้ดีสำหรับการตั้งค่า MVVM ของฉัน โปรดทราบว่ามันไม่ได้เป็นแบบไดนามิกอย่างที่คนอื่น ๆ ในความรู้สึกของการอนุญาตให้ผู้ใช้ป้อนตัวละครที่ผิดพลาด แต่มันบล็อกพวกเขาจากการกดปุ่มและทำสิ่งใด สิ่งนี้สอดคล้องกับธีมของปุ่มสีเทาเมื่อไม่สามารถดำเนินการได้

ฉันมีTextBoxที่ผู้ใช้ต้องป้อนจำนวนหน้าเอกสารที่จะพิมพ์:

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

... ด้วยคุณสมบัติที่มีผลผูกพันนี้:

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

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

ฉันยังมีปุ่ม:

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

... ด้วยการผูกคำสั่งนี้:

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

        return _setNumberPagesCommand;
    }
}

และจากนั้นก็มีวิธีการSetNumberOfPages()แต่ไม่สำคัญสำหรับหัวข้อนี้ มันทำงานได้ดีในกรณีของฉันเพราะฉันไม่จำเป็นต้องเพิ่มรหัสใด ๆ ลงในไฟล์ behind รหัสของมุมมองและช่วยให้ฉันสามารถควบคุมพฤติกรรมการใช้Commandคุณสมบัติ



1

ในแอปพลิเคชัน WPF คุณสามารถจัดการสิ่งนี้ได้ด้วยการจัดการTextChangedเหตุการณ์:

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

สำหรับนักพัฒนาที่ต้องการให้ฟิลด์ข้อความยอมรับตัวเลขที่ไม่ได้ลงชื่อเท่านั้นเช่นซ็อกเก็ตพอร์ตและอื่น ๆ :

WPF

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

ค#

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

2
โปรดทราบว่าหากคุณต้องการใช้วิธีนี้กับช่องพอร์ตซ็อกเก็ตจริงๆ 65535คุณจะต้องตรวจสอบว่าจำนวนเต็มน้อยกว่าหรือเท่ากับ หากยิ่งใหญ่แสดงว่าไม่ใช่พอร์ตที่ถูกต้อง นอกจากนี้การตั้งค่าTextBox.MaxLengthเพื่อ5จะช่วยให้ทั้งโปรแกรมหรือ XAML
Beyondo

0

นี่คือสิ่งที่ฉันจะใช้เพื่อรับกล่องข้อความ WPF ที่ยอมรับตัวเลขและจุดทศนิยม:

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

ใส่รหัสในไฟล์คลาสใหม่เพิ่ม

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

ที่ด้านบนของไฟล์และสร้างโซลูชัน การควบคุม numericTextBox จะปรากฏขึ้นที่ด้านบนของกล่องเครื่องมือ


1
ดูโซลูชันที่ง่ายขึ้นมากก่อนหน้านี้โดยใช้ NumberValidationTextBox และนิพจน์ทั่วไป นี่มันไร้สาระ
Scott Shaw-Smith

@ ScottShaw-Smith บางทีทางออกที่ยอมรับอาจเป็นรหัสน้อยกว่า แต่ก็ไม่เร็วกว่านี้ มีบางโครงการที่ต้องการพลังการประมวลผลมากกว่าการใช้ regex อยู่เสมอ
Beyondo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.