2010-09-15 3 views
2

J'ai un UserControl WPF qui contient un StackPanel rendu visible suite à un changement d'état. Lorsque le StackPanel devient visible, je veux définir le focus du clavier sur un TextBox enfant particulier. J'ai trouvé (après beaucoup d'essais et d'erreurs) que l'appel à TextBox.Focus() ne parviendra pas à mettre au point (et retourne false) à moins que je conclurai cet appel dans un appel BeginInvoke comme indiqué ici:Pourquoi dois-je appeler Dispatcher.BeginInvoke à SetFocus?

private void CtlClientLookupPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { 
     LogThreadMsg(string.Format("CtlClientLookupPanel_IsVisibleChanged to {0}", CtlClientLookupPanel.Visibility)); 
     if (CtlClientLookupPanel.Visibility == Visibility.Visible) { 
      Dispatcher.BeginInvoke((ThreadStart)delegate { 
       bool gotFocus = CtlClientSearchText.Focus(); 
       LogThreadMsg(string.Format("CtlClientSearchText.Focus() returned {0}", gotFocus)); 
      }); 
     } 
    } 

    private void LogThreadMsg(string msg) { 
     string fullMsg = string.Format("Thread: {0} - {1}", Thread.CurrentThread.ManagedThreadId, msg); 
     System.Diagnostics.Trace.WriteLine(msg); 
    } 

les deux appels LogThreadMsg indiquent qu'ils sont sur le même fil (IU) comme indiqué ici:

[5232] Thread: 1 - CtlClientLookupPanel_IsVisibleChanged to Visible 
[5232] Thread: 1 - CtlClientSearchText.Focus() returned True 

Alors pourquoi est-ce « hack » nécessaire? Cela semble être une sorte de problème de synchronisation et je cherchais un événement en aval qui serait peut-être un meilleur endroit pour appeler Focus() sans avoir recours à cela, mais je ne l'ai pas trouvé. Quelqu'un peut-il expliquer ce qui se passe ici?

Répondre

3

C'est en effet un problème de synchronisation. Quand CtlClientLookupPanel devient visible, je pense que votre TextBox n'est pas encore visible et ne peut pas être mis au point. Vous pouvez essayer de gérer l'événement IsVisibleChanged sur le TextBox au lieu de

+0

Cela a fait l'affaire. Merci! –

1

En fait, il peut être plus exactement dit que la zone de texte n'est pas encore rendu lorsque vous essayez de le faire. WPF a une sorte de pompe de messages à son coeur, un peu comme WinForms, mais beaucoup plus avancé - le Dispatcher. Le Dispatcher est utilisé pour le travail en file d'attente - certaines actions que vous avez déclenchées sont exécutées plus tard, sur le même thread, lorsque les messages de la file sont traités en fonction de leurs priorités. BeginInvoke met en file d'attente un autre élément de travail dans la file d'attente Dispatcher et il est exécuté après d'autres éléments requis en premier.

C'est une explication très hack-ish, et je vous encourage à en lire plus à ce sujet - il suffit de google WPF Dispatcher et lire toute la tonne d'articles, la plupart sont très bons. Editer: aussi, un bon événement à gérer dans votre cas serait l'événement Loaded de la zone de texte; Généralement, l'événement Loaded est déclenché uniquement après que tous les autres travaux de mise en page sont terminés et qu'un contrôle est réellement visible. Cependant, la mise en file d'attente des éléments sur Dispatcher est également un bon moyen de faire les choses.

+0

Merci pour la bonne explication. En fait, le TextBox (et StackPanel parent) sont chargés précédemment dans le cadre de UserControl et initialement défini sur Collapsed; l'événement Loaded ne fonctionne pas dans mon cas. Cependant, définir le focus sur IsVisibleChanged de la TextBox a fait l'affaire. –

Questions connexes