jsmyth886
jsmyth886

Reputation: 531

UWP Changing style of AppBarButton breaks the dynamic overflow's automatic styling

Trying to use the new IsDynamicOverflowEnabled property on the CommandBar and I've run into a styling issue with the Overflow. Issue is as follows, when the AppBarButton's Style is not overridden and drops off to the Overflow area, the highlight of the AppBarButton stretches across the entire width of the Popup and its hover/hit detection is also the entire width of the Popup.

When it's Style is overridden the highlight only covers the area of the text (in this case home) and its hover/hit detection are only over this area, even if the overriding Style is exactly the same as the one in the generic.xaml.

I notice when inspecting the properties at Runtime that the ones that aren't being overridden are being applied another style from the TargetType - FrameworkElement.

From looking through the generic.xaml, the only place I can see this sort of change to a style is in the CommandBarOverflowPresenter (see below)

<CommandBarOverflowPresenter.ItemContainerStyle>
  <Style TargetType="FrameworkElement">
    <Setter Property="HorizontalAlignment" Value="Stretch" />
    <Setter Property="Width" Value="NaN" />
  </Style>
</CommandBarOverflowPresenter.ItemContainerStyle>

To remove all of the other noise from the app I'm working on, I created a new project with just a CommandBar on the main page and the necessary buttons and text needed to replicate my issue. Below is the mainpage.xaml:

