2010-02-04 16 views
21

¿Hay alguna forma de configurar Focus de un control a otro con WPF Trigger s?WPF - Establecer el enfoque cuando se hace clic en un botón - Sin código detrás

Al igual que el siguiente ejemplo:

<Page 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition/> 
     <RowDefinition/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 

    <TextBox Name="txtName"></TextBox>  
    <TextBox Grid.Row="1" Name="txtAddress"></TextBox> 
    <Button Grid.Row="2" Content="Finish"> 
     <Button.Triggers> 
      <EventTrigger RoutedEvent="Button.Click"> 

       <!-- Insert cool code here--> 

      </EventTrigger> 
     </Button.Triggers> 
    </Button> 
    </Grid> 
</Page> 

¿Hay alguna manera para este EventTrigger para poner a centrarse en el cuadro de texto "txtName"?

Estoy tratando de encontrar la manera de hacer algo como esto usando un MVVM estricto. Si esto es algo que no debe hacerse a través del XAML (en MVVM), entonces está bien. Pero me gustaría ver algún tipo de documentación sobre cómo encaja en el patrón MVVM hacerlo fuera del XAML.

Respuesta

28

¿Usted ha considerado el uso de un comportamiento adjunto. Son simples de implementar y usan AttachedProperty's. Aunque todavía requiere código, este código se abstrae en una clase y se reutiliza. Pueden eliminar la necesidad de 'código detrás' y a menudo se usan con el patrón MVVM.

Pruebe este y vea si funciona para usted.

public class EventFocusAttachment 
{ 
    public static Control GetElementToFocus(Button button) 
    { 
     return (Control)button.GetValue(ElementToFocusProperty); 
    } 

    public static void SetElementToFocus(Button button, Control value) 
    { 
     button.SetValue(ElementToFocusProperty, value); 
    } 

    public static readonly DependencyProperty ElementToFocusProperty = 
     DependencyProperty.RegisterAttached("ElementToFocus", typeof(Control), 
     typeof(EventFocusAttachment), new UIPropertyMetadata(null, ElementToFocusPropertyChanged)); 

    public static void ElementToFocusPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     var button = sender as Button; 
     if (button != null) 
     { 
      button.Click += (s, args) => 
       { 
        Control control = GetElementToFocus(button); 
        if (control != null) 
        { 
         control.Focus(); 
        } 
       }; 
     } 
    } 
} 

Y luego, en el XAML hacer algo así ...

<Button 
    Content="Click Me!" 
    local:EventFocusAttachment.ElementToFocus="{Binding ElementName=textBox}" 
    /> 
<TextBox x:Name="textBox" /> 
+0

Me gusta esto. Voy a probarlo. – Vaccano

+0

Eso funcionó muy bien. Si bien usa código, no es el código detrás de la ventana, así que estoy de acuerdo. ¡Gracias! – Vaccano

+0

Esta es la única solución de trabajo que pude usar con DataTemplate y enfocar un cuadro de texto desde ViewModel. Gracias. – alexandrudicu

12

no estoy seguro, pero creo que se podría hacer algo como esto:

FocusManager.FocusedElement="{Binding ElementName=txtName}"> 

Disculpe si estoy equivocado, no estoy por el estudio visual así que no puedo probarlo.

+0

Me gusta esto, pero necesito un poco más de código para ver cómo podría funcionar. No pude encontrar una manera de configurar esto usando un EventTrigger. (Un guión gráfico no puede hacer eso por lo que puedo decir.) – Vaccano

+0

@Vaccano - Llego muy tarde a la conversación, pero si estás buscando cambiar el enfoque después del evento de clic como originalmente lo habías publicado, no necesita el EventTrigger en absoluto, puede simplemente establecer la propiedad del elemento enfocado que se haya señalado ... una vez que se hace clic en el botón, cambiará el foco al elemento enlazado. – Aaj

+0

Funciona aquí exactamente como lo pidió OP. Buen trabajo, caesay! –

1

¿Esto es lo que quieres?

<TextBox Name="txtName"></TextBox> 
    <TextBox Grid.Row="1" Name="txtAddress"></TextBox> 
    <Button Grid.Row="2" Content="Finish"> 
     <Button.Style> 
      <Style TargetType="{x:Type Button}"> 
       <EventSetter Event="Click" Handler="MoveFocusOnClick" /> 
      </Style> 
     </Button.Style> 
     <!--<Button.Triggers> 
      <EventTrigger RoutedEvent="Button.Click"> 
      </EventTrigger> 
     </Button.Triggers>--> 
    </Button> 

C#:

public void MoveFocusOnClick(object sender, RoutedEventArgs e) 
    { 
     Keyboard.Focus(txtName); // Or your own logic 
    } 
+0

