How to Add a Context Menu to .NET MAUI ListView? | Syncfusion Blogs
Detailed Blog page Skeleton loader
How to Add a Context Menu to .NET MAUI ListView

TL;DR: Discover how to add a context menu to the .NET MAUI ListView for quick actions like copy, delete, and mark as favorite. Using the .NET MAUI Popup control, we’ll display the menu on long-press or right-tap gestures. This blog covers defining menu actions, handling user interactions, and seamlessly integrating the popup with ListView. Elevate your app’s usability with an intuitive and responsive context menu!

In mobile and desktop applications, users expect interactive and responsive interfaces. A well-designed context menu in your list view can help users engage more intuitively with content by adding functionality like quick actions for deleting or marking items as favorites and providing undo options.

In this blog, we’ll create a context menu in the Syncfusion .NET MAUI ListView control and learn to display it on long-press or right-tap actions. This implementation is ideal for email clients, to-do lists, or other apps where users frequently interact with a list of items.

.NET MAUI ListView: An overview

The .NET MAUI ListView control presents lists of data virtually in a vertical or horizontal orientation with different layouts. It supports essential features, such as selection, template selectors, horizontal and vertical orientation, load more, autofitting items, and more. The control also supports sorting, grouping, and filtering with optimization for working with large amounts of data.

Implementing a context menu in your .NET MAUI app is straightforward with the ListView control, which adds a layer of interactivity that makes your UI feel responsive and user-friendly.

This blog includes the following sections:

  • Preparing a context menu UI for quick actions.
  • Preparing the list view to display the inbox items.
  • Showing the context menu on a popup.

Let’s get started!

Preparing a context menu UI for quick actions

First, create a context menu using the .NET MAUI Popup control. This context menu will appear when an item is long-pressed or right-tapped.

Note: Before proceeding, refer to the getting started with .NET MAUI Popup control documentation.

Step 1: Define the context menu action class

Create a class to define the actions that will populate in the context menu. Each action will have a name and an associated icon. Refer to the following code example.

public class ContextMenuAction
{
    public ContextMenuAction()   
    {
    }
    public string ActionName { get; set; } // Holds the action name
    public string ActionIcon { get; set; } // Holds the picture for the action
}

Step 2: Populate actions in ViewModel

In the ViewModel, create an ObservableCollection to store the context menu actions. This collection will be populated with a few quick actions.

public class ViewModel : INotifyPropertyChanged
{
    public ObservableCollection<ContextMenuAction> ContextMenuActions { get; set; }

    public ViewModel()
    {
        GenerateContextMenuActions();
    }

    private void GenerateContextMenuActions()
    {
        ContextMenuActions = new ObservableCollection<ContextMenuAction>();
        ContextMenuActions.Add(new ContextMenuAction() { ActionName = "Copy", ActionIcon = "\uE737" });
        ContextMenuActions.Add(new ContextMenuAction() { ActionName = "Delete" , ActionIcon= "\uE73C" });
        ContextMenuActions.Add(new ContextMenuAction() { ActionName = "Archive", ActionIcon = "\uE777" });
        ContextMenuActions.Add(new ContextMenuAction() { ActionName = "Mark as Read/Unread", ActionIcon= "\uE75C" });
        ContextMenuActions.Add(new ContextMenuAction() { ActionName = "Mark as Favorite", ActionIcon= "\uE73A" });
    }
}

Step 3: Handling user interactions

Now that the context menu is defined and populated, the next step is to manage user interactions when they click on the quick actions. Here, we’ll implement the logic to handle the various actions within the ViewModel.

public class ViewModel : INotifyPropertyChanged
{
    private async void OnContextMenuClicked(object eventArgs)
    {
        var eventData = eventArgs as Syncfusion.Maui.ListView.ItemTappedEventArgs;
        if (eventData == null)
            return;

        var action = (eventData.DataItem as ContextMenuAction).ActionName;
        switch(action)
        {
            case "Copy":
                OnExcuteCopyCommand(); 
                break;
            case "Delete":
                OnDelete(listViewItem);
                break;
            case "Archive":
                OnArchive(listViewItem);
                break;
            case "Mark as Read/Unread":
                listViewItem.IsOpened = !listViewItem.IsOpened;
                break;
            case "Mark as Favorite":
                OnSetFavorites(listViewItem);
                break;
        }
        ContextMenuPopup.IsOpen = false;
    }

    private void OnExcuteCopyCommand()
    {
        string text = "From" + ":" + listViewItem.ProfileName + "\n" + "Subject" + ":" + listViewItem.Subject + "\n" + "Received" + ":" + listViewItem.Date;
        text = Regex.Replace(text, "<.*?>| ", string.Empty);
        Clipboard.SetTextAsync(text);
    }

    private async void OnArchive(object item)
    {
        PopUpText = "Archived";
        listViewItem = (ListViewInboxInfo)item;
        listViewItemIndex = inboxInfo!.IndexOf(listViewItem);
        inboxInfo!.Remove(listViewItem);
        archivedMessages!.Add(listViewItem);
        this.IsDeleted = true;
        await Task.Delay(3000);
        this.IsDeleted = false;
    }

