WPF et focus initial

Il semble que quand une application WPF démarre, rien n’a le focus.

C’est vraiment bizarre. Chaque autre framework que j’ai utilisé fait exactement ce que vous attendez: place le focus initial sur le premier contrôle dans l’ordre de tabulation. Mais j’ai confirmé que c’est WPF, pas seulement mon application – si je crée une nouvelle fenêtre et y mets juste un TextBox, et que je lance l’application, le TextBox ne sera pas actif tant que je n’aurai pas cliqué dessus ou appuyé sur Tab . Beurk.

Mon application réelle est plus compliquée qu’une simple TextBox. J’ai plusieurs couches de UserControls dans UserControls. L’un de ces UserControls a Focusable = “True” et les gestionnaires KeyDown / KeyUp, et je veux qu’il soit actif dès que ma fenêtre s’ouvre. Je suis encore un peu novice de WPF, et je n’ai pas beaucoup de chance de trouver comment le faire.

Si je lance mon application et que j’appuie sur la touche Tab, la focalisation se fait sur mon contrôle focalisable et commence à fonctionner comme je le souhaite. Mais je ne veux pas que mes utilisateurs doivent cliquer sur Tab avant de pouvoir utiliser la fenêtre.

J’ai joué avec FocusManager.FocusedElement, mais je ne suis pas sûr de savoir sur quel contrôle il faut le placer (la fenêtre de niveau supérieur? Le parent qui contient le contrôle focalisable? Le contrôle focalisable lui-même?)

