2012-06-18 12 views
8

¿hay alguna forma de actualizar un enlace antes o cuando se activa un comando? Tengo algunos campos de texto que puedo editar y guardar usando un comando, accesible a través de un atajo de teclado. Como el enlace generalmente solo se actualiza cuando el campo de texto pierde el foco, el último cambio no se mantiene al presionar la tecla para guardar los datos. En cambio, tengo que salir primero del campo de texto para actualizarlo y luego guardarlo.Force binding para actualizar cuando se activa Command

¿Hay alguna manera de forzar la actualización de una manera elegante? Estoy usando MVVM (pero no cualquier framework de MVVM), así que me gustaría mantener las cosas específicas de UI fuera del código de comando. Además, realmente no quiero cambiar el enlace para actualizar cada cambio, está bien tenerlo actualizado solo cuando se pierde el foco.

Respuesta

2

que han resuelto este momento estableciendo explícitamente el foco a algún otro elemento antes de pedir los valores. Obviamente, esto hace que el elemento que actualmente tiene el foco lo pierda y actualice el enlace.

Para establecer el enfoque, he escrito una propiedad adjunta, inspirada en las respuestas a otras preguntas. Además, junto con my other question hice esto algo automatizado.

Así que para usarlo, básicamente adjuntar mi propiedad a un elemento, en este caso un control de ficha:

<TabControl c:Util.ShouldFocus="{Binding ShouldClearFocus}"> 

En mi modelo de vista, tengo una sencilla propiedad booleana ShouldClearFocus que es una elevación propiedad estándar a PropertyChangedEvent, por lo que el enlace de datos funciona. Luego, simplemente configuro ShouldClearFocus en true cuando quiero restablecer el enfoque. La propiedad adjunta establece automáticamente el foco y restablece el valor de la propiedad nuevamente. De esa manera puedo seguir configurando ShouldClearFocus sin tener que configurarlo en false en el medio.

La propiedad adjunta es una implementación estándar con esto como su manejador cambio:

public static void ShouldFocusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
{ 
    if (!(bool)e.NewValue || !(obj is FrameworkElement)) 
     return; 

    FrameworkElement element = (FrameworkElement)obj; 
    if (element.Focusable) 
     element.Focus(); 

    // reset value 
    BindingExpression bindingExpression = BindingOperations.GetBindingExpression(obj, ShouldFocusProperty); 
    if (bindingExpression != null) 
    { 
     PropertyInfo property = bindingExpression.DataItem.GetType().GetProperty(bindingExpression.ParentBinding.Path.Path); 
     if (property != null) 
      property.SetValue(bindingExpression.DataItem, false, null); 
    } 
    else 
     SetShouldFocus(obj, false); 
} 
+0

De lejos, la solución más flexible/elegante propuesta en este foro. Funcionó muy bien - gracias – BCA

2

Aquí es la clase que he usado para una situación similar:

public class TextBoxUpdatesTextBindingOnPropertyChanged : Behavior<TextBox> 
{ 
    protected override void OnAttached() 
    { 
     base.OnAttached(); 

     AssociatedObject.TextChanged += TextBox_TextChanged; 
    } 

    protected override void OnDetaching() 
    { 
     base.OnDetaching(); 

     AssociatedObject.TextChanged -= TextBox_TextChanged; 
    } 

    void TextBox_TextChanged(object sender, TextChangedEventArgs e) 
    { 
     var bindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty); 
     bindingExpression.UpdateSource(); 
    } 
} 

XAML:

<TextBox Text="{Binding UserName,Mode=TwoWay,UpdateSourceTrigger=Explicit}"> 
    <i:Interaction.Behaviors> 
     <h:TextBoxUpdatesTextBindingOnPropertyChanged /> 
    </i:Interaction.Behaviors> 
</TextBox> 

Calling OnPropertyChanged("YourPropertyName") alertará al objeto de actualizar el valor límite.

De lo contrario echa un vistazo a esta respuesta: WPF TextBox DataBind on EnterKey press.

+0

El problema no es el enlace desde la vista → datos, es al revés. Quiero forzar una actualización en el lado de los datos. – poke

+0

