Enlazar TextBox en presionar Enter

El enlace de datos predeterminado en TextBox es TwoWay y confirma el texto en la propiedad solo cuando TextBox pierde su foco.

¿Hay alguna forma sencilla de XAML para hacer que el enlace de datos suceda cuando presiono la tecla Enter en el TextBox ? Sé que es bastante fácil de hacer en el código subyacente, pero imagine si este TextBox está dentro de una compleja DataTemplate .

Puede hacerse un enfoque XAML puro mediante la creación de un comportamiento adjunto .

Algo como esto:

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

Luego, en su XAML, establece la propiedad InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty en la que desea actualizar cuando se presiona la tecla Intro . Me gusta esto

  

(Solo necesita asegurarse de incluir una referencia xmlns clr-namespace para “b” en el elemento raíz de su archivo XAML apuntando al espacio de nombres en el que coloca el InputBindingsManager).

No creo que haya una forma “pura XAML” de hacer lo que describes. Puede configurar un enlace para que se actualice cada vez que el texto en un cuadro de texto cambie (en lugar de cuando el cuadro de texto pierda el foco) configurando la propiedad UpdateSourceTrigger , como esta:

  

Si configura UpdateSourceTrigger como “Explícito” y luego maneja el evento PreviewKeyDown de TextBox (buscando la tecla Intro), entonces puede lograr lo que quiere, pero requeriría código subyacente. Tal vez algún tipo de propiedad adjunta (similar a mi propiedad EnterKeyTraversal) funcione para usted.

Así es como resolví este problema. Creé un controlador de eventos especial que entró en el código detrás:

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

Luego acabo de agregar esto como un controlador de eventos KeyUp en el XAML:

   

El controlador de eventos usa su referencia de sender para hacer que su propio enlace se actualice. Como el controlador de eventos es autónomo, debería funcionar en una plantilla de datos compleja. Este controlador de un evento ahora se puede agregar a todos los cuadros de texto que necesitan esta característica.

Puede crear fácilmente su propio control heredando de TextBox y reutilizarlo en todo su proyecto.

Algo similar a esto debería funcionar:

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

Puede haber una forma de evitar este paso, pero de lo contrario deberías enlazar así (usando Explícito):

  

Si combina las soluciones de Ben y Ausadmin, obtendrá una solución muy amigable con MVVM:

      

… lo que significa que está pasando el propio TextBox como el parámetro para el Command .

Esto lleva a que su Command vea así (si está utilizando una implementación de estilo DelegateCommand en su máquina virtual):

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

Esta implementación de Command se puede utilizar para cualquier TextBox y, lo mejor de todo, sin código en el código subyacente, aunque es posible que desee colocarlo en su propia clase para que no haya dependencias en System.Windows.Controls en su VM. Depende de cuán estrictas sean sus pautas de código.

Aquí hay un enfoque que para mí parece bastante sencillo, y más fácil que agregar un AttachedBehaviour (que también es una solución válida). Usamos el UpdateSourceTrigger predeterminado (LostFocus para TextBox), y luego agregamos un InputBinding a la tecla Enter, ligado a un comando.

El xaml es el siguiente

       

Entonces los métodos de comando son

 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 

Y el TextBox está vinculado a la propiedad

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

Hasta ahora, esto parece funcionar bien y capta el evento Enter Key en el TextBox.

Más simple, simplemente configure UpdateSourceTrigger en PropertyChanged en el enlace de su TextBox sin agregar nada en codebehind. Solo así:

  

Esto funciona para mi.

En caso de que esté utilizando MultiBinding con su TextBox, necesita usar el método BindingOperations.GetMultiBindingExpression lugar de 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(); } } 

Esto funciona para mí:

       

Respondí aquí con bastante elegancia utilizando comportamientos adjuntos, mi método preferido para casi cualquier cosa.

WPF cómo hacer que cuadro de texto pierda el foco después de presionar enter

Esta no es una respuesta a la pregunta original, sino más bien una extensión de la respuesta aceptada por @Samuel Jack. Hice lo siguiente en mi propia aplicación, y admiré la elegancia de la solución de Samuel. Es muy limpio y muy reutilizable, ya que se puede usar en cualquier control, no solo en el TextBox . Pensé que esto debería ser compartido con la comunidad.

Si tiene una ventana con miles de TextBoxes que requieren actualizar el origen de enlace al TextBoxes , puede adjuntar este comportamiento a todos ellos incluyendo el XAML a continuación en sus Resources Window lugar de adjuntarlo a cada cuadro de texto. Primero debes implementar el comportamiento adjunto según la publicación de Samuel , por supuesto.

    

Siempre puede limitar el scope, si es necesario, colocando el Estilo en los Recursos de uno de los elementos secundarios de la Ventana (es decir, una Grid ) que contiene los Campos de texto de destino.

Personalmente, creo que tener una extensión de marcado es un enfoque más limpio.

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