Delen via


Quickstart: Adding a MenuFlyout (XAML)

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

Learn how to use a MenuFlyout to temporarily show a list of commands or options related to what your user is currently doing.

Prerequisites

  • Windows 8.1 or Windows Phone 8.1
  • Microsoft Visual Studio 2013
  • We assume that you can add controls to a basic Windows Runtime app using C++, C#, or Visual Basic. For instructions on adding a control, see Quickstart: Adding controls and handling events.

When to use a menu flyout

A MenuFlyout control displays lightweight UI (called a flyout). This UI lets the user choose from a contextual list of simple commands or options. Unlike a dialog, a flyout can be lightly dismissed by clicking or tapping outside it. A flyout should be shown only in response to a user tap or click. The selected action is performed or option is set, and the flyout is dismissed, as soon as the user selects a menu item in the flyout. The user can dismiss a flyout at any time without performing an action, by tapping or clicking outside of it, or by pressing ESC.

You define the contents of the menu by adding MenuFlyoutItem, ToggleMenuFlyoutItem, and MenuFlyoutSeparator objects to the MenuFlyout. These objects are for:

To show temporary, contextual UI that isn't a menu, use a Flyout instead. See Quickstart: Adding a Flyout.

Don't use a MenuFlyout to let a user select a value. Instead, use a ComboBox.

For more info, see Guidelines and checklist for Flyouts.

Using a MenuFlyout with a Button control

It’s common to attach a menu flyout to a button, so the Button control has a Flyout property to simplify attaching and opening a MenuFlyout control. A menu flyout attached to a button opens automatically when the button is clicked; you don't need to handle any events to open the flyout.

This example shows a MenuFlyout attached to a Button. The menu flyout lets a user turn "Shuffle" and "Repeat" options on or off, or reset the options.

  1. Declare a Button control in Extensible Application Markup Language (XAML).

  2. Attach a MenuFlyout to the buttons Flyout property.

  3. Declare the contents of the menu flyout. A MenuFlyout can contain only MenuFlyoutItem, ToggleMenuFlyoutItem, and MenuFlyoutSeparator objects.

  4. Handle the Click event of a menu item to perform an action, or bind to the IsChecked property of a ToggleMenuFlyoutItem.

    When the user selects a menu item, the menu flyout is dismissed automatically.

    To cancel the action, the user can dismiss the flyout by tapping outside it or pressing ESC.

Note  In a Windows Store app, the ToggleMenuFlyoutItem shows a checkmark when the IsChecked property is true, and doesn't show the checkmark when the IsChecked property is false.

<Button Content="Options">
    <Button.Flyout>
        <MenuFlyout>
            <MenuFlyoutItem Text="Reset" Click="Reset_Click"/>
            <MenuFlyoutSeparator/>
            <ToggleMenuFlyoutItem Text="Shuffle" 
                IsChecked="{Binding IsShuffleEnabled, Mode=TwoWay}"/>
            <ToggleMenuFlyoutItem Text="Repeat" 
                IsChecked="{Binding IsRepeatEnabled, Mode=TwoWay}"/>
        </MenuFlyout>
    </Button.Flyout>
</Button>
private void Reset_Click(object sender, RoutedEventArgs e)
{
    IsShuffleEnabled = false;
    IsRepeatEnabled = false;
}

When the user clicks the button, the open flyout looks like this.

Note  

In a Windows Phone Store app, MenuFlyoutSeparator is ignored if present in your XAML, and can be removed.

ToggleMenuFlyoutItem doesn't show a checkmark when the IsChecked property is true. Windows Phone design guidelines recommend that you should reword the menu item instead of showing a checkmark to denote the checked state. Here, the text is toggled between "Repeat" and "Repeat off", and "Shuffle" and "Shuffle off". The state of the toggle is queried via the IsChecked property of the ToggleMenuFlyoutItem.

<Button Content="Options">
    <Button.Flyout>
        <MenuFlyout>
            <MenuFlyoutItem Text="Reset" Click="Reset_Click"/>
            <MenuFlyoutSeparator/>
            <ToggleMenuFlyoutItem x:Name="ShuffleToggle" Text="Shuffle"  Click="ShuffleToggle_Click" 
                IsChecked="{Binding IsShuffleEnabled, Mode=TwoWay}"/>
            <ToggleMenuFlyoutItem x:Name="RepeatToggle" Text="Repeat"  Click="RepeatToggle_Click"
                IsChecked="{Binding IsRepeatEnabled, Mode=TwoWay}"/>
        </MenuFlyout>
    </Button.Flyout>
</Button>
private void Reset_Click(object sender, RoutedEventArgs e)
{
    IsShuffleEnabled = false;
    ShuffleToggle.Text = "Shuffle";

    IsRepeatEnabled = false;
    RepeatToggle.Text = "Repeat";       
}

private void ShuffleToggle_Click(object sender, RoutedEventArgs e)
{
    if (ShuffleToggle.IsChecked == true)
    {
        IsShuffleEnabled = true;
        ShuffleToggle.Text = "Shuffle off";
    }
    else
    {
        IsShuffleEnabled = false;
        ShuffleToggle.Text = "Shuffle";
    }
}

