web-dev-qa-db-fra.com

Coller un événement dans une zone de texte WPF

J'ai créé un contrôle personnalisé héritant de TextBox. Ce contrôle personnalisé est un TextBox numérique, prenant uniquement en charge les nombres.

J'utilise OnPreviewTextInput pour vérifier chaque nouveau caractère tapé pour voir si le caractère est une entrée valide. Cela fonctionne très bien. Cependant, si je colle le texte dans le TextBox, OnPreviewTextInput n'est pas déclenché.

Quelle est la meilleure façon de capturer du texte collé dans un TextBox?

De plus, j'ai un problème lorsque l'espace arrière est pressé, je ne peux pas savoir quel événement cela se déclenchera. OnPreviewTextInput n'est pas viré!

Avez-vous des idées sur la façon de capturer du texte collé et des événements d'espace arrière dans WPF TextBox?

61
code-zoop

Voici un code que je traînais au cas où j'en aurais besoin. Ça pourrait vous aider.

public Window1()
{
    InitializeComponent();

    // "tb" is a TextBox
    DataObject.AddPastingHandler(tb, OnPaste);
}

private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
    var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
    if (!isText) return;

    var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
    ...
}
120
Matt Hamilton

Le problème avec la tentative d'intercepter et d'intercepter tous les événements individuels susceptibles de provoquer la modification d'une propriété TextBox.Text est qu'il existe de nombreux événements de ce type:

  • TextInput: types d'utilisateurs
  • KeyDown: Supprimer, Retour arrière, Entrer, IME
  • Gestes de commande: Ctrl-X, Ctrl-Y, Ctrl-V, Ctrl-X
  • MouseDown: bouton Coller, bouton Couper, bouton Annuler, ...
  • Cliquez: barre d'espace enfoncée lorsque les boutons Coller, Couper, Annuler ont le focus local
  • RaiseEvent: le code déclenche les commandes Coller, Couper, Annuler, Rétablir
  • Accessibilité: commandes vocales, claviers braille, etc.

Essayer d'intercepter de manière fiable tout cela est un exercice futile. Une bien meilleure solution consiste à surveiller TextBox.TextChanged et à rejeter les modifications que vous n'aimez pas.

Dans cette réponse je montre comment implémenter une classe TextBoxRestriction pour le scénario particulier qui est demandé. Cette même technique peut être généralisée pour une utilisation avec toutes les restrictions que vous souhaitez placer sur votre contrôle TextBox.

Par exemple, dans votre cas, vous pouvez implémenter une propriété attachée RestrictValidChars de la même manière que la propriété RestrictDeleteTo dans ce code. Ce serait la même chose sauf que la boucle intérieure vérifierait les insertions, pas les suppressions. Il serait utilisé comme ceci:

<TextBox my:TextBoxRestriction.RestrictValidChars="0123456789" />

Ce n'est qu'une idée de la façon dont cela pourrait être géré. Il existe de nombreuses façons de structurer votre code en fonction de ce que vous voulez. Par exemple, vous pouvez modifier TextBoxRestriction pour appeler votre propre code pour valider à l'aide d'une propriété attachée qui prend un délégué ou un objet contenant un événement.

Consultez l'autre réponse pour plus de détails sur la façon de lier la propriété Text lorsque vous utilisez la classe TextBoxRestriction afin qu'elle ne déclenche pas la restriction lorsque vous ne le souhaitez pas.

14
Ray Burns

Pour le retour arrière, veuillez vérifier l'événement PreviewKeyDown

Pour la commande coller, ajoutez une liaison de commande à ApplicationCommands.Paste et définissez l'argument sur géré, si vous ne souhaitez rien faire avec:

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Paste"
                  Executed="PasteExecuted" />
</Window.CommandBindings>

Et en code derrière:

private void PasteExecuted(object sender, ExecutedRoutedEventArgs e)
{
    e.Handled = true;
}
7
Arcturus

Vous pouvez y parvenir avec l'événement PreviewKeyDown et l'événement TextChanged.

Dans PreviewKeyDown capturez l'opération Coller

if(Key.V == e.Key && Keyboard.Modifiers == ModifierKeys.Control)
{
   strPreviousString = this.txtNumber.Text;
   bIsPasteOperation = true;
}

Dans TextChanged événement

if (true == bIsPasteOperation)
{

   if (false == this.IsNumber(this.txtNumber.Text))
   {
      this.txtNumber.Text = strPreviousString;
      e.Handled = true;
   }
   bIsPasteOperation = false;
}

IsNumber méthode valide le texte saisi est Number ou non

private bool IsNumber(string text)
{
   int number;

   //Allowing only numbers
   if (!(int.TryParse(text, out number)))
   {
      return false;
   }
   return true
}
2
Syed

Cela fonctionne assez bien pour moi. J'ai voulu changer la couleur de la zone de texte lorsque l'utilisateur a modifié le contenu.

  • accepter les nombres, y compris les points et les caractères négatifs
  • touches tapées: supprimer, retour arrière, ctrl-V (coller), ctrl-X (couper)
  • clic droit de la souris pour coller et couper

J'ai pu y parvenir avec les 3 événements ci-dessous:

    public bool IsDirty {
        set {
            if(value) {
                txtValue.Background = Brushes.LightBlue;
            } else {
                txtValue.Background = IsReadOnly ? Brushes.White : Brushes.LightYellow;
            }
        }
        get {
            return txtValue.Background == Brushes.LightBlue;
        }
    }

    private void PreviewTextInput(object sender, TextCompositionEventArgs e) {
        TextBox tb = ((TextBox)sender);
        string originalText = tb.Text;
        string newVal = "";

        //handle negative
        if (e.Text=="-") {
            if(originalText.IndexOf("-") > -1 || tb.CaretIndex != 0 || originalText == "" || originalText == "0") {
                //already has a negative or the caret is not at the front where the - should go
                //then ignore the entry
                e.Handled = true;
                return;
            }

            //put it at the front
            newVal = e.Text + originalText;
        } else {
            //normal typed number
            newVal = originalText + e.Text;
        }

        //check if it's a valid double if so then dirty
        double dVal;
        e.Handled = !double.TryParse(newVal, out dVal);
        if(!e.Handled) {
            IsDirty = true;
        }
    }

    private void PreviewKeyUp(object sender, KeyEventArgs e) {
        //handle paste
        if ((Key.V == e.Key || Key.X == e.Key) && Keyboard.Modifiers ==  ModifierKeys.Control) {
            IsDirty = true;
        }
        //handle delete and backspace
        if (e.Key == Key.Delete || e.Key == Key.Back) {
            IsDirty = true;
        }
    }


    private void PreviewExecuted(object sender, ExecutedRoutedEventArgs e) {
        //handle context menu cut/paste
        if (e.Command == ApplicationCommands.Cut || e.Command == ApplicationCommands.Paste) {
            IsDirty = true;
        }
    }
0
JJ_Coder4Hire