2010-03-03 7 views
3

Travailler sur une application tactile qui dispose également d'un clavier attaché, j'ai le problème suivant:Garder le focus clavier sur un contrôle unique tout en beeing capable d'utiliser un ListBox

La fenêtre WPF a une zone de texte, qui devrait recevoir TOUTES les entrées au clavier. Il y a aussi des boutons et un ListBox, qui sont uniquement utilisés par TouchScreen (= Mouse).

Un exemple très simple ressemble à ceci:

<Window x:Class="KeyboardFocusTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1"> 
    <StackPanel> 
     <TextBox Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
       PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus"/> 
     <Button Click="Button_Click">Add</Button> 
     <ListBox ItemsSource="{Binding Strings}" /> 
    </StackPanel> 
</Window> 

Pour garder le TextBox toujours concentré, je fais juste:

private void TextBox_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e) 
{ 
    e.Handled = true; 
} 

Jusqu'à présent, si bien - le problème est maintenant que je peux Ne sélectionnez plus les éléments de la ListBox. Cela semble seulement fonctionner, si le ListBox a le focus de clavier. Mais si je perds le focus du clavier sur la TextBox, je ne peux plus entrer de texte sans cliquer dessus en premier.

Toutes les idées, suggestions de commentaires sont les bienvenues!

Répondre

2

Il existe peut-être une solution plus élégante pour cela, mais vous pouvez toujours gérer l'événement PreviewKeyDown au niveau de la fenêtre et passer le focus au TextBox s'il ne l'a pas déjà, au lieu de l'empêcher de perdre le focus. la première place. De cette façon, la ListBox peut utiliser le focus comme d'habitude, mais dès qu'une touche est pressée, la focus saute directement à la TextBox. En outre, vous pouvez filtrer les touches que vous ne voulez pas changer de focus - les touches fléchées viennent à l'esprit, qui pourraient alors être utilisées pour se déplacer vers le haut et vers le bas dans la ListBox.

Ajout d'un gestionnaire d'événements comme ce qui suit devrait faire l'affaire:

private void Window_PreviewKeyDown(object sender, KeyEventArgs e) 
{ 
    if (!textBox.IsFocused) 
    { 
     textBox.Focus(); 
    } 
} 
+0

Cela fonctionne, mais ce n'est pas facile à faire d'une manière réutilisable (= extension de balisage). Voir mon approche de ce problème en utilisant votre suggestion ci-dessous. –

3

Basé sur la suggestion de Nicolas (thx!), Voici une extension de balisage, qui est utilisé comme:

<TextBox Helpers:KeyboardFocusAttractor.IsAttracted="true" /> 

Cela semble fonctionner, et ANTS n'a montré aucune fuite de mémoire. Mais quand il s'agit de WPF et surtout d'événements et de liaisons, on ne sait jamais, alors utilisez-le avec soin!

public static class KeyboardFocusAttractor 
{ 
    public static readonly DependencyProperty IsAttracted = DependencyProperty.RegisterAttached("IsAttracted", 
     typeof (bool), typeof (KeyboardFocusAttractor), new PropertyMetadata(false, OnIsAttracted)); 

    private static void OnIsAttracted(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var isAttracted = (bool) e.NewValue; 
     var controlWithInputFocus = d as Control; 

     if (controlWithInputFocus != null) 
     { 
      if (isAttracted) 
      { 
       new KeyboardInputFocusEventManager(controlWithInputFocus); 
      } 
     } 
    } 

    public static void SetIsAttracted(DependencyObject dp, bool value) 
    { 
     dp.SetValue(IsAttracted, value); 
    } 

    public static bool GetIsAttracted(DependencyObject dp) 
    { 
     return (bool) dp.GetValue(IsAttracted); 
    } 

    private class KeyboardInputFocusEventManager 
    { 
     private readonly Control _control; 
     private Window _window; 

     public KeyboardInputFocusEventManager(Control control) 
     { 
      _control = control; 
      _control.Loaded += ControlLoaded; 
      _control.IsVisibleChanged += ControlIsVisibleChanged; 
      _control.Unloaded += ControlUnloaded; 
     } 

     private void ControlLoaded(object sender, RoutedEventArgs e) 
     { 
      _window = Window.GetWindow(_control); 
      if (_window != null) 
      { 
       _control.Unloaded += ControlUnloaded; 
       _control.IsVisibleChanged += ControlIsVisibleChanged; 
       if (_control.IsVisible) 
       { 
        _window.PreviewKeyDown += ParentWindowPreviewKeyDown; 
       } 
      } 
     } 

     private void ControlUnloaded(object sender, RoutedEventArgs e) 
     { 
      _control.Unloaded -= ControlUnloaded; 
      _control.IsVisibleChanged -= ControlIsVisibleChanged; 
     } 

     private void ControlIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) 
     { 
      if (_window != null) 
      { 
       _window.PreviewKeyDown -= ParentWindowPreviewKeyDown; 
      } 

      if (_control.IsVisible) 
      { 
       _window = Window.GetWindow(_control); 
       if (_window != null) 
       { 
        _window.PreviewKeyDown += ParentWindowPreviewKeyDown; 
       } 
      } 
     } 

     private void ParentWindowPreviewKeyDown(object sender, KeyEventArgs e) 
     { 
      Keyboard.Focus(_control); 
     } 
    } 
} 
Questions connexes