Que dois-je faire pour que mon contrôle profondément nested ait le focus initial dès que la fenêtre s’ouvre? Ou mieux encore, pour concentrer le premier contrôle focalisable dans l’ordre de tabulation?

    J’ai eu la bonne idée de fouiller dans Reflector pour voir où la propriété Focusable est utilisée, et j’ai trouvé la voie vers cette solution. Je dois juste append le code suivant au constructeur de ma fenêtre:

    Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); 

    Cela sélectionnera automatiquement le premier contrôle dans l’ordre de tabulation, c’est donc une solution générale qui devrait pouvoir être placée dans n’importe quelle fenêtre et fonctionner.

    Cela fonctionne aussi:

       ...   

    Basé sur la réponse acceptée implémentée en tant que comportement associé:

     using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace UI.Behaviors { public static class FocusBehavior { public static readonly DependencyProperty FocusFirstProperty = DependencyProperty.RegisterAttached( "FocusFirst", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, OnFocusFirstPropertyChanged)); public static bool GetFocusFirst(Control control) { return (bool)control.GetValue(FocusFirstProperty); } public static void SetFocusFirst (Control control, bool value) { control.SetValue(FocusFirstProperty, value); } static void OnFocusFirstPropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args) { Control control = obj as Control; if (control == null || !(args.NewValue is bool)) { return; } if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); } } } } 

    Utilisez-le comme ceci:

      

    J’ai trouvé une autre solution possible. Mark Smith a publié une extension de balisage FirstFocusedElement à utiliser avec FocusManager.FocusedElement.

      

    Après avoir eu un «cauchemar de focus initial WPF» et basé sur des réponses sur stack, ce qui suit s’est avéré être la meilleure solution.

    Tout d’abord, ajoutez votre App.xaml OnStartup () aux éléments suivants:

     EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent, new RoutedEventHandler(WindowLoaded)); 

    Ajoutez ensuite l’événement ‘WindowLoaded’ également dans App.xaml:

     void WindowLoaded(object sender, RoutedEventArgs e) { var window = e.Source as Window; System.Threading.Thread.Sleep(100); window.Dispatcher.Invoke( new Action(() => { window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); })); } 

    Le problème de threading doit être utilisé car le focus initial de WPF échoue principalement en raison de certaines conditions de concurrence du framework.

    J’ai trouvé la solution suivante car elle est utilisée globalement pour l’ensemble de l’application.

    J’espère que cela aide…

    Oran

    Avait le même problème résolu avec une solution simple: Dans la fenêtre principale:

       

    Dans le contrôle utilisateur:

     private void UserControl_GotFocus_1(object sender, RoutedEventArgs e) { targetcontrol.Focus(); this.GotFocus -= UserControl_GotFocus_1; // to set focus only once } 

    Vous pouvez facilement faire en sorte que le contrôle se positionne comme élément focalisé dans XAML.

       ...   

    Je n’ai jamais essayé de mettre cela dans une commande utilisateur et de voir si cela fonctionne, mais cela peut arriver.

    Une version minimale de la réponse de Mizipzor pour C # 6+.

     public static class FocusBehavior { public static readonly DependencyProperty GiveInitialFocusProperty = DependencyProperty.RegisterAttached( "GiveInitialFocus", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, OnFocusFirstPropertyChanged)); public static bool GetGiveInitialFocus(Control control) => (bool)control.GetValue(GiveInitialFocusProperty); public static void SetGiveInitialFocus(Control control, bool value) => control.SetValue(GiveInitialFocusProperty, value); private static void OnFocusFirstPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = obj as Control; if (control == null || !(args.NewValue is bool)) return; if ((bool)args.NewValue) control.Loaded += OnControlLoaded; else control.Loaded -= OnControlLoaded; } private static void OnControlLoaded(object sender, RoutedEventArgs e) => ((Control)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); } 

    Utilisez dans votre XAML:

      

    Si vous êtes comme moi et que vous utilisez des frameworks qui, d’une manière ou d’une autre, gênent les comportements de focus de base et rendent toutes les solutions non pertinentes, vous pouvez toujours le faire:

    1 – Notez l’élément qui reçoit le focus (quel qu’il soit!)

    2 – Ajoutez ceci dans votre code derrière xxx.xaml.cs

     private bool _firstLoad; 

    3 – Ajoutez ceci sur l’élément qui obtient le premier focus:

     GotFocus="Element_GotFocus" 

    4 – Ajoutez la méthode Element_GotFocus dans le code et spécifiez l’élément nommé WPF qui nécessite le premier focus:

     private void Element_GotFocus(object sender, RoutedEventArgs e) { if(_firstLoad) { this.MyElementWithFistFocus.Focus(); _firstLoad = false; } } 

    J’espère que cela aidera en tant que solution de dernier recours

      

    J’ai aussi fait face au même problème. J’avais trois zones de texte à l’intérieur du conteneur de canevas et je voulais que la première zone de texte soit focalisée lorsque le contrôle utilisateur s’ouvrirait. Le code WPF a suivi le modèle MVVM. J’ai créé une classe de comportement distincte pour focaliser l’élément et l’ai lié à ma vue comme celle-ci.

    Code de comportement du canevas

     public class CanvasLoadedBehavior : Behavior { private Canvas _canvas; protected override void OnAttached() { base.OnAttached(); _canvas = AssociatedObject as Canvas; if (_canvas.Name == "ReturnRefundCanvas") { _canvas.Loaded += _canvas_Loaded; } } void _canvas_Loaded(object sender, RoutedEventArgs e) { FocusNavigationDirection focusDirection = FocusNavigationDirection.Next; // MoveFocus takes a TraveralReqest as its argument. TraversalRequest request = new TraversalRequest(focusDirection); UIElement elementWithFocus = Keyboard.FocusedElement as UIElement; if (elementWithFocus != null) { elementWithFocus.MoveFocus(request); } } } 

    Code pour voir

                                                                     

    La solution ci-dessus ne fonctionnait pas comme prévu pour moi, j’ai légèrement modifié le comportement proposé par Mizipzor comme suit:

    De cette partie

     if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); } 

    Pour ça

     if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.Focus(); } 

    Et je ne rattache pas ce comportement à Window ou UserControl, mais pour contrôler, je veux d’abord me concentrer, par exemple:

      

    Oh, désolé pour les noms différents J’utilise le nom InitialFocus pour la propriété attachée.

    Et cela fonctionne pour moi, peut-être que cela pourrait aider quelqu’un d’autre.