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
?
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;
...
}
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:
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.
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;
}
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;
}
Où 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
}
Cela fonctionne assez bien pour moi. J'ai voulu changer la couleur de la zone de texte lorsque l'utilisateur a modifié le contenu.
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;
}
}