Bien, el enlace que publiqué muestra cómo crear un comportamiento que actualizará el enlace (en el lado de los datos) cuando se lanza un determinado evento en el Control. Si lo conecta a los eventos KeyPressed o KeyUp, debe obtener el efecto que necesita. –

+0

Esa solución incluye establecer 'UpdateSourceTrigger' en' PropertyChanged', que ya debería ser suficiente, pero no es lo que quiero, como dije en la pregunta. Estoy buscando una forma de hacer esto sin cambiar el gatillo. – poke

0

uso lo siguiente en mis proyectos. el problema es que el botón no recibió el foco.

en mis App.xaml.cs

EventManager.RegisterClassHandler(typeof(Button), ButtonBase.ClickEvent, new RoutedEventHandler(ButtonClick)); 

    private void ButtonClick(object sender, RoutedEventArgs e) 
    { 
     if (sender != null && sender is Button) 
     { 
      (sender as Button).Focus(); 
     } 
    } 
+0

Esa solución se vería aceptable, pero no tengo un botón que se haga clic. El comando es invocado por un 'KeyBinding'. – poke

9

En lugar de cambiar de enfoque que podría también acaba de actualizar el origen de enlace si el elemento actual es una TextBox. Podría hacer algo similar para otros controles, pero en mi experiencia solo he tenido este problema con TextBox.

// if the current focused element is textbox then updates the source. 
var focusedElement = Keyboard.FocusedElement as FrameworkElement; 

if (focusedElement is TextBox) 
{ 
    var expression = focusedElement.GetBindingExpression(TextBox.TextProperty); 
    if (expression != null) expression.UpdateSource(); 
} 
+0

Buena idea, ¿dónde pondrías eso en una configuración de MVVM? - Además, no sabía que realmente hay un método 'UpdateSource' en la expresión de enlace, ¡gracias por eso! – poke

+0

Creo que tendrías que ponerlo en tu propiedad adjunta, simplemente pensé que era una mejora en el avance del foco. También puede utilizar algo como CompositeCommand de Enterprise Library para manejar esta funcionalidad, pero realmente depende de cómo esté estructurada su aplicación. –

0

Creo que la mejor manera de hacer esto es establecer la unión de UpdateSourceTrigger=PropertyChanged, y luego usando Reactive Extensions para estrangular el flujo de eventos.De esta forma, la propiedad solo se actualizará una vez que el usuario haya dejado de escribir durante un tiempo determinado (1/2 segundo es una buena cantidad).

Esto es genial porque la propiedad no se actualizará todo el tiempo, pero también se actualizará sin necesidad de cambiar el foco. Exactamente lo que quieres

Here is some really useful information on Rx

Hay una biblioteca de extensiones MVVM que tiene IObservables enlazables here. El ejemplo que tienen aquí está haciendo exactamente lo que sugerí.

Usted pidió elegancia: aquí está. Y tampoco es tanto trabajo.

3

En su TextBox, debe configurar el UpdateSourceTrigger en su enlace de texto que define cuándo se actualizará la fuente con el valor de la caja de texto. Por defecto es LostFocus para la propiedad Text, que es exactamente lo que está sucediendo, solo actualiza la fuente cuando pierde el foco. Debe establecer el valor de UpdateSourceTrigger en PropertyChanged y se actualizará cada vez que cambie el valor del cuadro de texto.

p. Ej.

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

Path es la propiedad predeterminada cuando se utiliza el comando de unión, por lo que lo anterior es igual a Path=Foo

+0

De la pregunta: * "Además, realmente no quiero cambiar el enlace para actualizar cada cambio" *. – poke

0

Hay muchas cosas flexible/elegantes aquí. Estoy usando las propiedades de Control Name y CommandParameter para solucionar este problema.

Soy nuevo en WPF, por lo que me haga saber si no estoy haciendo las cosas bien;)

XAML:

<dxc:SimpleButton Name="okButton" Content="OK" Command="{Binding OKCommand}" CommandParameter="{Binding ElementName=okButton}" IsDefault="True" Width="100"/> 

C#:

private void OnOKCommand(object obj) 
    { 
     ((System.Windows.FrameworkElement)obj).Focus(); 

     // other code here 
    } 
Cuestiones relacionadas