2009-06-12 27 views
6

在此示例窗口中,從第一個文本框跳轉到最後一個文本框,然後到達擴展器標題。如何在WPF擴展器控件上設置TabIndex?

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

顯然,我想這個去第一個文本框,擴展器標題,然後是最後一個文本框。是否有一種簡單的方法將TabIndex分配給擴展器的頭部?

我試過強迫擴展器成爲使用KeyboardNavigation.IsTabStop="True"的tabstop,但是這會使整個擴展器變得焦點,而整個擴展器不會對空格作出反應。在兩個標籤後,標題再次被選中,我可以用空格鍵打開它。

編輯: 我會拋出一個賞金在那裏的任何人誰可以想出一個更乾淨的方式做到這一點 - 如果不是,那麼rmoore,你可以有代表。謝謝你的幫助。

+0

我更新了我的答案,你想 – jjxtra 2009-06-16 02:47:19

回答

10

將下面的代碼即使沒有TabIndex屬性也可以工作,因此需要包含它們以便更清楚地瞭解預期的Tab鍵順序。

<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

非常酷,有沒有辦法讓它在擴展時也尊重TabIndex? – rmoore 2009-06-15 23:41:41

3

我找到了一個方法,但有一些更好的東西。


通過Mole查看Expander,或者查看它由Blend生成的ControlTemplate,我們可以看到響應Space/Enter/Click/etc的標頭部分實際上是一個ToggleButton。現在壞消息了,因爲Header的ToggleButton對Expander的擴展屬性Up/Down/Left/Right有不同的佈局,所以它已經通過擴展器的ControlTemplate爲其指定了樣式。這阻礙了我們在Expander的資源中創建一個默認的ToggleButton樣式。

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

如果你有機會獲得後面的代碼,或不介意添加代碼隱藏的資源字典的膨脹機,那麼你就可以訪問切換按鈕,並在擴展設置tabIndex。 Loaded事件,像這樣:

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

您也可以只投sender對象到精通如果你需要它與多個擴展器一起工作,也是如此。 另一種選擇是爲擴展器創建自己的ControlTemplate並在其中進行設置。

編輯 我們也可以將代碼部分移動到AttachedProperty,使它更清潔和更容易使用:

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

而且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

是啊,我已經用我自己的ControlTemplate打周圍的一切,但它是一個絕對的野獸。就像你說的那樣,有一個更好的方法。 – Eclipse 2009-06-12 23:35:51

相關問題