Home

Awesome

<img src="./The49.Maui.ContextMenu.TitleLogo.svg?raw=true" height="64" />

What is Maui.ContextMenu?

Maui.ContextMenu is a .NET MAUI library for Android and iOS used to open a native context menu on long press.

AndroidiOS
<img src="screenshots/android.png?raw=true" height="480" /><img src="screenshots/ios.png?raw=true" height="480" />

Getting Started

Enable this plugin by calling UseContextMenu() in your MauiProgram.cs

using The49.Maui.ContextMenu;

public static class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		
		// Initialise the plugin
		builder.UseMauiApp<App>().UseContextMenu();

		// the rest of your logic...
	}
}

XAML usage

In order to make use of the plugin within XAML you can use this namespace:

xmlns:the49="https://schemas.the49.com/dotnet/2023/maui"

Then you can add a context menu to any control:

<ContentView HeightRequest="200" WidthRequest="200" Background="GreenYellow">
    <the49:ContextMenu.Menu>
        <DataTemplate>
            <the49:Menu>
                <the49:Action Title="Upload documents" />
                <the49:Action Title="Copy" />
                <the49:Action Title="Cut" />
                <the49:Action Title="Paste" />
            </the49:Menu>
        </DataTemplate>
    </the49:ContextMenu.Menu>
</ContentView>

Defining the menu

The available elements for the menu are:

The <the49:Menu> element supports the following parameters:

PropertyTypeDescriptioniOSAndroid
TitlestringA string title to be displayed at the top of the context menu

The <the49:Group> element supports the following parameters:

PropertyTypeDescriptioniOSAndroid
TitlestringA string title to be displayed at the top of the group

The <the49:Action> element supports the following parameters:

PropertyTypeDescriptioniOSAndroid
TitlestringThe text for the action
CommandICommandA command to execute when the user selects this action
CommandParameterobjectA parameter that will be passed to the Command
IconImageSourceAn icon to be displayed next to the action text
SystemIconstringA string reference to a system icon to use instead of the Icon✅*❌**
IsEnabledboolEnables/Disables the action in the menu
IsVisibleboolHides/Displays the action in the menu
IsDestructiveboolDisplays the action as destructive

Here is what a more complex menu would look like using most properties available:

<the49:Menu Title="More options">
    <the49:Action Title="Copy" SystemIcon="doc.on.clipboard" />
    <the49:Action Title="Upload documents" Icon="dotnet_bot.png" />
    <the49:Group Title="Lifecycle">
        <the49:Action Title="Start" Command="{Binding StartCommand, Source={x:Reference this}}" CommandParameter="foo" />
        <the49:Action Title="Stop" IsDestructive="True" />
    </the49:Group>
    <the49:Menu Title="Clipboard">
        <the49:Action Title="Copy" SystemIcon="doc.on.clipboard" IsEnabled="False" />
        <the49:Action Title="Paste" IsVisible="False" />
    </the49:Menu>
</the49:Menu>

Customising the preview

When an item is selected for its context menu to open, a highlighted preview can be displayed. This preview by default is a snapshot of the view itself. This can be customised using the Preview property.

PropertyTypeDescriptioniOSAndroid
PreviewTemplateDataTemplateProvide a different view to render the preview
VisiblePathIShapeCustomise the path used to clip the preview✅*
BackgroundColorColorThe background color for the hightlight preview
PaddingThicknessThe padding of the VisiblePath within the highlight preview

Example:

<the49:ContextMenu.Preview>
    <the49:Preview BackgroundColor="Red" Padding="16">
        <the49:Preview.PreviewTemplate>
            <DataTemplate>
                <ContentView WidthRequest="100" HeightRequest="100" Background="Green" />
            </DataTemplate>
        </the49:Preview.PreviewTemplate>
        <the49:Preview.VisiblePath>
            <RoundRectangle CornerRadius="10, 20, 30, 40" />
        </the49:Preview.VisiblePath>
    </the49:Preview>
</the49:ContextMenu.Preview>

Standard click

Because of how context menus ar attached to view on each different platforms, the TapGestureRecognizer might not work in combination with the context menu. This is why this plugin also offers a ClickCommand property to ensure your command is called on every platform.

<ContentView 
        the49:ContextMenu.ClickCommand="{Binding ClickCommand}"
        the49:ContextMenu.ClickCommandParameter="Hello">
    <the49:ContextMenu.Menu>
        <DataTemplate>
            <!-- ... -->
        </DataTemplate>
    </the49:ContextMenu.Menu>
</ContentView>

Show menu on click

Sometimes you want to show the context menu on a click instead of the default long press/right click. Setting ShowMenuOnClick to true will simply do that. The highlight preview will however not be shown in this mode.

On iOS it is not possible to open a context menu on click. This plugin uses the UIKit showsMenuAsPrimaryAction property to support this feature. This is however only supported on UIKit.UIbutton, meaning ShowMenuOnClick will only work on Button on iOS

TableView, ListView, CollectionView

To add a contextmenu to all items of a CollectionView for example:

<CollectionView>
    <the49:ContextMenu.Menu>
        <DataTemplate>
            <the49:Menu>
                <the49:Action Title="Upload" />
                <the49:Action Title="Delete" />
            </the49:Menu>
        </DataTemplate>
    </the49:ContextMenu.Menu>
    <!-- ... -->
</CollectionView>

This will add a contextmenu to each item of a CollectionView. However all the menus will be identical. If you need to have the menu change based on the item of the CollectionView selected, you can do so by using bindings. By default, the BindingContext of the <the49:Menu> is the item the user opened the context menu from.

This can also be useful to pass the item to the command of an action.

For example, if the Items of the CollectionView are:


public class UserDocuments {
    public string Name { get; set; }
    public bool CanUpload { get; set; }
}

You can configure the context menu for these items like so:

<the49:CollectionView>
    <the49:ContextMenu.Menu>
        <DataTemplate>
            <the49:Menu>
                <the49:Action
                    Title="Upload"
                    IsVisible="{Binding CanUpload}"
                    Command="{Binding UploadDocument, Source={x:Reference this}}"
                    CommandParameter="{Binding .}"
                />
            </the49:Menu>
        </DataTemplate>
    </the49:ContextMenu.Menu>
    <!-- ... -->
</the49:CollectionView>

Implementation details

This plugins aims to use the underlying platform's features as much as possible, however when needed the platform was extended to offer the best user experience.

iOS

iOS provides a native and comprehensive context menu feature. This was used to support this plugin.

The UIContextMenuInteraction was used to support standalone controls. For items views, contextMenuConfigurationForItemsAt or an equivalent method was used

Android

Android offers a native context menu API. However this API basically just opens a PopupMenu anchored to the target view.

This plugin implements a more complete context menu experience by adding a highlighted preview and multiple other features to match the featureset provided by iOS.

A MenuBuilder is still used to conform the custom implementation to all other Android menus.

TODO:


<img src="https://the49.com/logo.svg" height="64" />

Made within The49