Come posso ottenere un TextBox per accettare solo input numerici in WPF?


335

Sto cercando di accettare le cifre e il punto decimale, ma nessun segno.

Ho esaminato degli esempi usando il controllo NumericUpDown per Windows Form e questo esempio di un controllo personalizzato NumericUpDown di Microsoft . Ma finora sembra che NumericUpDown (supportato da WPF o meno) non fornirà la funzionalità che desidero. Nel modo in cui è progettata la mia applicazione, nessuno nella loro mente giusta vorrà pasticciare con le frecce. Non hanno alcun senso pratico, nel contesto della mia applicazione.

Quindi sto cercando un modo semplice per far sì che un TextBox WPF standard accetti solo i caratteri che desidero. È possibile? È pratico?

Risposte:


418

Aggiungi un evento di immissione testo in anteprima. In questo modo: <TextBox PreviewTextInput="PreviewTextInput" />.

Quindi, all'interno di questo set, e.Handledil testo non è consentito.e.Handled = !IsTextAllowed(e.Text);

Uso un semplice regex nel IsTextAllowedmetodo per vedere se dovrei consentire ciò che hanno digitato. Nel mio caso, voglio solo consentire numeri, punti e trattini.

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

Se si desidera impedire il incollamento di dati errati, collegare l' DataObject.Pastingevento DataObject.Pasting="TextBoxPasting"come mostrato qui (codice estratto):

// 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
Il tuo regex non consente la notazione scientifica (1e5) se questo è importante.
Ron Warholic,

14
Nota che questa risposta controlla solo ciò che scrivi, quindi puoi inserire 3-.3
David Sykes

153
Il punto della risposta non era specificare il Regex perfetto, era mostrare come usare WPF per filtrare ciò che qualcuno digita.
Ray,

27
[Spazio] non genera l'evento PreviewTextInput.
peterG

5
Qualcosa del genere double.TryParse()verrebbe probabilmente implementato con lo stesso numero di linee e sarebbe più flessibile.
Thomas Weller,

190

Il gestore eventi sta visualizzando l'anteprima del testo inserito. Qui un'espressione regolare corrisponde all'immissione di testo solo se non è un numero, quindi non viene inserita nella casella di testo di immissione.

Se si desidera solo lettere, sostituire l'espressione regolare come [^a-zA-Z].

XAML

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

FILE 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
Il gestore eventi è l'input di testo di anteprima. In questo caso l'espressione regolare corrisponde all'immissione di testo solo se non è un numero, quindi non viene creata nella casella di testo di immissione. Se vuoi solo alfabeti, sostituisci l'espressione regolare come [^ a-zA-Z].
Kishor,

Che dire di numeri, decimali e operatori?
Jason Ebersey,

Per favore fatemi sapere come usarlo quando dichiara in qualche altra classe STATICA e applicare alla casella di testo?
SHEKHAR SHETE il

3
Mi piace questa più della risposta effettiva, breve e semplice. La risposta effettiva richiede due metodi per ottenere lo stesso risultato.
Nastro

1
@Jagd La risposta suggerita è una migliore separazione delle preoccupazioni. Tuttavia, è possibile impostare il numero di caselle di testo anche su questa convalida. aggiungi solo PreviewTextInput = "NumberValidationTextBox". (Proprio come l'altra risposta!)
Nastro

84

Ho usato un po 'di quello che era già qui e ci ho dato una svolta usando un comportamento, quindi non devo propagare questo codice in un sacco di visualizzazioni ...

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

Ecco il codice di visualizzazione pertinente:

<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
Ispirato da questa fantastica soluzione, ho implementato alcuni miglioramenti. Si prega di dare un'occhiata qui sotto nella discussione.
Alex Klaus,

2
Ciao. So che è un po 'tardi, ma sto cercando di implementarlo, ma continuo a ricevere errori. Immagino che mi mancano alcuni riferimenti. Ci sono altri che devono essere digitati oltre alle impostazioni predefinite dopo aver creato una classe?
Offerta

1
@Offerta Sì, assicurati di includere xmlns: interactivity = " schemas.microsoft.com/expression/2010/interactivity " nella parte superiore della finestra di xaml.
WiteCastle,

L'espressione è ora obsoleta. Sebbene questo approccio sia pulito, utilizza un codice che non viene più mantenuto.
Robert Baker,

1
Quindi, se si modifica la funzione IsValid per restituire! ExceedsMaxLength (newText, paste) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RegularExpression); quindi questo valuterà l'intera stringa. A proposito - Adoro questa opzione con i comportamenti !!
Rogala,

