Is there a way to automatically switch to a TabItem that hosts a control I want to focus?
My sample window to exemplify the issue.
<Window x:Class="Sample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Sample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="80"/>
<Setter Property="Height" Value="24"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TabControl Grid.Row="0" Margin="10" Background="White">
<TabItem Header="TabItem1">
<Button x:Name="SomeButton" Content="Set Focus on TextBox" Width="120" Click="Button_Click"/>
</TabItem>
<TabItem Header="TabItem2">
<TextBox x:Name="SomeTextBox" Margin="10" Text="Sample text"/>
</TabItem>
</TabControl>
<StackPanel Grid.Row="1" Margin="10">
<Button Content="Cancel" HorizontalAlignment="Right" Click="Button_Click_1"/>
</StackPanel>
</Grid>
</Window>
And the code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SomeTextBox.Focus();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Close();
}
}
So, when clicking SomeButton
, I want to move the focus to SomeTextBox
but, for that to work, I have to select TabItem2
first. That's obviously easy in the provided example, but I'm looking for an automated solution that 'knows' that SomeTextBox
belongs to TabItem2
and switches to it and then sets the focus on the desired control. Needless to say that in the real case scenario SomeTextBox
might be in some other level in the VisualTree
in some other kind of pane. Is it possible?
BTW, if it helps, the application of such feature would be setting the focus on a control highlighted by error validation and/or switching to the pane that holds it.
You can use the VisualTreeHelper
class to find the nearest parent of a specific type.
Here's an implementation example from This answer.
public static T FindParentOfType<T>(this DependencyObject child) where T : DependencyObject
{
DependencyObject parentDepObj = child;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
T parent = parentDepObj as T;
if (parent != null) return parent;
}
while (parentDepObj != null);
return null;
}
Once you have a reference to your TabItem
, you can set its IsSelected
property to select it in the TabControl
.
private void Button_Click(object sender, RoutedEventArgs e)
{
var tabItem = FindParentOfType<TabItem>(SomeTextBox);
if (tabItem is null) return;
(tabItem as TabItem).IsSelected = true;
SomeTextBox.Focus();
}
Edit
Good catch on the TextBox
not being in the visual tree while its parent TabItem
is not selected. Here's another way to get at the TextBox
that isn't dependent on the visual tree.
private void Button_Click(object sender, RoutedEventArgs e)
{
FrameworkElement currentItem = SomeTextBox;
do
{
currentItem = (FrameworkElement)currentItem.Parent;
}
while (!(currentItem is TabItem)
&& (currentItem as TabItem).Parent != MyTabControl // Optional handling in case you have nested TabControls
&& !(currentItem.Parent is null));
(currentItem as TabItem).IsSelected = true;
currentItem.UpdateLayout();
SomeTextBox.Focus();
}