Search code examples
c#wpfxaml

WPF Initial focus for each TabItem


Okay, I know this title looks like it has answers all around SO, but none of the answers found here worked for me. So, here goes.

I have this layout:

<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>

Now, my goal is to put focus on txt TextBox when ti1 TabItem gets selected and on dg DataGrid when ti2 TabItem gets selected. Also, I would really love to set this in XAML.

Note: I can only use controls that are named here, so up until Page control.

What have I tried so far:

  • setting FocusManager.FocusedElement="{Binding ElementName=txt}" on all of the parent controls in the parent tree of the txt Control (up until Page).
  • setting FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" on the txt and dg controls.
  • setting focus in code through TabControl's SelectionChanged event:

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

TextBox and DataGrid controls are created like UserControls, but are actually classes that inherit TextBox and DataGrid, like this:

<TextBox ... </TextBox>

and

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

As I said, XAML solution is desired, but I will also settle for a code one, if former is not possible.


Solution

  • Okay, the Dispatcher part from Keyur PATEL's answer was the solution for me, although not the complete one. The answer for me was to update TabControl layout with the Dispatcher and than invoke the Focus Dispatcher. So, the complete answer for me was:

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

    or you can use just Invoke instead, for the UI thread to wait for your Action.

    And for the reason why I had to use a Dispatcher, it is because I used it to change the selected tab in the first place. That is my best guess, at least.