2009-06-12 13 views
6

En esta ventana de ejemplo, la tabulación va desde el primer cuadro de texto hasta el último cuadro de texto y luego al encabezado del expansor.¿Cómo puedo configurar TabIndex en un control WPF Expander?

<Window x:Class="ExpanderTab.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" 
    FocusManager.FocusedElement="{Binding ElementName=FirstField}"> 
    <StackPanel> 
     <TextBox TabIndex="10" Name="FirstField"></TextBox> 
     <Expander TabIndex="20" Header="_abc"> 
      <TextBox TabIndex="30"></TextBox> 
     </Expander> 
     <TextBox TabIndex="40"></TextBox> 
    </StackPanel> 
</Window> 

Obviamente, me gustaría que fuera Primer cuadro de texto, encabezado de expansión, luego el último cuadro de texto. ¿Hay alguna manera fácil de asignar un TabIndex al encabezado del expansor?

He intentado forzar el expansor para que sea una tabstop usando KeyboardNavigation.IsTabStop="True", pero eso hace que todo el expansor se enfoque, y todo el expansor no reacciona a la barra espaciadora. Después de dos pestañas más, el encabezado se selecciona nuevamente y puedo abrirlo con la barra espaciadora.

Edit: Voy a arrojar una recompensa por ahí para cualquier persona que pueda encontrar una manera más limpia de hacerlo - si no, entonces rmoore, puede tener el representante. Gracias por tu ayuda.

+0

He actualizado mi respuesta a hacer todo lo que quiere – jjxtra

Respuesta

10

el siguiente código funcionan incluso sin las propiedades de TabIndex, se incluyen para mayor claridad sobre el orden de tabulación esperado.

<Window x:Class="ExpanderTab.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" FocusManager.FocusedElement="{Binding ElementName=FirstField}"> 
    <StackPanel> 
     <TextBox TabIndex="10" Name="FirstField"></TextBox> 
     <Expander TabIndex="20" Header="Section1" KeyboardNavigation.TabNavigation="Local"> 
      <StackPanel KeyboardNavigation.TabNavigation="Local"> 
       <TextBox TabIndex="30"></TextBox> 
       <TextBox TabIndex="40"></TextBox> 
      </StackPanel> 
     </Expander> 
     <Expander TabIndex="50" Header="Section2" KeyboardNavigation.TabNavigation="Local"> 
      <StackPanel KeyboardNavigation.TabNavigation="Local"> 
       <TextBox TabIndex="60"></TextBox> 
       <TextBox TabIndex="70"></TextBox> 
      </StackPanel> 
     </Expander> 
     <TextBox TabIndex="80"></TextBox> 
    </StackPanel> 
</Window> 
+0

Muy bien, ¿hay alguna manera de que respete el TabIndex cuando se expande también? – rmoore

+0

Voy a investigar y actualizar esta respuesta una vez que la tenga – jjxtra

+0

¡Muy bien hasta ahora! – Eclipse

3

Encontré una manera, pero tiene que haber algo mejor.


Mirando el Expansor a través de Mole, o mirando su ControlTemplate generado por Blend, podemos ver que la parte del encabezado que responde a Espacio/Entrar/Clic/etc es realmente un ToggleButton. Ahora las malas noticias son que, debido a que el ToggleButton del encabezado tiene un diseño diferente para las propiedades expandidas del expansor Arriba/Abajo/Izquierda/Derecha, ya tiene estilos asignados a él a través de ControlTemplate del expansor. Eso nos impide hacer algo simple como crear un estilo ToggleButton predeterminado en los recursos del expansor.

alt text http://i44.tinypic.com/2dlq1pl.png

Si usted tiene acceso al código detrás, o no le importa la adición de un CodeBehind al diccionario de recursos que el expansor está en, a continuación, puede acceder a la ToggleButton y establecer el ÍndiceDeTabulación en el expansor. evento cargado, como esto:

<Expander x:Name="uiExpander" 
      Header="_abc" 
      Loaded="uiExpander_Loaded" 
      TabIndex="20" 
      IsTabStop="False"> 
    <TextBox TabIndex="30"> 

    </TextBox> 
</Expander> 


private void uiExpander_Loaded(object sender, RoutedEventArgs e) 
{ 
    //Gets the HeaderSite part of the default ControlTemplate for an Expander. 
    var header = uiExpander.Template.FindName("HeaderSite", uiExpander) as Control; 
    if (header != null) 
    { 
     header.TabIndex = uiExpander.TabIndex; 
    } 
} 

también puede simplemente enviar el objeto emisor a un Exp ander también, si lo necesitas para trabajar con múltiples expansores. La otra opción es crear su propio ControlTemplate para el Expansor (es) y configurarlo allí.

EDITAR También podemos mover la parte de código a un AttachedProperty, por lo que es mucho más limpio y más fácil de usar:

<Expander local:ExpanderHelper.HeaderTabIndex="20"> 
    ... 
</Expander> 

Y el AttachedProperty:

public class ExpanderHelper 
{ 
    public static int GetHeaderTabIndex(DependencyObject obj) 
    { 
     return (int)obj.GetValue(HeaderTabIndexProperty); 
    } 

    public static void SetHeaderTabIndex(DependencyObject obj, int value) 
    { 
     obj.SetValue(HeaderTabIndexProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for HeaderTabIndex. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty HeaderTabIndexProperty = 
     DependencyProperty.RegisterAttached(
     "HeaderTabIndex", 
     typeof(int), 
     typeof(ExpanderHelper), 
     new FrameworkPropertyMetadata(
      int.MaxValue, 
      FrameworkPropertyMetadataOptions.None, 
      new PropertyChangedCallback(OnHeaderTabIndexChanged))); 

    private static void OnHeaderTabIndexChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
     var expander = o as Expander; 
     int index; 

     if (expander != null && int.TryParse(e.NewValue.ToString(), out index)) 
     { 
      if (expander.IsLoaded) 
      { 
       SetTabIndex(expander, (int)e.NewValue); 
      } 
      else 
      { 
       // If the Expander is not yet loaded, then the Header will not be costructed 
       // To avoid getting a null refrence to the HeaderSite control part we 
       // can delay the setting of the HeaderTabIndex untill after the Expander is loaded. 
       expander.Loaded += new RoutedEventHandler((i, j) => SetTabIndex(expander, (int)e.NewValue)); 
      } 
     } 
     else 
     { 
      throw new InvalidCastException(); 
     } 
    } 

    private static void SetTabIndex(Expander expander, int index) 
    { 
     //Gets the HeaderSite part of the default ControlTemplate for an Expander. 
     var header = expander.Template.FindName("HeaderSite", expander) as Control; 
     if (header != null) 
     { 
      header.TabIndex = index; 
     } 
    } 
} 
+0

Sí, he estado jugando con mi propia ControlTemplate, pero es una bestia absoluta. Como dijiste, tiene que haber una mejor manera. – Eclipse

Cuestiones relacionadas