<Page
    x:Class="App4.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App4"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

  <Page.Resources>
    <Style x:Key="Style" TargetType="AppBarButton">
      <Setter Property="Background"
              Value="{ThemeResource AppBarButtonBackground}" />
      <Setter Property="Foreground"
              Value="{ThemeResource AppBarButtonForeground}" />
      <Setter Property="BorderBrush"
              Value="{ThemeResource AppBarButtonBorderBrush}" />
      <Setter Property="HorizontalAlignment"
              Value="Left" />
      <Setter Property="VerticalAlignment"
              Value="Top" />
      <Setter Property="FontFamily"
              Value="{ThemeResource ContentControlThemeFontFamily}" />
      <Setter Property="FontWeight"
              Value="Normal" />
      <Setter Property="Width"
              Value="68" />
      <Setter Property="UseSystemFocusVisuals"
              Value="True" />
      <Setter Property="AllowFocusOnInteraction"
              Value="False" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="AppBarButton">
            <Grid x:Name="Root"
                  MinWidth="{TemplateBinding MinWidth}"
                  MaxWidth="{TemplateBinding MaxWidth}"
                  Background="{TemplateBinding Background}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  BorderThickness="{TemplateBinding BorderThickness}">
              <Grid.Resources>
                <Style x:Name="LabelOnRightStyle"
                       TargetType="AppBarButton">
                  <Setter Property="Width"
                          Value="NaN" />
                </Style>
              </Grid.Resources>
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="ApplicationViewStates">
                  <VisualState x:Name="FullSize" />
                  <VisualState x:Name="Compact">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Collapsed" />
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="LabelOnRight">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content"
                                                     Storyboard.TargetProperty="Margin">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="12,14,0,14" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot"
                                                     Storyboard.TargetProperty="MinHeight">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarThemeCompactHeight}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="(Grid.Row)">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="0" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="(Grid.Column)">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="1" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="TextAlignment">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Left" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="Margin">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="8,15,12,17" />
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="LabelCollapsed">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot"
                                                     Storyboard.TargetProperty="MinHeight">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarThemeCompactHeight}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Collapsed" />
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Overflow">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot"
                                                     Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Collapsed" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel"
                                                     Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Visible" />
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="OverflowWithToggleButtons">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot"
                                                     Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Collapsed" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel"
                                                     Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="Visible" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel"
                                                     Storyboard.TargetProperty="Margin">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="38,0,12,0" />
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal">
                    <Storyboard>
                      <PointerUpThemeAnimation Storyboard.TargetName="OverflowTextLabel" />
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="PointerOver">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root"
                                                     Storyboard.TargetProperty="Background">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonBackgroundPointerOver}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root"
                                                     Storyboard.TargetProperty="BorderBrush">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonBorderBrushPointerOver}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundPointerOver}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundPointerOver}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundPointerOver}" />
                      </ObjectAnimationUsingKeyFrames>
                      <PointerUpThemeAnimation Storyboard.TargetName="OverflowTextLabel" />
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Pressed">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root"
                                                     Storyboard.TargetProperty="Background">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonBackgroundPressed}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root"
                                                     Storyboard.TargetProperty="BorderBrush">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonBorderBrushPressed}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundPressed}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundPressed}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundPressed}" />
                      </ObjectAnimationUsingKeyFrames>
                      <PointerDownThemeAnimation Storyboard.TargetName="OverflowTextLabel" />
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Disabled">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root"
                                                     Storyboard.TargetProperty="Background">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonBackgroundDisabled}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root"
                                                     Storyboard.TargetProperty="BorderBrush">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonBorderBrushDisabled}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundDisabled}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundDisabled}" />
                      </ObjectAnimationUsingKeyFrames>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowTextLabel"
                                                     Storyboard.TargetProperty="Foreground">
                        <DiscreteObjectKeyFrame KeyTime="0"
                                                Value="{ThemeResource AppBarButtonForegroundDisabled}" />
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
                <VisualStateGroup x:Name="InputModeStates">
                  <VisualState x:Name="InputModeDefault" />
                  <VisualState x:Name="TouchInputMode">
                    <VisualState.Setters>
                      <Setter Target="OverflowTextLabel.Padding"
                              Value="0,11,0,13" />
                    </VisualState.Setters>
                  </VisualState>
                  <VisualState x:Name="GameControllerInputMode">
                    <VisualState.Setters>
                      <Setter Target="OverflowTextLabel.Padding"
                              Value="0,11,0,13" />
                    </VisualState.Setters>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>
              <Grid x:Name="ContentRoot"
                    MinHeight="{ThemeResource AppBarThemeMinHeight}">
                <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="*" />
                  <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                  <RowDefinition Height="Auto" />
                  <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <ContentPresenter x:Name="Content"
                                  Height="20"
                                  Margin="0,14,0,4"
                                  Content="{TemplateBinding Icon}"
                                  Foreground="{TemplateBinding Foreground}"
                                  HorizontalAlignment="Stretch"
                                  AutomationProperties.AccessibilityView="Raw" />
                <TextBlock x:Name="TextLabel"
                           Grid.Row="1"
                           Text="{TemplateBinding Label}"
                           Foreground="{TemplateBinding Foreground}"
                           FontSize="12"
                           FontFamily="{TemplateBinding FontFamily}"
                           TextAlignment="Center"
                           TextWrapping="Wrap"
                           Margin="2,0,2,6" />
              </Grid>
              <TextBlock x:Name="OverflowTextLabel"
                         Text="{TemplateBinding Label}"
                         Foreground="{TemplateBinding Foreground}"
                         FontSize="15"
                         FontFamily="{TemplateBinding FontFamily}"
                         TextAlignment="Left"
                         TextTrimming="Clip"
                         TextWrapping="NoWrap"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Center"
                         Margin="12,0,12,0"
                         Padding="0,5,0,7"
                         Visibility="Collapsed" />
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Page.Resources>

  <Page.TopAppBar>
    <CommandBar IsDynamicOverflowEnabled="True">
      <CommandBar.Content>
        <TextBlock Text="Text to force the buttons to drop off at some point when the screen is resized" />
      </CommandBar.Content>
      <CommandBar.PrimaryCommands>
        <AppBarButton Label="Edit"
                      Icon="Edit" />
        <AppBarButton Label="People"
                      Icon="People" />
        <AppBarButton Label="Really long text should should make overflow wider"
                      Icon="Highlight" />
        <AppBarButton Label="Home"
                      Icon="Home"
                      Style="{StaticResource Style}" />
      </CommandBar.PrimaryCommands>
    </CommandBar>
  </Page.TopAppBar>
