aivanov
Topic Author
Posts: 11
Joined: 30 Sep 2024, 17:25

Setting Focus Directly to Expander Button

30 Sep 2024, 19:22

I am trying to set focus to the `ToggleButton` of an `Expander` element when the user control gains focus. Is there a way to reference the internal element of the Expander in the target field of SetFocusAction? Is there any other way of setting focus like this through XAML?

The goal of this is to have the focus set to the button as soon as the screen becomes visible so the user can click it without needing to navigate first.
<Grid x:Name="StateTriggers">
            <b:Interaction.Triggers>                
                <b:PropertyChangedTrigger Binding="{Binding IsVisible, ElementName=MyContainer}">
                    <noesis:SetFocusAction TargetName="MyExpander"/>
                </b:PropertyChangedTrigger>
            </b:Interaction.Triggers>
</Grid>

<Grid x:Name="MyContainer" Width="400">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="80"/>
            </Grid.RowDefinitions>

            <ScrollViewer x:Name="Content" Grid.Row="0" Margin="20,20,10,20" VerticalAlignment="Top" VerticalScrollBarVisibility="Visible">
                <StackPanel>
                    <Expander x:Name="MyExpander" Template="{StaticResource MyExpanderTemplate}" OverridesDefaultStyle="True" IsExpanded="True" DataContext="{Binding MyExpanderModel}" KeyboardNavigation.DirectionalNavigation="Continue">
                        <Expander.Header>
                            <TextBlock Text="This is an expander"/>
                        </Expander.Header>
                        <ItemsControl x:Name="MyExpanderItems" ItemTemplate="{StaticResource MyExpanderItemTemplate}" ItemsSource="{Binding Items}" Margin="0,10,0,0" Focusable="False">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel Orientation="Vertical"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                        </ItemsControl>
                    </Expander>
                </StackPanel>
            </ScrollViewer>
</Grid>
At runtime, the hierarchy looks something like this:
> Expander (Focus goes here)
>> Dock Panel
>>>ToggleButton (Want focus to go here)

It would be nice if there was the option to do something like the line below but I haven't found any example doing something similar yet:
<noesis:SetFocusAction TargetName="MyExpander.DockPanel.ToggleButton"/>
Any help or ideas are much appreciated!
 
User avatar
sfernandez
Site Admin
Posts: 3154
Joined: 22 Dec 2011, 19:20

Re: Setting Focus Directly to Expander Button

02 Oct 2024, 11:48

You cannot access template elements from outside the control like that. I suggest you just set the focus on the Expander, and then move it down so its ToggleButton acquires the focus, something like this:
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
  xmlns:noesis="clr-namespace:NoesisGUIExtensions;assembly=Noesis.GUI.Extensions">
  <b:Interaction.Triggers>
    <b:EventTrigger EventName="Loaded">
      <noesis:SetFocusAction TargetName="expand"/>
      <noesis:MoveFocusAction Direction="Down"/>
    </b:EventTrigger>
  </b:Interaction.Triggers>
  <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <Button Content="First Button"/>
    <Expander x:Name="expand" Header="Expander" IsExpanded="True">
      <Ellipse Height="100" Fill="Red"/>
    </Expander>
    <Button Content="Second   Button"/>
  </StackPanel>
</Grid>
Could you try that?
 
aivanov
Topic Author
Posts: 11
Joined: 30 Sep 2024, 17:25

Re: Setting Focus Directly to Expander Button

02 Oct 2024, 23:15

This does work, Thank you!

There is however, an additional edge case that I am hitting. The expander is part of a grid and a VisualStateManager is setup to periodically hide and show the grid that wraps the expander. Is there a way to set focus when the visibility of the grid container is updated and not only when the entire control is first loaded?

I have tried these things and others without much success:
- have an EventTrigger on "IsVisibleChanged" inside of grid1
- have an EventTrigger on "IsLoaded" inside of grid1
- have a PropertyChangedTrigger for the visibility of grid1 inside of the root grid

Is there a good way of updating focus with the states of the VisualStateManager or at least to correct the focus in response to the visual state changing?
<Grid x:Name"root">
<VisualStateManager.VisualStateGroups>
  <!-- Multiple states where grid1 visibility is set to either Visible or Collapsed -->