    private void OnUndoAction()
    {
        this.IsDeleted = false;
        if (listViewItem != null)
        {
            inboxInfo!.Insert(listViewItemIndex, listViewItem);
        }
        listViewItemIndex = 0;
        listViewItem = null;
    }

    private void OnSetFavorites(object item)
    {
        var listViewItem = item as ListViewInboxInfo;
        if ((bool)listViewItem!.IsFavorite)
        {
            listViewItem.IsFavorite = false;
        }
        else
        {
            listViewItem.IsFavorite = true;
        }          
    }

    private async void OnDelete(object item)
    {
        PopUpText = "Deleted";            
        listViewItemIndex = inboxInfo!.IndexOf(listViewItem);   
        inboxInfo!.Remove(listViewItem);
        this.IsDeleted = true;
        await Task.Delay(3000);
        this.IsDeleted = false;                    
    }
}

Step 4: Define the popup on the XAML page

Then, define the popup in the ContentPage.Resources section for easy reuse. Refer to the following code example.

<popup:SfPopup x:Name="contextMenuPopup" x:Key="contextMenu" ShowHeader="False" WidthRequest="250" HeightRequest="225">
    <popup:SfPopup.ContentTemplate>
        <DataTemplate>
            <syncfusion:SfListView Margin="10" ItemSize="40" ItemsSource="{Binding ContextMenuActions}"  TapCommand="{Binding ContextMenuCommand}">
                <syncfusion:SfListView.ItemTemplate>
                    <DataTemplate>

                            <Label Grid.Column="0" Margin="5" Text="{Binding ActionIcon}" FontSize="18" FontAttributes="Bold" FontFamily="MauiSampleFontIcon" VerticalOptions="Center" VerticalTextAlignment="Center"/>
                            <Label x:Name="label" Grid.Column="1" VerticalTextAlignment="Center" Text="{Binding ActionName}" FontSize="16"/>

                    </DataTemplate>
                </syncfusion:SfListView.ItemTemplate>
            </syncfusion:SfListView>
        </DataTemplate>
    </popup:SfPopup.ContentTemplate>
</popup:SfPopup>

In the above code example, we’ve shown the action items in the .NET MAUI ListView and enabled the actions by tapping them.

Prepare the .NET MAUI ListView to display the inbox items

Let’s create a responsive inbox item list view showcasing essential features such as data binding, item selection, and interaction commands.

Step 1: Defining the inbox item class

Create a class to define the inbox information that will be displayed in the .NET MAUI ListView.

public class ListViewInboxInfo : INotifyPropertyChanged
{
    #region Fields
        
    private string? profileName;
    private string? subject;
    private string? desc;
    private string? date;
    private string? image;
    private bool? isAttached;
    private bool isOpened;
    private bool isFavorite = true;

    #endregion

    #region Constructor

    public ListViewInboxInfo()
    {

    }

    #endregion

    #region Properties

    public string? ProfileName
    {
        get { return profileName; }
        set
        {
            profileName = value;
            OnPropertyChanged("Title");
        }
    }

    public string? Subject
    {
        get
        {
            return subject;
        }

        set
        {
            subject = value;
            OnPropertyChanged("Subject");
        }
    }

    public string? Description
    {
        get
        {
            return desc;
        }

        set
        {
            desc = value;
            OnPropertyChanged("Description");
        }
    }

    public string? Date
    {
        get
        {
            return date;
        }

        set
        {
            date = value;
            OnPropertyChanged("Date");
        }
    }

    public string? Image
    {
        get
        {
            return image;
        }

        set
        {
            image = value;
            OnPropertyChanged("Image");
        }
    }

    public bool? IsAttached
    {
        get { return isAttached; }
        set
        {
            isAttached = value;
            OnPropertyChanged("IsAttached");
        }
    }

    public bool IsFavorite
    {
        get { return isFavorite; }
        set
        {
            isFavorite = value;
            OnPropertyChanged("IsFavorite");
        }
    }

    public bool IsOpened
    {
        get { return isOpened; }
        set
        {
            isOpened = value;
            OnPropertyChanged("IsOpened");
        }
    }

    #endregion

    #region Interface Member

    public event PropertyChangedEventHandler? PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

    #endregion
}

Step 2: Populate the inbox information in ViewModel

Then, create a ViewModel class to:

  • Store and manage the data for the inbox items, which we’ll bind to the SfListView.
  • Define commands that will trigger the context menu on gestures.
    public class ViewModel : INotifyPropertyChanged
    {
    
        private ObservableCollection<ListViewInboxInfo>? inboxInfo;
    
        #region Constructor
    
        public ViewModel()
        {
            GenerateSource();
        }
    
        #endregion
    
        public ObservableCollection<ListViewInboxInfo>? InboxInfo
        {
            get { return inboxInfo; }
            set { this.inboxInfo = value; OnPropertyChanged("InboxInfo"); }
        }
    
        #region Generate Source
    
        private void GenerateSource()
        {        
            ListViewInboxInfoRepository inboxinfo = new ListViewInboxInfoRepository();   
            inboxInfo = inboxinfo.GetInboxInfo();            
        }
    }
    

