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

I have a TabControl and I'm adding Tabpages to it programmatically. Each TabPage loads a UserControl in it, and each user control contains a few controls. For example:

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

Now I want that whenever the user changes the tab, the previously-focused control of that tab get focus again when the tab is selected again.

Example:

  1. Let's say the focus is on TabPage1 → UserControl1 → TextBox2
  2. Then I click on TagPage 2 → UserControl2 → TextBox4
  3. Then I again click on TabPage1 and I want TextBox2 get the focus.

What shall I do?



Solution 1:[1]

Possible solution:

Override UpdateDefaultButton() in your Form; this method is called each time a Control becomes the ActiveControl.
Of course, if you have UserControls inside TabPages, the ActiveControl is the UserControl, but you need its child Control that is currently focused.

In the sample code, I'm using the GetFocus() function to get the handle of the focused Control, then use Control.FromHandle() to get the Control instance with that handle and, if it's not null, store this information in a Dictionary, along with the current TabPage.

When the TabControl's Selected event is raised, check whether the Dictionary has stored the new current TabPage and, if a Control is associated with that TabPage, move the focus on it.
(I'm using BeginInvoke() because we're changing the ActiveControl in the Selected handler, which would in turn cause a call to UpdateDefaultButton())

  • Note that I'm not verifying here whether the Focused Control is child of a different container than the TabPage: if you have nested Containers in your TabPages, you need a recursive method that checks whether one of the ancestors is a 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 Function declaration:

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

C# Version:

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 Function declaration:

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

This is how it works:
Note: TabPage2 and TabPage3 contain the instance of a UserControl with 2 TextBoxes and a ListBox. TabPage1 contains a TextBox and a NumericUpDown.

TabControl TabPage restore ActiveControl

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1