Bind TextBox sulla pressione di Enter-key

Il databinding predefinito su TextBox è TwoWay e impegna il testo sulla proprietà solo quando TextBox perso il focus.

C’è un modo semplice XAML per fare in modo che il database si verifichi quando premo il tasto Invio sul TextBox ?. So che è piuttosto semplice farlo nel codice, ma immagina se questo TextBox trova all’interno di un DataTemplate complesso.

Puoi creare un approccio XAML puro creando un comportamento collegato .

Qualcosa come questo:

 public static class InputBindingsManager { public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty = DependencyProperty.RegisterAttached( "UpdatePropertySourceWhenEnterPressed", typeof(DependencyProperty), typeof(InputBindingsManager), new PropertyMetadata(null, OnUpdatePropertySourceWhenEnterPressedPropertyChanged)); static InputBindingsManager() { } public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value) { dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value); } public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp) { return (DependencyProperty)dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty); } private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e) { UIElement element = dp as UIElement; if (element == null) { return; } if (e.OldValue != null) { element.PreviewKeyDown -= HandlePreviewKeyDown; } if (e.NewValue != null) { element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown); } } static void HandlePreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { DoUpdateSource(e.Source); } } static void DoUpdateSource(object source) { DependencyProperty property = GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject); if (property == null) { return; } UIElement elt = source as UIElement; if (elt == null) { return; } BindingExpression binding = BindingOperations.GetBindingExpression(elt, property); if (binding != null) { binding.UpdateSource(); } } } 

Quindi in XAML si imposta la proprietà InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty su quella che si desidera aggiornare quando si preme il tasto Invio . Come questo

  

(Devi solo assicurarti di includere un riferimento al namespace clr xmlns per “b” nell’elemento root del tuo file XAML puntando a quale mai spazio dei nomi hai inserito InputBindingsManager).

Non credo che esista un modo “puro XAML” per fare ciò che stai descrivendo. È ansible impostare un’associazione in modo che si aggiorni ogni volta che il testo in un TextBox cambia (piuttosto che quando il TextBox perde lo stato attivo ) impostando la proprietà UpdateSourceTrigger , in questo modo:

  

Se si imposta UpdateSourceTrigger su “Explicit” e quindi si gestisce l’evento PreviewKeyDown del TextBox (cercando il tasto Invio), è ansible ottenere ciò che si desidera, ma richiederebbe code-behind. Forse una sorta di proprietà allegata (simile alla mia proprietà EnterKeyTraversal) funziona per te.

Ecco come ho risolto questo problema. Ho creato un gestore di eventi speciale inserito nel codice sottostante:

 private void TextBox_KeyEnterUpdate(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { TextBox tBox = (TextBox)sender; DependencyProperty prop = TextBox.TextProperty; BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop); if (binding != null) { binding.UpdateSource(); } } } 

Quindi ho aggiunto questo come gestore di eventi KeyUp in XAML:

   

Il gestore di eventi utilizza il riferimento del sender per fare in modo che il proprio binding venga aggiornato. Poiché il gestore eventi è autonomo, dovrebbe funzionare in un DataTemplate complesso. Questo unico gestore di eventi può ora essere aggiunto a tutte le caselle di testo che richiedono questa funzionalità.

Potresti facilmente creare il tuo controllo ereditato da TextBox e riutilizzarlo nel tuo progetto.

Qualcosa di simile a questo dovrebbe funzionare:

 public class SubmitTextBox : TextBox { public SubmitTextBox() : base() { PreviewKeyDown += new KeyEventHandler(SubmitTextBox_PreviewKeyDown); } void SubmitTextBox_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { BindingExpression be = GetBindingExpression(TextBox.TextProperty); if (be != null) { be.UpdateSource(); } } } } 

Potrebbe esserci un modo per aggirare questo passaggio, ma altrimenti dovresti eseguire il binding in questo modo (usando Explicit):

  

Se si combinano sia le soluzioni di Ben che quelle di ausadmin, si finisce con una soluzione molto MVVM:

      

… il che significa che stai passando lo stesso TextBox come parametro al Command .

