每个TabItem的WPF初始焦点

4

好的,我知道这个标题看起来好像在Stack Overflow上有很多答案,但是我在这里找到的答案都不适用于我。所以,接下来就是我的问题。

我有这个布局:

<Window>
  <Grid>
    <DockPanel>
      <TabControl>
        <TabItem>
          <Page x:Name="p">
            <Grid x:Name="g2">
              <TabControl x:Name="tc">
                <TabItem x:Name="ti1">
                  <StackPanel x:Name="sp">
                    <C:TextBox x:Name="txt"/>
                  </StackPanel>
                </TabItem>
                <TabItem x:Name="ti2">
                  <C:DataGrid x:Name="dg"/>
                </TabItem>
              </TabControl>
            </Grid>
          </Page>
        </TabItem>
      </TabControl>
    </DockPanel>
  </Grid>
</Window>

现在,我的目标是在选中 ti1 TabItem 时将焦点放在 txt TextBox 上,在选中 ti2 TabItem 时将焦点放在 dg DataGrid 上。此外,我非常希望能够在 XAML 中设置这些。

注意:我只能使用这里提到的控件,所以仅限于 Page 控件。

我已经尝试过:

  • txt 控件的父级树中 (直到 Page 控件),设置 FocusManager.FocusedElement="{Binding ElementName=txt}"
  • txtdg 控件上,设置 FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
  • 通过 TabControlSelectionChanged 事件,在代码中设置焦点:

    • if ( ti1.IsSelected ) { tc.UpdateLayout(); FocusManager.SetFocusedElement( sp, txt ); }
    • if ( ti1.IsSelected ) { tc.UpdateLayout(); txt.Focus(); }

TextBoxDataGrid 控件像这样创建为 UserControl,但实际上是继承自 TextBoxDataGrid 的类:

<TextBox ... </TextBox>

并且

public partial class TextBox : System.Windows.Controls.TextBox

就像我之前说的一样,希望使用XAML语言来解决问题,但如果无法实现,也可以采用代码方式。


希望能看到每次尝试的结果(即发生了什么)。既然你已经尝试了最明显的方法,那么有一个可以复制和播放的 mcve 就很好了。 - Sinatr
@Sinatr 我会在有更多时间的时候再试一下,但现在我可以说除了 SetFocusedElement 之外的所有解决方案都没有任何效果。但是使用 FocusManager 可以在 TextBox 中看到可见的 Caret,但焦点不在那里,我无法输入。而且这只在页面最初打开时有效,当我选择其他选项卡,然后返回第一个选项卡时,即使 Caret 也不可见。 - Adder
@Adder,请不要大幅编辑其他人的答案以包含额外信息。在您的情况下,我建议您为有帮助的答案投票(如果您觉得合适),并发布自己的答案以提供额外信息并接受它。请参阅:https://stackoverflow.com/help/self-answer - Alex
@Alex,如果我这样做,人们会看到他的答案被采纳了并使用它,只会发现它不起作用。我为什么要这样呢?我希望人们能够看到解决问题的方法,同时也给予Keyur PATEL帮助我的功劳。 - Adder
1
@Alex,抱歉,我读成了“up-vote”,但在我的脑海中被翻译成了“接受它” : )。没关系,我会按照你说的去做。 - Adder
3个回答

3

好的,来自Keyur PATEL答案的Dispatcher部分对我很有帮助,虽然不是完整的解决方案。对我来说,答案是使用 Dispatcher 更新TabControl布局,然后调用Focus Dispatcher。因此,对我来说完整的答案是:

Dispatcher.BeginInvoke( (Action) (() => tc.UpdateLayout()) );
Dispatcher.BeginInvoke( (Action) (() => txt.Focus() ) );

或者您可以直接使用Invoke,让UI线程等待您的Action。关于为什么我必须使用Dispatcher的原因,是因为我在第一次选择选项卡时使用了它。至少这是我最好的猜测。


1
啊,很高兴你最终搞定了 :) - Keyur PATEL

1
您可以尝试类似于您的代码后端解决方案,但有所变化:

<TabControl x:Name="tc" SelectionChanged="tc_selectionChanged">

在后台代码中:
InitializeComponent();

//if you know which control to focus by default when page is first loaded
Dispatcher.BeginInvoke(new Action(() => { txt.Focus(); })); 

并且

private void tc_selectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (ti1.IsSelected)
    {
        txt.Focus();
    }
    else if (ti2.IsSelected)
    {
        dg.Focus();
    }
}

我在自己的WPF应用程序中尝试了这个确切的设置,所以我知道它是可以工作的。

有用的链接:

WPF TabControl On SelectionChanged, set focus to a text field

如何在WPF的tabItem中将焦点聚集在控件上(对于我来说,它可以在没有UpdateLayout()的情况下工作)


这对我没有用。 Caret 可见,但焦点不在 TextBox 上。文本框的图片 - Adder
@Adder 很有趣,我也遇到了同样的问题(冻结的插入符号可见,但输入内容时文本框没有焦点),但我用了这行代码 Dispatcher.BeginInvoke(new Action(() => { txt.Focus(); })); 就解决了。你可以确认一下,在代码后面是否紧跟着 InitializeComponent(); 调用它? - Keyur PATEL
可以尝试使用 FocusManager.SetFocusedElement(txt, txt) 而不是 txt.Focus() - grek40

0

使用扩展属性找到解决方案

public static class TabItemExtention
{
    public static bool GetIsSelected(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsSelectedProperty);
    }

    public static void SetIsSelected(DependencyObject obj, bool value)
    {
        obj.SetValue(IsSelectedProperty, value);
    }

    public static readonly DependencyProperty IsSelectedProperty =
        DependencyProperty.RegisterAttached(
            "IsSelected", typeof(bool), typeof(TabItemExtention),
            new UIPropertyMetadata(false, OnIsSelectedPropertyChanged));

    private static void OnIsSelectedPropertyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var uie = (UIElement)d;
        var tabItem = uie as TabItem;
        if (tabItem != null)
            tabItem.IsSelected = (bool)e.NewValue;
        if ((bool)e.NewValue)
        {
            uie.UpdateLayout();
        }
    }
}

XAML:

ctrl:TabItemExtention.IsSelected="{Binding IsTabNewCustomsDelaySelected}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接