59

Questa è una soluzione migliorata della risposta di WilP . I miei miglioramenti sono:

  • Comportamento migliorato su Del e Backspace pulsanti
  • aggiunto EmptyValue proprietà, se la stringa vuota non è appropriata
  • Risolti alcuni errori di battitura minori
/// <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] --------------------------------
}

L'utilizzo è piuttosto semplice:

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

1
Questa soluzione è piuttosto migliore. Ma hai fatto un piccolo errore: quando non si imposta il MaxLengthconditon si (this.MaxLength == 0 || text.Length <= this.MaxLength)ritorna sempre falsequando si prova il nuovo testo. Questo dovrebbe essere meglio (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)poiché hai impostato int.MinValuecome valore predefinito per MaxLength.
Christoph Meißner,

1
Grazie @Christoph, sì, hai ragione. Ho modificato la mia risposta.
Alex Klaus,

@AlexKlaus funziona alla grande finché non aggiungo UpdateSourceTrigger=PropertyChangedalla rilegatura. Qualche idea su come far funzionare questo codice quando si cambia l' UpdateSourceTriggerimpostazione è PropertyChanged? Grazie per aver condiviso questo codice.
Junior

32

Ecco un modo molto semplice e facile per farlo usando MVVM.

Associa la tua textBox con una proprietà intera nel modello di visualizzazione e funzionerà come una gemma ... mostrerà anche la convalida quando viene inserito un non intero nella casella di testo.

Codice XAML:

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

Visualizza codice modello:

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

Ma la domanda conteneva "Sto cercando di accettare le cifre e il punto decimale" . Il punto decimale è accettato per questa risposta?
Peter Mortensen,

Ho provato a passare longa float, ma non ha funzionato perfettamente con la convalida immediata. Ho aggiunto UpdateSourceTrigger="PropertyChanged"l'associazione, in modo da eseguire la convalida man mano che veniva digitato ogni carattere e non poteva più digitare un '.' nella TextBox a meno che non fosse presente un carattere illegale (è stato necessario digitare "1x.234" quindi eliminare la "x"). Sembra anche un po 'lento in questa modalità. Questo sembra usare System.Number.ParseSingle()per fare il lavoro, quindi accetta una varietà di notazioni.
fadden,

@wolle probabilmente non verrà votato perché non spiega come funziona la validazione.
Paul McCarthy,

26

Aggiungi una REGOLA DI CONVALIDA in modo che, quando il testo cambia, controlla per determinare se i dati sono numerici e, se lo sono, consente il proseguimento dell'elaborazione e, in caso contrario, richiede all'utente che vengano accettati solo dati numerici in quel campo.

Maggiori informazioni in Convalida in Windows Presentation Foundation


6
Questa non è davvero una risposta per gli standard SO.
Robert Baker,

Questo sembra essere il modo .net di farlo.
Telemat,

1
Il è la giusta risposta: convalida deve essere di modello VIENE o livello di modello. Inoltre puoi semplicemente associarti a un tipo numerico simile doublee che ti sta già fornendo una validazione standard.


20

Potrebbe anche semplicemente implementare una regola di convalida e applicarla a TextBox:

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

Con l'implementazione della regola come segue (utilizzando lo stesso Regex proposto nelle altre risposte):

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

Se si desidera inserire cifre decimali, non restituire "valido" quando il testo termina in "." Si prega di fare riferimento al stackoverflow.com/a/27838893/417939
YantingChen

14

Qui ho una soluzione semplice ispirata alla risposta di Ray . Questo dovrebbe essere sufficiente per identificare qualsiasi forma di numero.

