1
votes

I have a Popup with a Textbox and a Button control in it. When I click/tap anywhere inside the Popup, the Textbox doesn't lose focus. The only way to make the Textbox lose focus is to set focus on the Button. I want to be able to remove focus from the Textbox without having to close the Popup or clicking the button. This issue only occurs in a Popup. I used to have the controls in a Flyout, and the behavior there is when a user clicks outside the Textbox and anywhere inside the Flyout, the Textbox loses focus. Also when the Flyout opens, the Textbox would automatically get focus on Flyout opening.

I tested the different behaviors for how Textboxes losing focus in a new blank UWP project, and it's the same. Also tested behavior of Textbox directly in root-grid. This is the XAML from MainPage:

<Page x:Class="App1.MainPage"
  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:local="using:App1"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <StackPanel Margin="0,40"
                HorizontalAlignment="Center"
                VerticalAlignment="Top"
                Orientation="Horizontal">
        <Button Margin="20,0"
                Content="Popup button"
                Tapped="Button_Tapped" />
        <Button Margin="20,0" Content="Flyout button">
            <Button.Flyout>
                <Flyout>
                    <Grid Width="200">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <TextBox VerticalAlignment="Center" />
                        <Button Grid.Column="1" Content="Test" />
                    </Grid>
                </Flyout>
            </Button.Flyout>
        </Button>
    </StackPanel>
    <Popup x:Name="MyPopup"
           Width="320"
           Height="60"
           IsLightDismissEnabled="True">
        <Grid Width="320"
              Height="60"
              Background="WhiteSmoke"
              Padding="12,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBox VerticalAlignment="Center" />
            <Button Grid.Column="1" Content="Test" />
        </Grid>
    </Popup>
    <TextBox Grid.Row="1"
             MinWidth="64"
             MaxWidth="300"
             Margin="0,40"
             VerticalAlignment="Center" />
</Grid>
</Page>

And the event to open the Popup:

private void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
    MyPopup.IsOpen = true;
}
1

1 Answers

1
votes

The problem is that Grid is not "focusable" because it does not derive from Control. One way is that you place your Grid inside a ContentControl or ContentPresenter.

The other way is a bit of a hack but you can set the focus programatically on the button when the Grid inside the Popup is tapped:

<Popup
    x:Name="MyPopup"
    Width="320"
    Height="60"
    IsLightDismissEnabled="True">
    <Grid
        x:Name="Grid"
        Width="320"
        Height="60"
        Background="WhiteSmoke"
        Tapped="Grid_OnTapped"
        Padding="12,0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <TextBox VerticalAlignment="Center" />
        <Button
            Grid.Column="1"
            Content="Test" />
    </Grid>
</Popup>

And the Tapped handler in which you generally search for any focusable control that is not a TextBox. If you don't have any you can place an invisible ContentControl with Opacity=0.01 for example and this will then be the focus element:

    private void Grid_OnTapped(object sender, TappedRoutedEventArgs e)
    {
        var grid = sender as Grid;
        if(grid == null) return;
        var controlToFocus = FindChild<Button>(grid);
        controlToFocus.Focus(FocusState.Programmatic);
    }

    private static T FindChild<T>(DependencyObject parent) where T : DependencyObject
    {
        var childCount = VisualTreeHelper.GetChildrenCount(parent);
        for (var i = 0; i < childCount; i++)
        {
            var elt = VisualTreeHelper.GetChild(parent, i);
            if (elt is T) return (T)elt;
            var result = FindChild<T>(elt);
            if (result != null) return result;
        }

        return null;
    }