Questo porta al tuo Command con questo aspetto (se stai utilizzando un’implementazione in stile DelegateCommand nella tua VM):

  public bool CanExecuteUpdateTextBoxBindingOnEnterCommand(object parameter) { return true; } public void ExecuteUpdateTextBoxBindingOnEnterCommand(object parameter) { TextBox tBox = parameter as TextBox; if (tBox != null) { DependencyProperty prop = TextBox.TextProperty; BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop); if (binding != null) binding.UpdateSource(); } } 

Questa implementazione Command può essere utilizzata per qualsiasi TextBox e, soprattutto, nessun codice nel code-behind, anche se potresti volerlo inserire nella sua class, quindi non ci sono dipendenze su System.Windows.Controls nella tua VM. Dipende da quanto siano rigorose le linee guida del codice.

Ecco un approccio che a me sembra abbastanza semplice, e più semplice che aggiungere un AttachedBehaviour (che è anche una soluzione valida). Utilizziamo l’UpdateSourceTrigger predefinito (LostFocus per TextBox) e quindi aggiungiamo un InputBinding alla chiave Invio, associato a un comando.

Lo xaml è il seguente

       

Quindi i metodi di comando sono

 Private Function CanExecuteUpdateText1(ByVal param As Object) As Boolean Return True End Function Private Sub ExecuteUpdateText1(ByVal param As Object) If TypeOf param Is String Then Txt1 = CType(param, String) End If End Sub 

E il TextBox è associato alla proprietà

  Public Property Txt1 As String Get Return _txt1 End Get Set(value As String) _txt1 = value OnPropertyChanged("Txt1") End Set End Property 

Finora questo sembra funzionare bene e cattura l’evento Enter Key nel TextBox.

Più semplice, basta impostare UpdateSourceTrigger su PropertyChanged nel binding del TextBox senza aggiungere nulla in codebehind. Proprio come questo:

  

Per me funziona.

Nel caso in cui si usi MultiBinding con il TextBox, è necessario utilizzare il metodo BindingOperations.GetMultiBindingExpression anziché BindingOperations.GetBindingExpression .

 // Get the correct binding expression based on type of binding //(simple binding or multi binding. BindingExpressionBase binding = BindingOperations.GetBindingExpression(element, prop); if (binding == null) { binding = BindingOperations.GetMultiBindingExpression(element, prop); } if (binding != null) { object value = element.GetValue(prop); if (string.IsNullOrEmpty(value.ToString()) == true) { binding.UpdateTarget(); } else { binding.UpdateSource(); } } 

Questo funziona per me:

       

Ho risposto qui abbastanza elegantemente usando comportamenti allegati, il mio metodo preferito per quasi tutto.

WPF come rendere la casella di testo perde lo stato attivo dopo aver premuto invio

Questa non è una risposta alla domanda originale, ma piuttosto un’estensione della risposta accettata da @ Samuel Jack. Ho fatto ciò che segue nella mia domanda, ed ero intimorito dall’eleganza della soluzione di Samuel. È molto pulito e molto riutilizzabile, poiché può essere utilizzato su qualsiasi controllo, non solo su TextBox . Ho pensato che questo dovrebbe essere condiviso con la comunità.

Se hai una Finestra con migliaia di TextBoxes che richiedono tutti l’aggiornamento di Binding Source su Invio, puoi associare questo comportamento a tutti, includendo l’XAML in basso nelle tue Resources Window invece di collegarlo a ciascun TextBox. Per prima cosa devi attuare il comportamento allegato come per il post di Samuel , ovviamente.

    

È sempre ansible limitare l’ambito, se necessario, inserendo lo stile nelle risorse di uno degli elementi figlio di Window (ovvero una Grid ) che contiene i TextBox di destinazione.

Personalmente ritengo che avere un’estensione di markup sia un approccio più pulito.

 public class UpdatePropertySourceWhenEnterPressedExtension : MarkupExtension { public override object ProvideValue(IServiceProvider serviceProvider) { return new DelegateCommand(textbox => textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource()); } }