Questa soluzione può anche essere facilmente modificata se si desidera solo numeri positivi, valori interi o valori precisi su un numero massimo di cifre decimali, ecc.


Come suggerito nella risposta di Ray , devi prima aggiungere un PreviewTextInputevento:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Quindi inserisci quanto segue nel codice dietro:

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
mi piace molto questa risposta, semplice ed efficace +
Pulle

dio e semplice ma è brutto che permetta spazi
Momo

2
Ciò consente ancora a qualcuno di incollare una stringa nella casella di testo
FCin

8

Ho consentito numeri di tastiera numerici e 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
Mi consiglia di utilizzare i valori enum invece di 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

Presumo che:

  1. La casella di testo per la quale si desidera consentire l'input numerico ha la proprietà Text inizialmente impostata su un valore numerico valido (ad esempio, 2.7172).

  2. La tua casella di testo è figlio della finestra principale

  3. La finestra principale è di classe Window1

  4. Il tuo nome TextBox è numericTB

Idea base:

  1. Aggiungi: private string previousText;alla tua classe di finestra principale (Window1)

  2. Aggiungi: previousText = numericTB.Text;al costruttore della finestra principale

  3. Crea un gestore per l'evento numericTB.TextChanged in modo che sia simile al seguente:

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

Ciò continuerà a impostare il precedenteTesto su numericTB.Text finché è valido e imposta numericTB.Text sul suo ultimo valore valido se l'utente scrive qualcosa che non ti piace. Naturalmente, questa è solo un'idea di base, ed è solo "resistente agli idioti", non "a prova di idiota". Non gestisce il caso in cui l'utente incasina gli spazi, ad esempio. Quindi, ecco una soluzione completa che penso sia "a prova di idiota", e se sbaglio per favore dimmi:

  1. Contenuto del tuo file 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. Contenuto del tuo file 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;
                    }
                }
            }
        }
    }

E questo è tutto. Se hai molte TextBox, allora ti consiglio di creare un CustomControl che eredita da TextBox, in modo da poter racchiudere precedentiText e numericTB_TextCambiati in un file separato.


Caspita è fantastico! Come potrei consentire un simbolo negativo nella parte anteriore?
theNoobGuy

6

Questo è l'unico codice necessario:

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

Ciò consente solo l'inserimento di numeri nella casella di testo.

Per consentire un punto decimale o un segno meno, è possibile modificare l'espressione regolare in [^0-9.-]+.


1
Ottima soluzione ad eccezione di un nitpick minore: non ti impedirà di inserire spazi bianchi, poiché quelli non attivano l'evento PreviewTextInput.
Tim Pohlmann,

Anche Backspace non lo attiva.
Winger Sendon,

6

Se non vuoi scrivere molto codice per svolgere una funzione di base (non so perché le persone facciano metodi lunghi) puoi semplicemente fare questo:

  1. Aggiungi spazio dei nomi:

    using System.Text.RegularExpressions;
  2. In XAML, imposta una proprietà TextChanged:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
  3. In WPF con il metodo txt1_TextChanged, aggiungi Regex.Replace:

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

2
È molto più pulito utilizzare un comportamento o una proprietà allegata. Nessun fiddeling code-behind in ogni vista / per ogni casella di testo
Sir Rufo

Può funzionare ma è brutto in quanto la carota salterà davanti alla casella di testo
Momo

6

Un altro approccio utilizzerà un comportamento associato, ho implementato la mia classe TextBoxHelper personalizzata , che può essere utilizzata su caselle di testo in tutto il mio progetto. Perché ho pensato che iscriversi agli eventi per ogni casella di testo e in ogni singolo file XAML a questo scopo può richiedere molto tempo.

La classe TextBoxHelper che ho implementato ha queste caratteristiche:

  • Filtraggio e accettazione dei soli numeri nei formati Double , Int , Uint e Natural
  • Filtraggio e accettando solo Anche o dispari numeri
  • Gestione del gestore eventi incolla per impedire di incollare testo non valido nelle nostre caselle di testo numeriche
  • È possibile impostare un valore predefinito che verrà utilizzato per impedire dati non validi come ultimo scatto sottoscrivendo l'evento TextChanged delle caselle di testo