private void RepeatToggle_Click(object sender, RoutedEventArgs e)
{
    if (RepeatToggle.IsChecked == true)
    {
        IsRepeatEnabled = true;
        RepeatToggle.Text = "Repeat off";
    }
    else
    {
        IsRepeatEnabled = false;
        RepeatToggle.Text = "Repeat";
    }
}

This example shows how to use a MenuFlyout with an AppBarButton control.

<AppBarButton Icon="Sort" IsCompact="True">
    <Button.Flyout>
        <MenuFlyout>
            <MenuFlyoutItem Text="By rating" Tag="rating"
                            Click="SortMenuFlyoutItem_Click"/>
            <MenuFlyoutItem Text="By match" Tag="match" 
                            Click="SortMenuFlyoutItem_Click"/>
            <MenuFlyoutItem Text="By distance" Tag="distance" 
                            Click="SortMenuFlyoutItem_Click"/>
        </MenuFlyout>
    </Button.Flyout>
</AppBarButton>
private void SortMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
    MenuFlyoutItem selectedItem = sender as MenuFlyoutItem;

    if (selectedItem != null)
    {
        if (selectedItem.Tag.ToString() == "rating")
        {
            SortByRating();
        }
        else if (selectedItem.Tag.ToString() == "match")
        {
            SortByMatch();
        }
        else if (selectedItem.Tag.ToString() == "distance")
        {
            SortByDistance();
        }
    }
}

When the user clicks the button, the open flyout looks like this.

Using a MenuFlyout with other UI elements

You can attach a MenuFlyout control to any FrameworkElement object by using the FlyoutBase.AttachedFlyout attached property. If you do so, you must respond to an interaction on the FrameworkElement, such as the Tapped event, and open the MenuFlyout in your code.

This example shows how to use a shared MenuFlyout to show a menu when a user taps an Image.

  1. Declare the MenuFlyout as a StaticResource and give it an x:Key attribute value, like this: <MenuFlyout x:Key="ImageMenuFlyout">
  2. For each element that shares this MenuFlyout, set the FlyoutBase.AttachedFlyout attached property to the Flyout resource: <Image FlyoutBase.AttachedFlyout="{StaticResource ImageMenuFlyout}"/>
  3. Handle the Tapped event for each element that uses the flyout. In the event handler, call the FlyoutBase.AttachedFlyout method and pass the tapped element (sender) as the parameter.
<!-- Declare the shared flyout as a resource. -->
<Page.Resources>
    <MenuFlyout x:Key="ImageMenuFlyout">
        <MenuFlyout.MenuFlyoutPresenterStyle>
            <Style TargetType="MenuFlyoutPresenter">
                <Setter Property="BorderBrush" Value="CornflowerBlue"/>
                <Setter Property="BorderThickness" Value="5"/>
            </Style>
        </MenuFlyout.MenuFlyoutPresenterStyle>
        <MenuFlyoutItem Text="Stretch" IsEnabled="False"/>
        <MenuFlyoutSeparator/>
        <MenuFlyoutItem Text="Fill" Tag="Fill" 
                        DataContext="{Binding}" Click="ImageMenuFlyoutItem_Click"/>
        <MenuFlyoutItem Text="Uniform" Tag="Uniform" 
                        DataContext="{Binding}" Click="ImageMenuFlyoutItem_Click"/>
        <MenuFlyoutItem Text="UniformToFill" Tag="UniformToFill" 
                        DataContext="{Binding}" Click="ImageMenuFlyoutItem_Click"/>
    </MenuFlyout>
</Page.Resources>

...

<!-- Assign the flyout to each element that shares it. -->
<StackPanel Orientation="Horizontal" Margin="400,200">
    <Image Source="Assets/cliff.jpg" Width="200" Height="200" Margin="10" Tapped="Image_Tapped" 
           FlyoutBase.AttachedFlyout="{StaticResource ImageMenuFlyout}"
           DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"/>
    <Image Source="Assets/grapes.jpg" Width="200" Height="200" Margin="10" Tapped="Image_Tapped" 
           FlyoutBase.AttachedFlyout="{StaticResource ImageMenuFlyout}"
           DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"/>
    <Image Source="Assets/rainier.jpg" Width="200" Height="200" Margin="10" Tapped="Image_Tapped" 
           FlyoutBase.AttachedFlyout="{StaticResource ImageMenuFlyout}"
           DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"/>
</StackPanel>
...
// Open the MenuFlyout when the image is tapped.
private void Image_Tapped(object sender, TappedRoutedEventArgs e)
{
    FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);  
}


// Update the image stretch property when a menu item is clicked.
private void ImageMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
    MenuFlyoutItem item = sender as MenuFlyoutItem;

    if (item != null)
    {
        Image img = item.DataContext as Image;

        if (img != null)
        {
            if (item.Tag.ToString() == "Fill")
                img.Stretch = Stretch.Fill;
            else if (item.Tag.ToString() == "UniformToFill")
                img.Stretch = Stretch.UniformToFill;
            else img.Stretch = Stretch.Uniform;
        }
    }
}

