mvvmmenubarwinui-3winuimru

Create WinUI3/MVVM Most Recently Used (MRU) List in Menu Bar


I would like to create a classic "Recent Files" list in my Windows app menu bar (similar to Visual Studio's menu bar -> File -> Recent Files -> see recent files list)

The MRU list (List < string > myMRUList...) is known and is not in focus of this question. The problem is how to display and bind/interact with the list according to the MVVM rules.

Microsoft.Toolkit.Uwp.UI.Controls's Menu class will be removed in a future release and they recommend to use MenuBar control from the WinUI. I haven't found any examples, that use WinUI's MenuBar to create a "Recent Files" list.

I'm using Template Studio to create a WinUI 3 app. In the ShellPage.xaml I added

<MenuFlyoutSubItem x:Name="mruFlyout" Text="Recent Files"></MenuFlyoutSubItem> 

and in ShellPage.xaml.c

private void Button_Click(object sender, RoutedEventArgs e)
{
   mruFlyout.Items.Insert(mruFlyout.Items.Count, new MenuFlyoutItem(){ Text = "C:\\Test1_" + DateTime.Now.ToString("MMMM dd") } );
   mruFlyout.Items.Insert(mruFlyout.Items.Count, new MenuFlyoutItem(){ Text = "C:\\Test2_" + DateTime.Now.ToString("MMMM dd") } );
   mruFlyout.Items.Insert(mruFlyout.Items.Count, new MenuFlyoutItem(){ Text = "C:\\Test3_" + DateTime.Now.ToString("MMMM dd") } );
} 

knowing this is not MVVM, but even this approach does not work properly, because the dynamically generated MenuFlyoutItem can be updated only once by Button_Click() event.

Could anybody give me an example, how to create the "Recent Files" functionality, but any help would be great! Thanks


Solution

  • Unfortunately, it seems that there is no better solution than handling this in code behind since the Items collection is readonly and also doesn't response to changes in the UI Layout.

    In addition to that, note that because of https://github.com/microsoft/microsoft-ui-xaml/issues/7797, updating the Items collection does not get reflected until the Flyout has been closed and reopened.

    So assuming your ViewModel has an ObservableCollection, I would probably do this:

    // 1. Register collection changed
    MyViewModel.RecentFiles.CollectionChanged += RecentFilesChanged;
    
    // 2. Handle collection change
    private void RecentFilesChanged(object sender, NotifyCollectionChangedEventArgs args)
    {
        // 3. Create new UI collection
        var flyoutItems = list.Select(entry =>
            new MenuFlyoutItem()
            {
                Text = entry.Name
            }
        );
    
        // 4. Updating your MenuFlyoutItem
        mruFlyout.Items.Clear();
        flyoutItems.ForEach(entry => mruFlyout.Items.Add(entry));
    }