Ecco l'implementazione della classe 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;
            }
        }
    }
}

Ed ecco alcuni esempi del suo facile utilizzo:

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

O

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

Nota che my TextBoxHelper risiede nell'alias xmlns viewHelpers.

Spero che questa implementazione faciliti il ​​lavoro di qualcun altro :)


1
Questo è fantastico quando si utilizza una casella di testo all'interno di un DataTemplate, grazie!
NucS

3
Ottima risposta ma trovo i tuoi metodi difficili da leggere. Probabilmente dovresti scomporli in quelli più piccoli. Vedi Qual è la lunghezza ideale di un metodo per te?
Anthony,

Grazie per il feedback costruttivo @Anthony
Amir Mahdi Nassiri

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

nell'evento di anteprima keydown della casella di testo.


3
Tuttavia, non consente il backspace.
sventevit,

2
Backspace è 2, la scheda è 3
Daniel

6
-1 perché nella mia esperienza questo tipo di trucchi intelligenti alla fine ti mordono nel culo, come hanno notato alcuni altri commentatori.
DonkeyMaster,

La freccia sinistra è 23, la freccia destra è 25.
Aaron

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

2
anche questo non accetta punti ., poiché e.Textrestituisce solo l'ultimo carattere di input e un punto fallisce il IsDigitcontrollo.
Anthony,

4

Per coloro che cercano un'implementazione rapida e molto semplice per questo tipo di problema usando solo numeri interi e decimali, nel tuo file XAML, aggiungi una PreviewTextInputproprietà al tuo TextBoxe quindi nel tuo file xaml.cs usare:

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

È un po 'ridondante continuare a controllare l'intera stringa ogni volta a meno che, come altri hanno già detto, stai facendo qualcosa con notazione scientifica (anche se, se aggiungi alcuni caratteri come' e ', un semplice regex che aggiunge simboli / caratteri è veramente semplice e illustrato in altre risposte). Ma per semplici valori in virgola mobile, questa soluzione sarà sufficiente.

Scritto come una riga con un'espressione lambda:

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

3

Possiamo eseguire la convalida sull'evento modificato nella casella di testo. La seguente implementazione impedisce l'immissione di tasti diversi da un valore numerico e un punto decimale.

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

Cosa ne pensi di questo? Funziona bene per me. Spero di non aver perso nessun caso limite ...

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

In Windows Form era facile; puoi aggiungere un evento per KeyPress e tutto funziona facilmente. Tuttavia, in WPF quell'evento non c'è. Ma c'è un modo molto più semplice per farlo.

WPF TextBox ha l'evento TextChanged che è generale per tutto. Include incollare, scrivere e qualunque cosa ti venga in mente.

Quindi puoi fare qualcosa del genere:

XAML:

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

CODICE DIETRO:

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

Ciò accetta anche ., se non lo si desidera, rimuoverlo regexdall'istruzione @[^\d].

Nota : questo evento può essere utilizzato su molte TextBox poiché utilizza il sendertesto dell'oggetto. Scrivi l'evento solo una volta e puoi usarlo per più TextBox.


2

Ora so che questa domanda ha una risposta accettata , ma personalmente la trovo un po 'confusa e credo che dovrebbe essere più facile di così. Quindi proverò a dimostrare come ho ottenuto che funzioni nel miglior modo possibile:

In Windows Form , c'è un evento chiamato KeyPressche è perfettamente buono per questo tipo di attività. Ma questo non esiste in WPF , quindi utilizzeremo l' PreviewTextInputevento. Inoltre, per la convalida, credo che si possa usare un foreachper scorrere attraverso textbox.Texte verificare se corrisponde ;) la condizione, ma onestamente, questo è ciò che le espressioni regolari .

