在选项卡之间切换时,如何将焦点恢复到之前聚焦的 TabPage 控件

How do I restore focus to previously focused control of the TabPage when switching between tabs

我有一个 TabControl,我正在以编程方式向其添加 Tabpages。 每个TabPage在里面加载一个UserControl,每个用户控件又包含几个控件。例如:

TabPage1
   UserControl1
      TextBox1, TextBox2, TextBox3
TabPage2
   UserControl2
      TextBox4, TextBox5, TextBox6

现在我希望每当用户更改选项卡时,当再次选择该选项卡时,该选项卡先前获得焦点的控件再次获得焦点。

示例:

  1. 假设焦点在 TabPage1 → UserControl1 → TextBox2
  2. 然后我点击 TagPage 2 → UserControl2 → TextBox4
  3. 然后我再次单击 TabPage1,我希望 TextBox2 获得焦点。

我该怎么办?

可能的解决方案:

覆盖您表单中的 UpdateDefaultButton();每次控件变为 ActiveControl.
时都会调用此方法 当然,如果你在TabPages里面有UserControl,ActiveControl就是UserControl,但是你需要它的子Control是当前获得焦点的。

在示例代码中,我使用 GetFocus() function to get the handle of the focused Control, then use Control.FromHandle() 获取具有该句柄的 Control 实例,如果不是 null,则将此信息与当前的句柄一起存储在字典中TabPage.

当 TabControl 的 Selected 事件被引发时,检查 Dictionary 是否存储了新的当前 TabPage,如果控件与该 TabPage 相关联,则将焦点移到它上面。
(我正在使用 BeginInvoke(),因为我们正在更改 Selected 处理程序中的 ActiveControl,这将导致调用 UpdateDefaultButton()

  • 请注意,我在这里没有验证 Focused Control 是否是与 TabPage 不同的容器的子容器:如果您在 TabPage 中嵌套了容器,则需要一种递归方法来检查其中一个祖先是否是TabPage.
Private tabPagesActiveControl As New Dictionary(Of Integer, Control)

Protected Overrides Sub UpdateDefaultButton()
    MyBase.UpdateDefaultButton()
    If ActiveControl Is Nothing Then Return

    If TypeOf ActiveControl.Parent Is TabPage Then
        Dim tp = DirectCast(ActiveControl.Parent, TabPage)
        Dim tabPageIdx = DirectCast(tp.Parent, TabControl).SelectedIndex
        Dim ctl = FromHandle(GetFocus())

        If ctl IsNot Nothing Then
            If tabPagesActiveControl.Count > 0 AndAlso tabPagesActiveControl.ContainsKey(tabPageIdx) Then
                tabPagesActiveControl(tabPageIdx) = ctl
            Else
                tabPagesActiveControl.Add(tabPageIdx, ctl)
            End If
        End If
    End If
End Sub

Private Sub TabControl1_Selected(sender As Object, e As TabControlEventArgs) Handles TabControl1.Selected
    Dim ctl As Control = Nothing
    If tabPagesActiveControl.TryGetValue(e.TabPageIndex, ctl) Then
        BeginInvoke(New Action(Sub() ctl.Focus()))
    End If
End Sub

Win32 函数声明:

<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Friend Shared Function GetFocus() As IntPtr
End Function

C#版本:

private Dictionary<int, Control> tabPagesActiveControl = new Dictionary<int, Control>();

protected override void UpdateDefaultButton()
{
    base.UpdateDefaultButton();
    if (ActiveControl is null) return;

    if (ActiveControl.Parent is TabPage tp) {
        var tabPageIdx = (tp.Parent as TabControl).SelectedIndex;
        var ctl = FromHandle(GetFocus());
        if (ctl != null) {
            if (tabPagesActiveControl.Count > 0 && tabPagesActiveControl.ContainsKey(tabPageIdx)) {
                tabPagesActiveControl[tabPageIdx] = ctl;
            }
            else {
                tabPagesActiveControl.Add(tabPageIdx, ctl);
            }
        }
    }
}

private void tabControl1_Selected(object sender, TabControlEventArgs e)
{
    if (tabPagesActiveControl.TryGetValue(e.TabPageIndex, out Control ctl)) {
        BeginInvoke(new Action(() => ctl.Focus()));
    }
}

Win32 函数声明:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr GetFocus();

这是它的工作原理:
注意:TabPage2TabPage3 包含带有 2 个文本框和一个列表框的 UserControl 实例。 TabPage1 包含一个 TextBox 和一个 NumericUpDown。