2009-11-18 2 views
0

J'ai un UserControl qui incorpore une zone de texte. Je veux définir le focus clavier à cette zone de texte par programme lorsque l'utilisateur clique sur un bouton. J'ai essayé ceci:Comment faire pour définir le focusfocus à une zone de texte dans un UserControl par programme?

private void Button_Click(object sender,EventArgs e) 
{ 
    Keyboard.Focus(MyUserControl); 
} 

pas de chance. Ensuite, je l'exposais dans la zone de texte UserControl si une propriété de type TextBox

private void Button_Click(object sender,EventArgs e) 
{ 
    Keyboard.Focus(MyUserControl.TextBox); 
} 

encore pas de chance. Enfin, j'ai créé un eventhandler dans UserControl pour gérer l'événement GotKeyboardFocus, en appelant la méthode Keyboard.Focus dans la zone de texte qui s'y trouve.

Encore une fois pas de chance ?!

Comment faire ceci?

EDIT: Le problème n'est pas lié à UserControls. C'est un problème lorsque vous essayez de passer le focus à un autre UIElement dans un Click ou un MouseDownHandler. Le code XAML ci-dessous raconte leur propre histoire: le focus passe à la zone de texte, mais il est renvoyé par la zone de liste.

<Window x:Class="FocusSpike.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <StackPanel> 
     <TextBox Name="FocusedTextBox" Height="30"> 
      <TextBox.Style> 
       <Style TargetType="TextBox"> 
        <Setter Property="Text" Value="I am unfocused..."/> 
        <Setter Property="Opacity" Value=".3"/> 
        <Style.Triggers> 
         <Trigger Property="IsKeyboardFocused" Value="True"> 
          <Trigger.EnterActions> 
           <BeginStoryboard> 
            <Storyboard AutoReverse="True"> 
             <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20"/> 
            </Storyboard> 
           </BeginStoryboard> 
          </Trigger.EnterActions> 
          <Setter Property="Text" Value="I am focused!"/> 
          <Setter Property="Opacity" Value="1"/> 
         </Trigger> 
        </Style.Triggers> 
       </Style> 
      </TextBox.Style> 
     </TextBox> 
     <Button>Click to steal focus.</Button> 
     <ListBox> 
      <ListBoxItem GotFocus="Listbox_GotFocus"> 
       <Label MouseDown="ListBoxItem_MouseDown"> 
        Click to restore focus 
       </Label> 
      </ListBoxItem> 
     </ListBox> 
    </StackPanel> 
</Window> 

using System.Windows; 
using System.Windows.Input; 
namespace FocusSpike 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      FocusedTextBox.Focus(); 
     } 

     private void ListBoxItem_MouseDown(object sender, MouseButtonEventArgs e) 
     { 
      Keyboard.Focus(FocusedTextBox);//This does not work, remove it! 
     } 

     private void Listbox_GotFocus(object sender, RoutedEventArgs e) 
     { 
      //Keyboard.Focus(FocusedTextBox);//uncomment to restore focus! 
     } 
    } 
} 

Répondre

3
MyUserControl.FindControl("TextBox1").Focus(); 

A défaut ...

(TextBox)(MyUserControl.FindControl("TextBox1")).Focus(); 
+0

ou MyUserControl.TextBox.Focus() – Zenuka

+0

Ouais, juste que parfois il ne les voit pas ... – tsilb

+0

Merci pour la réponse. Cela ne marche pas cependant ;-) Je commence à soupçonner que l'accent est mis sur la zone de texte mais immédiatement volé. Je vais enquêter plus loin. – Dabblernl

6

Vous pouvez obtenir votre .Focus() appel à coller à l'aide Dispatcher.BeginInvoke pour retarder l'appel Focus() réelle jusqu'à ce que le gestionnaire d'événements Click ou MouseDown est terminée et le le code appelant a fini de fonctionner.

Voici comment faire:

private void ListBoxItem_MouseDown(object sender, MouseButtonEventArgs e) 
{ 
    //Keyboard.Focus(FocusedTextBox); // May be necessary to uncomment this in some scenarios 

    Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => 
    { 
    Keyboard.Focus(FocusedTextBox); 
    }); 
} 

Voici la séquence des événements:

  1. WPF appelle votre gestionnaire MouseDown
  2. Votre gestionnaire MouseDown demande au répartiteur WPF pour le rappeler lorsque toutes les invocations DispatcherPriority.Input (et supérieures) ont été effectuées
  3. Votre gestionnaire MouseDown est renvoyé et le code appelant termine son traitement 01 Tous les autres événements d'entrée dans la file d'attente sont traités
  4. Votre action est appelée et votre code appelle pour définir le focus.

Le point clé ici est que KeyboardFocus.Focus() n'est pas appelé jusqu'à ce que votre gestionnaire est de retour et WPF a terminé tout traitement d'entrée dans l'attente.

Il est possible que l'appel lui-même déclenche ListBox pour vous dérober le focus. Je n'ai pas vérifié cela. Si tel est le cas, la solution serait de l'appeler deux fois: une fois à partir du gestionnaire MouseDown et à nouveau à partir de l'action prévue par le gestionnaire MouseDown. Donc, si cela ne fonctionne pas avec la première ligne commentée, décommentez-la et essayez à nouveau.

+0

Je ne comprends pas: c'est un appel asynchrone et même s'il commence APRÈS la méthode qui l'appelle est terminée. Voulez-vous dire "retarder" l'achat de temps et espérer obtenir le bon timing (obtenir des résultats différents entre les ordinateurs en fonction de leur vitesse), ou voulez-vous dire que la méthode BeginInvoked ne sera pas exécutée correctement une fois la méthode d'appel terminée? Ce serait étrange: que se passe-t-il si je suis dans une humeur maligne dans un fil de sommeil juste après le BeginInvoke? – Dabblernl

+0

Ceci n'est pas lié à la synchronisation ou à la vitesse de l'ordinateur. L'appel BeginInvoke que j'ai donné exécute l'appel KeyBoard.Focus() dans une séquence déterministe. Plus précisément, il l'exécute après: 1. L'invocation en cours a été complétée, 2. Toutes les invocations au-dessus de DispatcherPriority.Input ont été complétées et 2. Toutes les invocations * actuellement planifiées * à DispatcherPriority.Input ont été complétées. Au moment précis où cette condition est satisfaite (quand c'est le cas), votre code 'KeyboardFocus (FocusedTextBox)' sera exécuté. –

+0

'BeginInvoke' ne sera vraiment pas exécuté tant que la méthode appelant n'est pas terminée. Si vous mettez un 'Thread.Sleep()' juste après le 'BeginInvoke', le focus ne changera pas jusqu'à la fin de l'intervalle de sommeil. Bien sûr, 'Thread.Sleep()' verrouillerait également le reste de l'interface utilisateur. –

Questions connexes