Based on chingucoding's answer I got to the "recent files list" binding working.
For completeness I post the detailed code snippets here (keep in mind, that I'm not an expert):
Again using Template Studio to create a WinUI 3 app.
ShellViewModel.cs
// constructor
public ShellViewModel(INavigationService navigationService, ILocalSettingsService localSettingsService)
{
...
MRUUpdateItems();
}
ShellViewModel_RecentFiles.cs ( <-- partial class )
using System.Collections.ObjectModel;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
namespace App_MostRecentUsedTest.ViewModels;
public partial class ShellViewModel : ObservableRecipient
{
public ObservableCollection<MRUItem> MRUItems{ get; set;} = new();
// update ObservableCollection<MRUItem>MRUItems from MostRecentlyUsedList
public void MRUUpdateItems()
{
var mruTokenList = StorageApplicationPermissions.MostRecentlyUsedList.Entries.Select(entry => entry.Token).ToList();
var mruMetadataList = StorageApplicationPermissions.MostRecentlyUsedList.Entries.Select(entry => entry.Metadata).ToList(); // contains path as string
MRUItems.Clear(); var i = 0;
foreach (var path in mruMetadataList)
{
MRUItems.Add(new MRUItem() { Path = path, Token = mruTokenList[i++] });
}
}
// called if user selects a recent used file from menu bar list
[RelayCommand]
protected async Task MRULoadFileClicked(int? fileId)
{
if (fileId is not null)
{
var mruItem = MRUItems[(int)fileId];
FileInfo fInfo = new FileInfo(mruItem.Path ?? "");
if (fInfo.Exists)
{
StorageFile? file = await Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruItem.Token);
if (file is not null)
{
Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file, file.Path); // store file.Path into Metadata
MRUUpdateItems();
// LOAD_FILE(file);
}
}
else
{
}
}
await Task.CompletedTask;
}
[RelayCommand]
protected async Task MenuLoadFileClicked()
{
StorageFile? file = await GetFilePathAsync();
if (file is not null)
{
Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.Add(file, file.Path); // store file.Path into Metadata
MRUUpdateItems();
// LOAD_FILE(file);
}
await Task.CompletedTask;
}
// get file path with filePicker
private async Task<StorageFile?> GetFilePathAsync()
{
FileOpenPicker filePicker = new();
filePicker.FileTypeFilter.Add(".txt");
IntPtr hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(filePicker, hwnd);
return await filePicker.PickSingleFileAsync();
}
public class MRUItem : INotifyPropertyChanged
{
private string? path;
private string? token;
public string? Path
{
get => path;
set
{
path = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(path));
}
}
public string? Token
{
get => token;
set => token = value;
}
public event PropertyChangedEventHandler? PropertyChanged;
}
}
ShellPage.xaml
<MenuBar>
<MenuBarItem x:Name="ShellMenuBarItem_File">
<MenuFlyoutItem x:Uid="ShellMenuItem_File_Load" Command="{x:Bind ViewModel.MenuLoadFileClickedCommand}" />
<MenuFlyoutSubItem x:Name="MRUFlyout" Text="Recent Files..." />
</MenuBarItem>
</MenuBar>
ShellPage.xaml.cs
// constructor
public ShellPage(ShellViewModel viewModel)
{
...
// MRU initialziation
// assign RecentFilesChanged() to CollectionChanged-event
ViewModel.MRUItems.CollectionChanged += RecentFilesChanged;
// Add (and RemoveAt) trigger RecentFilesChanged-event to update MenuFlyoutItems
ViewModel.MRUItems.Add(new MRUItem() { Path = "", Token = ""});
ViewModel.MRUItems.RemoveAt(ViewModel.MRUItems.Count - 1);
}
// MRU Handle collection change
private void RecentFilesChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// project each MRUItems list element into a new UI MenuFlyoutItem flyoutItems list
var i = 0;
var flyoutItems = ViewModel.MRUItems.Select(entry =>
new MenuFlyoutItem()
{
Text = " " + i.ToString() + " " + FilenameHelper.EllipsisString(entry.Path, 65),
Command = ViewModel.MRULoadFileClickedCommand,
CommandParameter = i++
}
);
//// If you want to update the list while it is shown,
//// you will need to create a new FlyoutItem because of
//// https://github.com/microsoft/microsoft-ui-xaml/issues/7797
// Create a new flyout and populate it
var newFlyout = new MenuFlyoutSubItem();
newFlyout.Text = MRUFlyout.Text; // Text="Recent Files...";
// Updating your MenuFlyoutItem
flyoutItems.ToList().ForEach(item => newFlyout.Items.Add(item));
// Get index of old sub item and remove it
var oldIndex = ShellMenuBarItem_File.Items.IndexOf(MRUFlyout);
ShellMenuBarItem_File.Items.Remove(MRUFlyout);
// Insert the new flyout at the correct position
ShellMenuBarItem_File.Items.Insert(oldIndex, newFlyout);
// Assign newFlyout to "old"-MRUFlyout
MRUFlyout = newFlyout;
}