Un'altra cosa prima di immergerci nel codice sacro . Affinché l'evento venga generato, si possono fare due cose:

  1. Usa XAML per dire al programma quale funzione chiamare: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Fallo nel Loadedcaso del modulo (in cui si trova il textBox): textBox.PreviewTextInput += onlyNumeric;

Penso che il secondo metodo sia migliore perché in situazioni come questa, ti verrà richiesto principalmente di applicare la stessa condizione ( regex) a più di una TextBoxe non vorrai ripetere te stesso!.

Infine, ecco come lo faresti:

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

Ecco la mia versione di esso. È basato su una ValidatingTextBoxclasse base che annulla ciò che è stato fatto se non è "valido". Supporta incolla, taglia, cancella, backspace, +, - ecc.

Per un numero intero a 32 bit, esiste una classe Int32TextBox che confronta solo con un int. Ho anche aggiunto classi di validazione in virgola mobile.

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

Nota 1: quando si utilizza l'associazione WPF, è necessario assicurarsi di utilizzare la classe che si adatta al tipo di proprietà associata, altrimenti si potrebbero ottenere risultati strani.

Nota 2: quando si utilizzano classi in virgola mobile con associazione WPF, assicurarsi che l'associazione utilizzi la cultura corrente per abbinare il metodo TryParse che ho usato.



1

Uso:

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

Una spiegazione sarebbe in ordine.
Peter Mortensen,

1

Stavo lavorando con una casella non associata per un semplice progetto a cui stavo lavorando, quindi non potevo usare l'approccio standard di rilegatura. Di conseguenza ho creato un semplice trucco che altri potrebbero trovare molto utili semplicemente estendendo il controllo TextBox esistente:

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

Ovviamente, per un tipo mobile, vorrai analizzarlo come float e così via. Si applicano gli stessi principi.

Quindi nel file XAML è necessario includere lo spazio dei nomi pertinente:

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

Dopodiché puoi usarlo come controllo regolare:

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

1

Dopo aver utilizzato alcune delle soluzioni qui per qualche tempo, ho sviluppato il mio che funziona bene per la mia configurazione MVVM. Si noti che non è dinamico come alcuni degli altri nel senso di consentire ancora agli utenti di inserire caratteri errati, ma impedisce loro di premere il pulsante e quindi fare qualsiasi cosa. Questo va bene con il mio tema di ingrigire i pulsanti quando non è possibile eseguire azioni.

Ho TextBoxun utente che deve inserire un numero di pagine del documento da stampare:

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

... con questa proprietà vincolante:

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

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

Ho anche un pulsante:

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

... con questo comando associato:

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

        return _setNumberPagesCommand;
    }
}

E poi c'è il metodo di SetNumberOfPages(), ma non è importante per questo argomento. Funziona bene nel mio caso perché non devo aggiungere alcun codice nel file code-behind di View e mi permette di controllare il comportamento usando la Commandproprietà.



1

Nell'applicazione WPF, puoi gestirlo gestendo l' TextChangedevento:

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

Per gli sviluppatori che desiderano che i loro campi di testo accettino solo numeri non firmati come porte socket e così via:

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
Nota che se vuoi davvero usare questo metodo con un campo porta socket; Dovresti verificare se il numero intero è minore o uguale a 65535. Se è maggiore, non è una porta valida. Inoltre, l'impostazione del TextBox.MaxLengthto 5sarebbe di aiuto a livello di codice o in XAML .
Beyondo,

0

Questo è ciò che vorrei utilizzare per ottenere una casella di testo WPF che accetta le cifre e il punto decimale:

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

Inserisci il codice in un nuovo file di classe, aggiungi

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

nella parte superiore del file e crea la soluzione. Il controllo numericTextBox verrà quindi visualizzato nella parte superiore della casella degli strumenti.


1
Scopri la soluzione MOLTO più semplice precedente utilizzando NumberValidationTextBox ed espressioni regolari. Questo è ridicolo.
Scott Shaw-Smith

@ ScottShaw-Smith Forse la soluzione accettata è meno codice, ma non è più veloce di così. Ci sono sempre alcuni progetti che richiedono molta potenza di elaborazione piuttosto che usare regex.
Beyondo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.