Search code examples
xamluwpwindows-10win-universal-appuwp-xaml

How to add an icon to a MenuFlyout in UWP?


I'm trying to add an icon to a menuflyoutitem, I'd like that my menu looks similar to this one:

enter image description here

This is my code:

<AppBarButton.Flyout>
    <MenuFlyout>
        <MenuFlyoutItem Text="SMS">
            <MenuFlyoutItem.Template>
                <ControlTemplate TargetType="MenuFlyoutItem">
                    <StackPanel Margin="12,10,0,0" Orientation="Horizontal">
                        <SymbolIcon Margin="0,0,12,0" Symbol="Comment" />
                        <TextBlock Text="{TemplateBinding Text}" />
                    </StackPanel>
                </ControlTemplate>
            </MenuFlyoutItem.Template>
        </MenuFlyoutItem>
        <MenuFlyoutItem Text="Email">
            <MenuFlyoutItem.Template>
                <ControlTemplate TargetType="MenuFlyoutItem">
                    <StackPanel Margin="12,10,0,10" Orientation="Horizontal">
                        <SymbolIcon Margin="0,0,12,0" Symbol="MailForward" />
                        <TextBlock Text="{TemplateBinding Text}" />
                    </StackPanel>
                </ControlTemplate>
            </MenuFlyoutItem.Template>
        </MenuFlyoutItem>
    </MenuFlyout>
</AppBarButton.Flyout>

I've been struggling and I cannot find a normal tutorial about it, does anybody know I should change? Because when I add the icon all the behaviour of the Menu is lost for example it doesn't change the color again when I'm over the menu. Thanks.


Solution

  • All the behaviour of the Menu is lost because you are using your custom MenuFlyoutItem template and in your template, you didn't deal with different visual states.

    By default, MenuFlyoutItem has 8 visual states which are Normal, PointerOver, Pressed, Disabled, Unchecked, Checked, DefaultPadding, and NarrowPadding. For more info, please check MenuFlyoutItem styles and templates.

    When you are over the menu, it triggers PointerOver state:

    <VisualState x:Name="PointerOver">
        <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Background">
                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListLowBrush}" />
            </ObjectAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Foreground">
                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
            </ObjectAnimationUsingKeyFrames>
            <PointerUpThemeAnimation Storyboard.TargetName="TextBlock" />
        </Storyboard>
    </VisualState>
    

    And in this state, it changes the background and the TextBlock's foreground. However in your custom template, you didn't have any VisualState, so it didn't change the color.

    To fix this issue, you can add visual states for each of your custom template.

    Or you can create a custom style based on the default styles and templates and use Tag property to set the SymbolIcon like:

    <Style x:Key="MyMenuFlyoutItem" TargetType="MenuFlyoutItem">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
        <Setter Property="Padding" Value="{ThemeResource MenuFlyoutItemThemePadding}" />
        <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="UseSystemFocusVisuals" Value="True" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="MenuFlyoutItem">
                    <Grid x:Name="LayoutRoot"
                          Background="{TemplateBinding Background}"
                          BorderBrush="{TemplateBinding BorderBrush}"
                          BorderThickness="{TemplateBinding BorderThickness}"
                          Padding="{TemplateBinding Padding}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                        <PointerUpThemeAnimation Storyboard.TargetName="TextBlock" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Backgroun
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Foreground
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerUpThemeAnimation Storyboard.TargetName="TextBlock" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Backgroun
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListMediumBrush}" 
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Foreground
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}"
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerDownThemeAnimation Storyboard.TargetName="TextBlock" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Foreground
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="CheckPlaceholderStates">
                                <VisualState x:Name="NoPlaceholder" />
                                <VisualState x:Name="CheckPlaceholder">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Margin">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource MenuFlyoutItemPlaceholderThemeThickness}
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="PaddingSizeStates">
                                <VisualState x:Name="DefaultPadding" />
                                <VisualState x:Name="NarrowPadding">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Padding">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource MenuFlyoutItemThemePaddingNarrow}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <StackPanel Orientation="Horizontal">
                            <SymbolIcon Margin="0,0,12,0" Symbol="{Binding Tag, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                            <TextBlock x:Name="TextBlock"
                                       HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                       VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                       Foreground="{TemplateBinding Foreground}"
                                       Text="{TemplateBinding Text}"
                                       TextTrimming="Clip" />
                        </StackPanel>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    And then, use this style in your MenuFlyoutItem:

    <AppBarButton.Flyout>
        <MenuFlyout>
            <MenuFlyoutItem Style="{StaticResource MyMenuFlyoutItem}" Tag="Comment" Text="SMS" />
            <MenuFlyoutItem Style="{StaticResource MyMenuFlyoutItem}" Tag="MailForward" Text="Email" />
        </MenuFlyout>
    </AppBarButton.Flyout>
    

    The style I've used is just for example, you may need to edit it to fit your needs.