When the user taps the image, the open flyout looks like this. The user can choose a Stretch value for the image.

Showing and hiding a MenuFlyout

Only one flyout can be open at a time. When a flyout opens, any other flyout that's already open is automatically dismissed.

The MenuFlyout is dismissed automatically when the user selects a menu item in the flyout, or when an action is cancelled by the user clicking outside of the flyout.

A flyout that's attached to a Button is shown automatically when the button is clicked. When a flyout is attached to any other element, you must open the flyout in response to an event, typically a Tapped event. In the event handler, call the FlyoutBase.ShowAttachedFlyout method. This is shown in the second example in this tutorial.

    FlyoutBase.ShowAttachedFlyout((FrameworkElement)sender);  

A flyout is typically positioned near the element it's attached to in order to keep the user in their current context. If it makes sense to position the flyout near a related element other than the element that invoked it, you can call ShowAt. For more info and examples, see the ShowAt method.

Sharing a MenuFlyout

A MenuFlyout declared in XAML can be declared either inline or as a resource that can be shared. The first example in this tutorial shows how to declare a MenuFlyout inline in a button's XAML. This MenuFlyout can only be attached to the Button where it's declared.

The second example in this tutorial shows how to declare a MenuFlyout as a StaticResource and use it with several different elements. In this example, the DataContext of the main Image is set to itself.

<Image DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"/>

The MenuFlyout inherits the DataContext of its anchor element. You can use the DataContext of the menu item that's clicked to access the Image that the user tapped. In this way, a single Flyout is used to change the Stretch property of whichever Image the user taps.

MenuFlyoutItem item = sender as MenuFlyoutItem;
if (item != null)
{
    Image img = item.DataContext as Image;
...
}

This example shows the syntax for declaring a Flyout inline or as a resource that can be shared.

<!-- Inline flyouts -->
<!-- MenuFlyout declared inline on a Button -->
<Button>
    <Button.Flyout>
        <MenuFlyout>
        <!-- MenuFlyoutItem content -->
        </MenuFlyout>
    </Button.Flyout>
</Button>

<!-- MenuFlyout declared inline on a FrameworkElement -->
<TextBlock>
    <FlyoutBase.AttachedFlyout>
        <MenuFlyout>
        <!-- MenuFlyoutItem content -->
        </MenuFlyout>
    </FlyoutBase.AttachedFlyout>
</TextBlock>


<!-- Shared resource flyouts -->
<!-- A shared MenuFlyout resource -->
<Page.Resources>
    <MenuFlyout x:Key="SharedMenuFlyoutResource">
        <!-- MenuFlyoutItem content -->
    </MenuFlyout>
</Page.Resources>

<!-- A shared MenuFlyout resource assigned to a Button -->
<Button Flyout="{StaticResource SharedMenuFlyoutResource}"/>

<!-- A shared MenuFlyout resource assigned to a FrameworkElement -->
<TextBlock FlyoutBase.AttachedFlyout="{StaticResource SharedMenuFlyoutResource}"/>

Placing a MenuFlyout

By default, a MenuFlyout is shown above the element it’s attached to. You can change the flyout’s preferred placement by setting the Placement property to a FlyoutPlacementMode value of Top, Bottom, Left, or Right. The flyout is shown using the preferred placement if there's space for it on the screen. If there isn’t enough space, like when the element is near the edge of the screen, the flyout is placed using this fallback order:

Placement Fall back order
Top Top > Bottom > Left > Right
Bottom Bottom > Top > Left > Right
Left Left > Right > Top > Bottom
Right Right > Left > Top > Bottom

 

You can specify the preferred placement of the MenuFlyout by setting the Placement property as shown here.

    <Flyout x:Key="ImagePreviewFlyout" Placement="Right">
    ...
    </Flyout>

Note  In a Windows Phone Store app, MenuFlyout always takes up the full width of the screen. Top and Bottom values are ignored when the app is in landscape orientation, and Left and Right values are ignored when the app is in portrait orientation.

Styling a MenuFlyout

The body of a MenuFlyout is always shown using either the light or high-contrast theme. You can't style a MenuFlyout control directly.

You can modify some aspects of the flyout's appearance, such as background, border, size constraints, and ScrollViewer settings, by styling the flyout's MenuFlyoutPresenter.

In the image flyout example shown previously, the flyout's appearance is modified to have a light blue border by setting the MenuFlyoutPresenterStyle, as shown here.

        <MenuFlyout.MenuFlyoutPresenterStyle>
            <Style TargetType="MenuFlyoutPresenter">
                <Setter Property="BorderBrush" Value="CornflowerBlue"/>
                <Setter Property="BorderThickness" Value="5"/>
            </Style>
        </MenuFlyout.MenuFlyoutPresenterStyle>

Samples

To learn more about the MenuFlyout control, download the XAML Flyout and MenuFlyout sample.

Summary and next steps

You learned how to use a MenuFlyout control to temporarily show a list of contextual commands or options to a user.

Learn how to use a Flyout control to show contextual info and actions to users in the Quickstart: Adding a MenuFlyout tutorial.