Sí, si estoy dispuesto a usar el código, existen varias formas de lograrlo. Me preguntaba si hay una manera de hacerlo sin código detrás. – Vaccano

8

También es posible usar un comportamiento WPF ...

public class FocusElementAfterClickBehavior : Behavior<ButtonBase> 
{ 
    private ButtonBase _AssociatedButton; 

    protected override void OnAttached() 
    { 
     _AssociatedButton = AssociatedObject; 

     _AssociatedButton.Click += AssociatedButtonClick; 
    } 

    protected override void OnDetaching() 
    { 
     _AssociatedButton.Click -= AssociatedButtonClick; 
    } 

    void AssociatedButtonClick(object sender, RoutedEventArgs e) 
    { 
     Keyboard.Focus(FocusElement); 
    } 

    public Control FocusElement 
    { 
     get { return (Control)GetValue(FocusElementProperty); } 
     set { SetValue(FocusElementProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for FocusElement. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty FocusElementProperty = 
     DependencyProperty.Register("FocusElement", typeof(Control), typeof(FocusElementAfterClickBehavior), new UIPropertyMetadata()); 
} 

Aquí es el XAML a utilizar el comportamiento .

Incluir espacios de nombres:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:local="clr-namespace:WpfApplication1" 

Adjunte WPF Comportamiento de botón y el elemento se unen desea fijar el enfoque:

<Button Content="Focus" Width="75"> 
    <i:Interaction.Behaviors> 
     <local:FocusElementAfterClickBehavior FocusElement="{Binding ElementName=CheckBoxComboBox, Mode=OneWay}"/> 
    </i:Interaction.Behaviors> 
</Button> 
<ComboBox x:Name="CheckBoxComboBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Grid.Row="1"/> 

Así de esta manera usted no tiene ningún código detrás y es reutilizable en cualquier control que hereda de ButtonBase.

Espero que esto ayude a alguien.

+0

esto es mejor que la propiedad adjunta en el sentido de que tiene una mejor compatibilidad de mezcla. Sin embargo, si no estás utilizando blend, esto requeriría muchísimo más código xaml. –

+0

No olvide agregar una referencia a System.Windows.Interactivity también. –

0

Esto es lo mismo que la solución de Ian Oakes, pero hice un par de cambios menores.

  1. El tipo botón puede ser más general, a saber ButtonBase, para manejar más casos, como ToggleButton.
  2. El tipo de destino también puede ser más general, es decir, UIElement. Técnicamente, esto podría ser IInputElement, supongo.
  3. Hice el controlador de eventos estático para que no genere un cierre de tiempo de ejecución cada vez. Para asegurarme de que permanece estático, lo saqué de una lambda.

Muchas gracias a Ian.


public sealed class EventFocusAttachment 
{ 
    [DebuggerStepThrough] 
    public static UIElement GetTarget(ButtonBase b) { return (UIElement)b.GetValue(TargetProperty); } 

    [DebuggerStepThrough] 
    public static void SetTarget(ButtonBase b, UIElement target) { b.SetValue(TargetProperty, target); } 

    public static readonly DependencyProperty TargetProperty = 
     DependencyProperty.RegisterAttached("Target", 
              typeof(UIElement), 
              typeof(EventFocusAttachment), 
              new UIPropertyMetadata(null, TargetPropertyChanged)); 

    public static void TargetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs _) 
    { 
     ButtonBase bb; 
     if ((bb = o as ButtonBase) != null) 
      bb.Click += handler; 
    } 

    static void handler(Object o, RoutedEventArgs _) 
    { 
     UIElement target; 
     if ((target = GetTarget((ButtonBase)o)) != null) 
      target.Focus(); 
    } 
}; 

uso es básicamente el mismo que el anterior:

<ToggleButton z:EventFocusAttachment.Target="{Binding RelativeSource={RelativeSource Self}}" /> 

Tenga en cuenta que el evento puede apuntar/enfocar el botón originándose.

3

necesita un TriggerAction para invocar el método Focus() en el control deseado.

public class SetFocusTrigger : TargetedTriggerAction<Control> 
{ 
protected override void Invoke(object parameter) 
{ 
    if (Target == null) return; 

    Target.Focus(); 
} 
} 

Para ajustar el enfoque a un control, colocar una colección disparadores después de su LayoutRoot (o ningún control en realidad), seleccionar el evento como el gatillo, y seleccione el SetFocusTrigger como la clase para funcionar. En la declaración SetFocusTrigger, coloca el nombre del control que desea que reciba el foco utilizando la propiedad TargetName.

<Button x:Name="LayoutRoot" > 
<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Clicked"> 
     <local:SetFocusTrigger TargetName="StartHere"/> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

<TextBox x:Name="StartHere"/> 
</Button> 
+0

Es una idea genial y una solución muy compacta. –

Cuestiones relacionadas