2009-06-12 32 views
6

Trong cửa sổ ví dụ này, duyệt qua từ hộp văn bản đầu tiên, đến hộp văn bản cuối cùng và sau đó đến tiêu đề mở rộng.Làm cách nào tôi có thể đặt TabIndex trên điều khiển 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> 

Rõ ràng, tôi muốn điều này để đi Hộp văn bản đầu tiên, tiêu đề giãn nở, sau đó hộp văn bản cuối cùng. Có cách nào dễ dàng để chỉ định một TabIndex cho tiêu đề của expander?

Tôi đã thử buộc trình giãn mở rộng là tabstop sử dụng KeyboardNavigation.IsTabStop="True", nhưng điều đó làm cho toàn bộ tiện ích mở rộng tập trung và toàn bộ trình giãn nở không phản ứng với phím cách. Sau hai tab khác, tiêu đề lại được chọn và tôi có thể mở nó bằng phím cách.

Chỉnh sửa: Tôi sẽ ném tiền thưởng ra cho bất kỳ ai có thể tìm ra cách làm sạch hơn - nếu không, sau đó bỏ qua, bạn có thể có đại diện. Cảm ơn bạn đã giúp đỡ.

+0

Tôi đã cập nhật câu trả lời của mình để làm mọi thứ bạn muốn – jjxtra

Trả lời

10

Các mã sau đây sẽ làm việc ngay cả khi không có thuộc tính TabIndex, chúng được bao gồm để làm rõ về thứ tự tab mong đợi.

<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

Rất hay, có cách nào để nó tôn trọng TabIndex khi mở rộng không? – rmoore

+0

Tôi sẽ xem xét điều đó và cập nhật câu trả lời này sau khi tôi có nó – jjxtra

+0

Rất tốt đẹp cho đến nay! – Eclipse

3

Tôi đã tìm được cách, nhưng phải có điều gì đó tốt hơn.


Nhìn vào Expander qua Mole, hoặc nhìn vào ControlTemplate được tạo bởi Blend, chúng ta có thể thấy phần tiêu đề phản hồi Space/Enter/Click/etc thực sự là một ToggleButton. Bây giờ tin xấu, Bởi vì ToggleButton của Header có một bố cục khác nhau cho các thuộc tính mở rộng của Expander Up/Down/Left/Right, nó đã có kiểu được gán cho nó thông qua ControlTemplate của Expander. Điều đó ngăn cản chúng ta làm một cái gì đó đơn giản như tạo một kiểu ToggleButton mặc định trong Tài nguyên của Expander.

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

Nếu bạn có quyền truy cập vào mã phía sau, hoặc không nhớ thêm một CodeBehind vào từ điển Resource rằng nở là vào, sau đó bạn có thể truy cập vào ToggleButton và thiết lập tabIndex trong Expander. sự kiện Loaded, như thế này:

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

Bạn cũng có thể chỉ cast object sender một Exp và cũng vậy, nếu bạn cần nó để làm việc với nhiều bộ mở rộng. Tùy chọn khác, là tạo ControlTemplate của riêng bạn cho Expander (s) và thiết lập nó trong đó.

EDIT Chúng tôi cũng có thể di chuyển phần mã để một AttachedProperty, làm cho nó sạch hơn và dễ dàng hơn để sử dụng:

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

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

Vâng, tôi đã chơi xung quanh với ControlTemplate của riêng mình, nhưng đó là một con thú tuyệt đối. Như bạn đã nói, có một cách tốt hơn. – Eclipse

Các vấn đề liên quan