3

我有一个 WPF 页面,其中包含几个带有选项卡顺序集的开箱即用控件。

我有一个自定义控件(NumericSpinner),其中包含:边框/网格/文本框/2 重复按钮(上/下)。

两个问题:

1)当我在自定义选择器控件的文本框中时,我无法将其跳出到页面上的其他控件。但是,单击向上/向下箭头之一后,我可以切换到其他控件。

2)我无法按顺序进入自定义控件的文本框。只有在我浏览了所有控件之后,光标才会落在文本框中(并且无法退出)。

语境:

<ComboBox Margin="97,315,21,0" Name="txtdweldatcdu" Style="{StaticResource fieldComboBoxStyle}" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" VerticalAlignment="Top" TabIndex="10" />
    <WpfControls:NumericSpinner Margin="97,338,21,0" Name="txtdweldatpctcomplete" HorizontalAlignment="Left" VerticalAlignment="Top" AllowNegativeValues="True" MaxValue="100" TabIndex="11" />
    <ComboBox Margin="97,363,21,0" Name="txtdweldatclass" Style="{StaticResource fieldComboBoxStyle}" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" VerticalAlignment="Top" TabIndex="12" />

自定义控件的一部分:

 <Border BorderThickness="1" BorderBrush="Gray" Margin="0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Width="117">
        <Grid Margin="0">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="98"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBox Name="valueText" 
                     BorderThickness="0" 
                     Grid.RowSpan="2"
                     Style="{StaticResource spinnerTextBoxStyle}"
                     PreviewKeyDown="valueText_PreviewKeyDown"
                     PreviewTextInput="valueText_PreviewTextInput"
                     TextChanged="valueText_TextChanged"
                     IsReadOnly="{Binding ElementName=Spinner, Path=IsReadOnly}"
                     Text="{Binding ElementName=Spinner, Path=Value, Mode=TwoWay}"
                     KeyboardNavigation.IsTabStop="True"
                     AcceptsTab="True"/>
            <RepeatButton Name="upButton" Style="{StaticResource spinnerRepeatButtonStyle}" Click="upButton_Click"  Grid.Column="1" Grid.Row="0" Height="10" Width="18" VerticalAlignment="Top" HorizontalAlignment="Right" HorizontalContentAlignment="Center">
                <Polygon  HorizontalAlignment="Center" Points="3,2 2,3 4,3"  Fill="Black"  Stretch="Uniform"  Stroke="Black"  StrokeThickness="0" />
            </RepeatButton>
            <RepeatButton Name="downButton" Style="{StaticResource spinnerRepeatButtonStyle}" Click="downButton_Click"  Grid.Column="1" Grid.Row="1" Height="10" Width="18" VerticalAlignment="Top" HorizontalAlignment="Right" HorizontalContentAlignment="Center">
                <Polygon  HorizontalAlignment="Center" Points="2,2 4,2 3,3"  Fill="Black"  Stretch="Uniform"  Stroke="Black"  StrokeThickness="0" />
            </RepeatButton>
        </Grid>
    </Border>

自定义控件由 xaml 和代码隐藏文件组成。

包含所有控件的父 xaml 页面是动态加载的,并且不包含任何代码隐藏。

在自定义控件的构造函数中,我将以下内容设置为测试:

    valueText.TabIndex = 3;
    this.TabIndex = 3;

我第四次制表时,我实际上将光标移到了文本字段中,但是我无法跳出它。

考虑到这一点,第一步是创建一个控件参数,我可以传递一个选项卡顺序编号,该编号将在控件的代码隐藏中设置。

我创建了一个 CustomTabIndex 属性:

/// <summary>
/// Custom tab index property
/// </summary>
public int CustomTabIndex
{
    get { return (int)GetValue(CustomTabIndexProperty); }
    set { SetValue(CustomTabIndexProperty, value); }
}

public static readonly DependencyProperty CustomTabIndexProperty = 
    DependencyProperty.Register("CustomTabIndex", typeof(int), typeof(NumericSpinner));

在 xaml 中,当我尝试设置 CustomTabIndex="3" 时,我收到错误消息:

在“NumericSpinner”类型中找不到属性“CustomTabIndex”。

一些帮助将不胜感激。

4

5 回答 5

6

我对第一个有答案...在您的 CustomControl 的静态构造函数中添加以下内容

KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));

这应该可以让您在控件中进出标签,并允许您为自定义控件的每个子项设置标签索引。

