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.