</VisualStateManager.VisualStateGroups>

<Grid x:Name"grid1">
  <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <Button Content="First Button"/>
     <Expander x:Name="expand" Header="Expander" IsExpanded="True">
    </Expander>
    <Button Content="Second   Button"/>
  </StackPanel>
</Grid>
<Grid x:Name"grid2">
 <!-- Other Content -->
</Grid>

</Grid>
 
User avatar
sfernandez
Site Admin
Posts: 3154
Joined: 22 Dec 2011, 19:20

Re: Setting Focus Directly to Expander Button

04 Oct 2024, 12:46

Hello, if you have some visual states that handle the visibility of some parts of the UI, and you want to change the focus after going to any of those states, you can wait for the state animation to finish and then set the focus. See this example:
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
  xmlns:noesis="clr-namespace:NoesisGUIExtensions;assembly=Noesis.GUI.Extensions"
  x:Name="rootGrid">
  <b:Interaction.Triggers>
    <b:EventTrigger EventName="Loaded">
      <b:GoToStateAction TargetName="rootGrid" StateName="ShowGrid1"/>
    </b:EventTrigger>
    <b:StoryboardCompletedTrigger Storyboard="{Binding Storyboard, ElementName=ShowGrid1}">
      <noesis:SetFocusAction TargetName="expand"/>
      <noesis:MoveFocusAction Direction="Down"/>
    </b:StoryboardCompletedTrigger>
    <b:StoryboardCompletedTrigger Storyboard="{Binding Storyboard, ElementName=ShowGrid2}">
      <noesis:SetFocusAction TargetName="btn"/>
    </b:StoryboardCompletedTrigger>
  </b:Interaction.Triggers>
  <VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="ScreenStates">
      <VisualState x:Name="ShowGrid1">
        <Storyboard>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="grid1" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
          </ObjectAnimationUsingKeyFrames>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="grid2" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
          </ObjectAnimationUsingKeyFrames>
        </Storyboard>
      </VisualState>
      <VisualState x:Name="ShowGrid2">
        <Storyboard>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="grid2" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
          </ObjectAnimationUsingKeyFrames>
          <ObjectAnimationUsingKeyFrames Storyboard.TargetName="grid1" Storyboard.TargetProperty="Visibility">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
          </ObjectAnimationUsingKeyFrames>
        </Storyboard>
      </VisualState>
    </VisualStateGroup>
  </VisualStateManager.VisualStateGroups>
  <Grid x:Name="grid1">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
      <Button Content="ButtonA"/>
      <Expander x:Name="expand" Header="Expander" IsExpanded="True">
        <Ellipse Height="200" Fill="Red"/>
      </Expander>
      <Button Content="ButtonB">
        <b:Interaction.Triggers>
          <b:EventTrigger EventName="Click">
            <b:GoToStateAction TargetName="rootGrid" StateName="ShowGrid2"/>
          </b:EventTrigger>
        </b:Interaction.Triggers>
      </Button>
    </StackPanel>
  </Grid>
  <Grid x:Name="grid2">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
      <Button x:Name="btn" Content="ButtonC"/>
      <Button Content="ButtonD"/>
      <Button Content="ButtonE">
        <b:Interaction.Triggers>
          <b:EventTrigger EventName="Click">
            <b:GoToStateAction TargetName="rootGrid" StateName="ShowGrid1"/>
          </b:EventTrigger>
        </b:Interaction.Triggers>
      </Button>
    </StackPanel>
  </Grid>
</Grid>
Is this what you're looking for?
 
aivanov
Topic Author
Posts: 11
Joined: 30 Sep 2024, 17:25

Re: Setting Focus Directly to Expander Button

04 Oct 2024, 17:15

Yes! This is exactly it! Focus navigation seems to be working perfectly now.

Thank you!!
 
User avatar
jsantos
Site Admin
Posts: 4123
Joined: 20 Jan 2012, 17:18
Contact:

Re: Setting Focus Directly to Expander Button

05 Oct 2024, 00:23

Great! Marking this as solved

Who is online

Users browsing this forum: Bing [Bot] and 2 guests