至于你的第二个问题,我正在努力解决同样的问题。我认为这必须归因于您的自定义控件具有 Focusable = False 的事实。但是将此设置为 true 将使控件获得焦点,而不是实际的孩子。我认为可能需要在您的 CustomControl 上为 GotFocus 事件设置一个事件处理程序。当控件获得焦点时,然后执行

this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

这应该将焦点移到子元素上。

当我找出正确的方法时,我会发布跟进。

更新

所以对于你的第二点。

确保在控件的静态构造函数中将 focusable 设置为 false 和 TabNavigationProperty

FocusableProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(false));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));

这将允许控件按预期工作并尊重控件上的 Tab 键顺序以及所有子项。确保在您的样式中没有设置 Focusable 或 TabNavigation 属性。

劳尔

于 2011-01-20T17:41:39.390 回答
4

我没有为您提供完整的解决方案,但应检查以下四件事以进行制表符循环:

  • TabIndex 定义了标签顺序
  • IsTabStop 表示该控件是否应该用于制表符循环
  • FocusManager.IsFocusScope说,如果一个元素建立了自己的焦点区域
  • Focusable表示 UIElement 是否可以获得焦点。

我可以想象 IsFocusScope 会很有趣。

于 2011-01-18T19:25:25.967 回答
0

Raul 回答了主要问题 - 能够在自定义控件的文本框中添加标签。第二个问题是无法从文本框中跳出。

这个控件的问题是它的文本框包含一个按键处理程序:

PreviewKeyDown="valueText_PreviewKeyDown"

处理程序只允许在文本框中按下某些键:

/// <summary>
/// Since this event handler traps keystrokes within the control, in order to facilitate tabbing order, allowing the 
/// tab key press must be enabled
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void valueText_PreviewKeyDown(object sender, KeyEventArgs e)
{ 
    KeyConverter converter = new KeyConverter();
    string key = converter.ConvertToString(e.Key);
    int index = ((TextBox)sender).CaretIndex;
    if (key != null)
    {
        if (AllowNegativeValues && (e.Key == Key.Subtract || e.Key == Key.OemMinus))
        {
            e.Handled = (valueText.Text.Contains('-') || index > 0) == true;
        }
        else if (AllowDecimal && (e.Key == Key.OemPeriod || e.Key == Key.Decimal))
        {
            e.Handled = valueText.Text.Contains('.') == true;
        }
        else
            e.Handled = ((((e.Key >= Key.D0) && (e.Key <= Key.D9) && (e.KeyboardDevice.Modifiers != ModifierKeys.Shift))
                            || ((e.Key >= Key.NumPad0) && (e.Key <= Key.NumPad9) && (e.KeyboardDevice.Modifiers != ModifierKeys.Shift))
                            || e.Key == Key.Left || e.Key == Key.Right
                            || e.Key == Key.Back || e.Key == Key.Delete 
                            || e.Key == Key.Tab) == false);             
    }
    else
        e.Handled = true;
}

我只需要添加允许的 TAB 击键,自定义控件就可以正常工作:

e.Key == Key.Tab
于 2011-01-21T16:15:48.140 回答
0

我已经尝试使用所选答案的GotFocus事件,但它对我不起作用,它似乎完全禁用了选项卡导航,即使TabStop确实选择了第一个。
对我有用的是使用该WindowActivated事件并在那里使用相同的命令:

MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

我希望它可以帮助某人。

于 2011-12-06T09:45:50.443 回答
0

HaxElit 的答案有效,除非我们想使用标签来集中我们的控制,Alt + hotkey如下所示:

<!-- Alt + C selects numCount -->
<Label Target="{Binding ElementName=numCount}">Elements _Count:</Label>
<local:NumericSpinner x:Name="numCount"/>

所以我的最终解决方案如下:

static NumericSpinner()
{
    // Next line prevents focusing our wrapper parent control. The problem with that is that
    // it prevents Label controls to select our control by Alt+<hotkey>
    //FocusableProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(false));

    // Next line specifies that the children controls have their own tab subtree so a deep
    // traversal is performed when our control is focused
    KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(NumericSpinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Local));
}

// our wrapper control is focused invisibly. We must relocate the focus.
protected override void OnGotFocus(RoutedEventArgs e)
{
    base.OnGotFocus(e);

    // Next line moves the focus either forward (to out first inner child)
    // or backward if Shift+TAB is pressed (to the previous control)
    MoveFocus(new TraversalRequest(Keyboard.IsKeyDown(Key.Tab) && (Keyboard.Modifiers & ModifierKeys.Shift) != 0 ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next));
}
于 2016-06-16T14:24:17.350 回答