SDK AutoCompleteBox 忽略 IsTabStop 奇怪问题

SDK AutoCompleteBox Ignores IsTabStop odd issue

我有几个 sdk AutoCompleteBox,我只想将其设置为 IsTabStop="False"。如果您这样做,它会被忽略并仍然需要 focus/tab 操作。

所以我深入研究 the template 并发现其中嵌入的 TextBox 明确硬编码为 IsTabStop=True 所以我想我可以把它拉出来,TemplateBind 到一个未使用的 属性 喜欢 Tag 将其设置在实例级别,只需在模板中提供一个 setter 默认为 True,对吗?

虽无欢喜,仍不予理会。所以我试着在模板中明确地设置它 False 。确实可以将其从 Tab 顺序中删除。但是,它还会禁用控件,因此它根本不会接受焦点或编辑。

我过去 运行 遇到过类似的情况,但 none 我的技巧很管用。有人 运行 以前参与过这个吗?我错过了什么只是让该死的东西脱离标签顺序?我也很困惑为什么在嵌入式 TextBox 上设置 IsTabStop 会禁用所有 hittestvisibility 的控件....

不确定设置为只忽略 属性 的原因是什么,也没有在任何文档中找到解释。

所以,在我为看似如此简单的事情走得太远之前,也许另一双眼睛可能会有所帮助,有什么想法吗?谢谢!

AutoCompleteBox里面的TextBox只有满足所有criteria for focus ownership才能拥有键盘焦点:

  • 必须派生自Control
  • 必须启用
  • 必须可见
  • 必须加载(如何测试:Loaded 事件触发/视觉父对象不再为空 [我使用扩展方法 IsLoaded])
  • 并且...IsTabStop必须为真

因此你必须让内部 TextBox 有它的 tabstoppy 方式。 但我们还有其他选择。 我考虑过使用 ContentControl 并设置 TabNavigation="Once"。据我所知,它应该让 Control 及其所有内容在选项卡导航链中表现得像一个完整的部分,因此理论上,当您使用 Tab 键时,您将进入 ContentControl,但永远不会进入其内容。

但我试过了,它没有按预期工作。不知道我的想法哪里错了。

我试了一下,发现以下解决方案有效:

FocusDitcher 包围它们(我们派生自 ContentControl 并使控件放弃焦点 OnGotFocus,但如果用户单击内部 TextBox 则不会触发)。我们也可以为此写一个 Behavior 。对于焦点转移,控件必须破坏焦点所有权的先决条件之一,例如进入禁用模式并返回启用(不使用 IsTabStop,尝试过,失败)。

// helper control
public class FocusCatcher : Control { }

[TemplatePart( Name = "ReversedTabSequenceEntryPointElement", Type = typeof( UIElement ) )]
public class FocusDitcher : ContentControl
{
    public FocusDitcher()
    {
        DefaultStyleKey = typeof( FocusDitcher );
        TabNavigation = KeyboardNavigationMode.Once;
    }

    private UIElement m_reversedTabSequenceEntryPoint;

    protected override void OnGotFocus( RoutedEventArgs e )
    {
        if (FocusManager.GetFocusedElement() == this)
        {
            DitchFocus();
        }
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        m_reversedTabSequenceEntryPoint = (UIElement) GetTemplateChild( "ReversedTabSequenceEntryPointElement" );
        m_reversedTabSequenceEntryPoint.GotFocus += OnReversedTabSequenceEntryPointGotFocus;
    }

    private void OnReversedTabSequenceEntryPointGotFocus( object sender, RoutedEventArgs e )
    {
        // tweak tab index to ensure that when we ditch the focus it will go to the first one in the tab order
        // otherwise it would not be possible to ever shift+tab back to any element precceeding this one in the tab order
        var localTabIndexValue = ReadLocalValue(TabIndexProperty);
        TabIndex = Int32.MinValue;
        DitchFocus();

        if (DependencyProperty.UnsetValue == localTabIndexValue)
            ClearValue( TabIndexProperty );
        else
            TabIndex = (int) localTabIndexValue;

    }

    private void DitchFocus()
    {
        IsEnabled = false; // now we lose the focus and it should go to the next one in the tab order
        IsEnabled = true;  // set the trap for next time
    }
}

和模板

<Style TargetType="myApp:FocusDitcher">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="myApp:FocusDitcher">
                <Grid>
                    <ContentPresenter
                        Content="{TemplateBinding Content}"
                        ContentTemplate="{TemplateBinding ContentTemplate}"
                        Cursor="{TemplateBinding Cursor}"
                        Margin="{TemplateBinding Padding}"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    <myApp:FocusCatcher x:Name="ReversedTabSequenceEntryPointElement"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>