web-dev-qa-db-fra.com

WPF - Définir le focus quand un bouton est cliqué - Pas de code derrière

Est-il possible de définir Focus d'un contrôle à un autre à l'aide de WPF Triggers?

Comme dans l'exemple suivant:

<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>

Existe-t-il un moyen pour que EventTrigger se concentre sur la zone de texte "txtName"?

J'essaie de trouver le moyen de faire quelque chose comme ça en utilisant un MVVM strict. Si c'est quelque chose qui ne devrait pas être fait via le XAML (dans MVVM), alors c'est très bien. Mais j'aimerais voir une sorte de documentation sur la manière dont cela s'insère dans le modèle MVVM en dehors du XAML.

22
Vaccano

Avez-vous envisagé d'utiliser un comportement attaché. Ils sont simples à mettre en œuvre et à utiliser AttachedProperty. Bien qu'il ait encore besoin de code, ce code est extrait d'une classe et réutilisé. Ils peuvent éliminer le besoin de "code derrière" et sont souvent utilisés avec le modèle MVVM.

Essayez celui-ci et voyez si cela fonctionne pour vous.

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

Et ensuite, dans votre XAML, faites quelque chose comme ...

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

Je ne suis pas près du studio visuel, donc je ne peux pas vraiment essayer cela pour le moment, mais vous devriez pouvoir faire quelque chose comme ceci:

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

Modifier:

Il y a une question complémentaire (posée plus récemment) à ce sujet ici: Comment définir l'autofocus uniquement dans xaml? qui contient cette méthode et quelques idées différentes sur son utilisation.

14
caesay

Vous pouvez également utiliser un comportement 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());
}

Voici le code XAML pour utiliser le comportement.

Inclure les espaces de noms:

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

Attachez le comportement WPF au bouton et liez l'élément sur lequel vous souhaitez définir le focus: 

<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"/>

Donc, de cette façon, vous n'avez pas de code derrière et il est réutilisable sur tout contrôle hérité de ButtonBase.

J'espère que ça aide quelqu'un.

10
droidalmatter

vous avez besoin d'une TriggerAction pour appeler la méthode Focus () sur le contrôle souhaité.

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

    Target.Focus();
 }
}

Pour que le focus soit défini sur un contrôle, vous placez une collection Triggers après votre LayoutRoot (ou tout contrôle en réalité), sélectionnez l'événement en tant que déclencheur, puis sélectionnez SetFocusTrigger en tant que classe à exécuter. Dans la déclaration SetFocusTrigger, vous indiquez le nom du contrôle dont vous souhaitez recevoir le focus à l'aide de la propriété 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>
4
ar.gorgin

C'est ce que tu veux?

    <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
    }
1
mg007

Regardez si vous utilisez un Dispatcher, ce serait utile, mais c’est une astuce que j’ai utilisée dans mon code. Utilisez simplement l'événement Loaded dans votre XAML et créez un nouveau gestionnaire. Dans ce gestionnaire, collez ce code et le bingo! tu es prêt à partir

Votre événement chargé avec quelques arguments ici ...

{
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new System.Action(() => {
        The element to be focused goes here......
    }));
}

PS: Il nécessite Windows. Enfiler si vous ne saviez pas;)

0
Yash Joshi

C’est la même chose que la solution de Ian Oakes, mais j’ai apporté quelques modifications mineures. 

  1. Le type de bouton peut être plus général, à savoir ButtonBase, pour traiter plusieurs cas, tels que ToggleButton.
  2. Le type de cible peut également être plus général, à savoir UIElement. Techniquement, cela pourrait être IInputElement, je suppose.
  3. J'ai rendu le gestionnaire d'événements statique afin qu'il ne génère pas de fermeture du runtime à chaque fois. Pour être sûr qu'il reste statique, je l'ai sorti d'un lambda.

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

L'utilisation est fondamentalement la même que ci-dessus:

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

Notez que l'événement peut cibler/focaliser le bouton d'origine lui-même.

0
Glenn Slayden