</Page>

As you can see I have 1 CommandBar, 1 TextBlock in the content of the CommandBar and 4 AppBarButtons in the CommandBar's PrimaryCommands. Only 1 of the AppBarButtons Styles is overridden (the Home one) and the Style which it is overridden with is grabbed directly from the generic.xaml from this location - C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.14393.0\Generic

Why does this happen? And is there any known workarounds?

Upvotes: 2

Views: 1667

Answers (1)

Jay Zuo
Jay Zuo

Reputation: 15758

As you've known, while an AppBarButton is added into SecondaryCommands, it will display in Popup. And if you check with Live Visual Tree in Visual Studio, you will find the AppBarButton is added in to a CommandBarOverflowPresenter named "SecondaryItemsControl".
enter image description here

The style you've found under CommandBarOverflowPresenter.ItemContainerStyle is used to reset AppBarButton's Width and HorizontalAlignment property as by default AppBarButton's Width is 68 and HorizontalAlignment is set to Left and if we use the default style, the AppBarButton can't take the whole width of the Popup.

<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Width" Value="68"/>

However, the style under CommandBarOverflowPresenter.ItemContainerStyle only applies on the AppBarButton with implicit styles. If you set the Style property of AppBarButton explicitly, it will lose its function. And this is the reason why changing style of AppBarButton breaks the dynamic overflow's automatic styling.

As a workaround, you can try to change the Width and HorizontalAlignment property like what under CommandBarOverflowPresenter.ItemContainerStyle and set MiniWidth to limit AppBarButton's width.

<Style x:Key="Style" TargetType="AppBarButton">
    <Setter Property="HorizontalAlignment" Value="Stretch" />
    <Setter Property="Width" Value="Auto" />
    <Setter Property="MinWidth" Value="68" />
    ...
</Style>

PS: This workaround may be not suitable for all scenarios, especially when the AppBarButton has a long Label. You may need to determine whether to use it according to your scenario.

Update:

Your answer raises another question, why can only implicit styles inherit this CommandBarOverflowPresenter.ItemContainerStyle?

This is related to Lookup behavior for XAML resource references. You can think that Styles have been set explicitly have the highest priority. Once you set the Style property, the XAML system will not look for other styles.

Ref ResourceDictionary and XAML resource references:

The XAML framework also looks for implicit style resources (those which use TargetType rather than x:Key or x:Name) when it decides which style & template to use for a control that hasn't set the Style and ContentTemplate or ItemTemplate properties.

So once you set Style for AppBarButton, the implicit style under CommandBarOverflowPresenter.ItemContainerStyle won't work.

Following is a simple sample that demonstrates this behavior.

<Page ...>
    <Page.Resources>
        <Style x:Key="ListViewItemStyle1" TargetType="ListViewItem">
            <Setter Property="Background" Value="Red" />
        </Style>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ListView>
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="Background" Value="Green" />
                    <Setter Property="Foreground" Value="White" />
                </Style>
            </ListView.ItemContainerStyle>
            <ListViewItem Style="{StaticResource ListViewItemStyle1}">1</ListViewItem>
            <ListViewItem Background="Blue">2</ListViewItem>
            <ListViewItem>3</ListViewItem>
            <ListViewItem>4</ListViewItem>
        </ListView>
    </Grid>
</Page>

And it looks like:
enter image description here

As you can see, for the first item, as I've set the Style property explicitly, it won't use the style under ListView.ItemContainerStyle. For the second item, as I didn't set the Style property, it will use the implicit style under ListView.ItemContainerStyle, however the property that has been set explicitly has a higher priority. So this item's foreground is white but its background is blue. And for the third and fourth item, as they use the default style which has the lowest priority, so the implicit style under ListView.ItemContainerStyle will be used instead of the default style.

Upvotes: 2

Related Questions