在此示例窗口中,Tabbing through从第一个文本框转到最后一个文本框,然后转到扩展程序标题。
<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 :(得分: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>
答案 1 :(得分:3)
我找到了一种方法,但必须有更好的东西。
通过Mole查看Expander,或者查看它由Blend生成的ControlTemplate,我们可以看到响应Space / Enter / Click / etc的标题部分实际上是一个ToggleButton。现在是坏消息,因为Header的ToggleButton为Expander的Expanded属性Up / Down / Left / Right提供了不同的布局,它已经通过Expander的ControlTemplate为它分配了样式。这使我们无法在Expander的资源中创建一个简单的ToggleButton样式。
如果你有权访问后面的代码,或者不介意在扩展器所在的资源字典中添加CodeBehind,那么你可以访问ToggleButton并在Expander.Loaded事件中设置TabIndex,如下所示:
<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;
}
}
如果需要使用多个扩展器,也可以将发送方对象强制转换为扩展器。 另一个选择是为扩展器创建自己的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;
}
}
}