Step 3: Creating the XAML layout

Now, create the layout using SfListView to display the inbox items. Refer to the following code example to implement the .NET MAUI ListView to showcase how to bind the data and define the item template.

<syncfusion:SfListView Grid.Row="1"  x:Name="listView"
                            ItemsSource="{Binding InboxInfo}"                                                           
                            SelectionMode="Single"                             
                            AutoFitMode="Height">

    <syncfusion:SfListView.ItemTemplate>
        <DataTemplate>
             
                    ---------------------------
                <Grid Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" HeightRequest="40" WidthRequest="40" HorizontalOptions="Center" VerticalOptions="Center">
                    <Image Source="{Binding Image}" HeightRequest="40" WidthRequest="40" />
                    <Label Text="{Binding ProfileName}" FontSize="16" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center" VerticalTextAlignment="Center" FontFamily="Roboto-Regular" CharacterSpacing="0.25"/>

                    ---------------------------

                </Grid>
        </DataTemplate>
    </syncfusion:SfListView.ItemTemplate>
</syncfusion:SfListView>

In the above code example, the ViewModel properties are bound to the SfListView. Now, run the project, and you will get the following output.

.NET MAUI ListView displaying the inbox items
.NET MAUI ListView displaying the inbox items

Show the context menu on a popup

To show the context menu on user gestures, define the LongPressCommand and RightTappedCommand properties in the ViewModel. These commands are bound to gesture events in the SfListView and will display the context menu at the touchpoint when triggered.

public class ViewModel : INotifyPropertyChanged
{
    private Command<object> longPressCommand;
    private Command<object> rightTappedCommand;


    public ViewModel()
    {
        DefineCommands();
   }

    public Command<object> LongPressCommand 
    {
        get { return  longPressCommand; }
        set
        {
            longPressCommand = value;
        }
    }
    public Command<object> RightTappedCommand
    {
        get { return rightTappedCommand; }
        set
        {
            rightTappedCommand = value;
        }
    }

    private void DefineCommands()
    {
        undoCommand = new Command(OnUndoAction);
        favoritesImageCommand = new Command(OnSetFavorites);
        LongPressCommand = new Command<object>(OnLongPress);
        rightTappedCommand = new Command<object>(OnRightTap);
        contextMenuCommand = new Command<object>(OnContextMenuClicked);
    }
    private async void OnLongPress(object eventArgs)
    {
        var eventData = eventArgs as Syncfusion.Maui.ListView.ItemLongPressEventArgs;
        await Task.Delay(200);
        ContextMenuActions = null;
        GenerateContextMenuActions();
        listViewItem = (ListViewInboxInfo)eventData.DataItem;
        ContextMenuPopup.Show((int)eventData.Position.X, (int)eventData.Position.Y);
    }

    private async void OnRightTap(object eventArgs)
    {
        var eventData = eventArgs as Syncfusion.Maui.ListView.ItemRightTappedEventArgs;
        await Task.Delay(200);
        ContextMenuActions = null;
        GenerateContextMenuActions();
        listViewItem = (ListViewInboxInfo)eventData.DataItem;
        ContextMenuPopup.Show((int)eventData.Position.X, (int)eventData.Position.Y);
    }

    #endregion
}

Finally, bind the commands to the SfListView.

<syncfusion:SfListView Grid.Row="1" x:Name="listView"
                            ItemsSource="{Binding InboxInfo}"                                                           
                            SelectionMode="Single"
                            LongPressCommand="{Binding LongPressCommand}"    
                            RightTapCommand="{Binding RightTappedCommand}"                                 
                            AutoFitMode="Height">
</syncfusion:SfListView>

Executing the above code example will give us the context menu when right-clicking or long-pressing the list view items. 

Adding a context menu to .NET MAUI ListView
Adding a context menu to .NET MAUI ListView

GitHub reference

For more details, refer to the adding context menu to the .NET MAUI ListView GitHub demo.

Supercharge your cross-platform apps with Syncfusion's robust .NET MAUI controls.

Conclusion

Thanks for reading the blog post! Implementing context menus in .NET MAUI ListView is crucial for user-friendly interactions. Enhance your app’s interactivity by integrating quick actions like deleting or marking items as favorites in context menus. 

If you’re an existing Syncfusion user, you can download the latest version of Essential Studio® from the License and Downloads page. For those new to Syncfusion, we offer a 30-day free trial so you can experience these exciting features firsthand.

Need help? Feel free to reach out via our support forumsupport portal, or feedback portal. We’re always here to assist you!

Test Flight
App Center Badge
Google Play Store Badge
Microsoft Badge
Github Store Badge

Be the first to get updates

Jayaleshwari N

Meet the Author

Jayaleshwari N

Jayaleshwari N works for Syncfusion as a product manager. She has been a .NET developer since 2013, and has experience in the development of custom controls in Xamarin and MAUI platforms.