2009-06-12 55 views
6

W tym przykładzie okno przechodzi z pierwszego pola tekstowego do ostatniego pola tekstowego, a następnie do nagłówka ekspandera.Jak ustawić element TabIndex na kontrolerze 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> 

Oczywiście, chciałbym to iść pierwszym polu tekstowym, nagłówek ekspandera, a następnie ostatni tekstowe. Czy istnieje prosty sposób przypisania TabIndex do nagłówka ekspandera?

Próbowałem wymusić ekspander jako tabulatury przy użyciu KeyboardNavigation.IsTabStop="True", ale to sprawia, że ​​cały ekspander uzyskać ostrość, a cały ekspander nie reaguje na spację. Po dwóch kolejnych zakładkach ponownie wybrano nagłówek i mogę go otworzyć za pomocą spacji.

Edytować: Wyrzucę nagrodę dla każdego, kto może wymyślić czystsze sposób, aby to zrobić - jeśli nie, to rmoore, możesz mieć przedstawiciela. Dzięki za pomoc.

+0

zaktualizowałem moją odpowiedź robić wszystko, co chcesz – jjxtra

Odpowiedz

10

Poniższy kod działają nawet bez właściwości TabIndex, są one dołączane w celu wyjaśnienia przewidywanej kolejności tabulacji.

<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

Bardzo fajne, czy istnieje sposób na to, aby po rozszerzeniu był zgodny z TabIndex? – rmoore

+0

Zajrzę do tego i zaktualizuję tę odpowiedź, gdy ją otrzymam. – jjxtra

+0

Bardzo miło, jak na razie! – Eclipse

3

Znalazłem sposób, ale musi być coś lepszego.


Patrząc na Expander przez Mole, lub patrząc na jego ControlTemplate generowane przez Blend możemy zobaczyć, że część nagłówka, która odpowiada na Space/Enter/Click/etc jest naprawdę ToggleButton. Teraz złe wieści, ponieważ ToggleButton Header ma inny układ dla rozwiniętych właściwości Expandera w górę/w dół/w lewo/w prawo, ma już style przypisane do niego za pomocą ControlTemplate Expander. Wyklucza to nas z robienia czegoś prostego, jak tworzenie domyślnego stylu ToggleButton w Zasobach Expander.

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

Jeśli masz dostęp do kodu tyłu, czy nie przeszkadza dodanie kodzie do zasobu słownika ekspander jest, można uzyskać dostęp do ToggleButton i ustawić TabIndex w Expander. załadowany wydarzenie tak:

<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; 
    } 
} 

można też po prostu rzucić przedmiot nadawcy do Exp ander też, jeśli potrzebujesz go do pracy z wieloma ekspanderami. Inną opcją jest stworzenie własnego ControlTemplate dla Expander (ów) i ustawienie go tam.

EDIT Możemy również przenieść część kodu do AttachedProperty, co znacznie czystsze i łatwiejsze w użyciu:

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

a 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

Tak, byłem zabawy z moim ControlTemplate, ale to absolutna bestia. Tak jak powiedziałeś, musi być lepszy sposób. – Eclipse