1

I've created a C# WPF app with a RibbonApplicationMenu displaying a Most Recently Used (MRU) list. Unfortunately the display doesn't update when I select an existing file from the list or upload a new file. In the XAML I have:

<local:MostRecentFiles x:Key="MostRecentFilesData" />
    ...
<ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
    <ribbon:RibbonGallery Name="RecentDocuments" CanUserFilter="False" 
        SelectedValue="{Binding MostRecentFile, UpdateSourceTrigger=PropertyChanged}">
        <ribbon:RibbonGalleryCategory Header="Recent Documents"
            ItemsSource="{DynamicResource MostRecentFilesData}">
        </ribbon:RibbonGalleryCategory>
    </ribbon:RibbonGallery>
</ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>

The DataContext is set to a class containing

private ObservableCollection<string> _mostRecentFile = new ObservableCollection<string>();
public ObservableCollection<string> MostRecentFile
{
    get { return _mostRecentFile; }
    set
    {
        _mostRecentFile = value;
        OnPropertyChanged("MostRecentFile");
    }
}

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

In the OpenFile routine the code is

MostRecentFiles mrf = new MostRecentFiles();
mrf.AddMRUitem(openFileDlg.FileName);

The MostRecentFiles class contains the main class methods and I've put some sample file paths in the code.

public class MostRecentFiles : ObservableCollection<string>
{
    public ObservableCollection<string> MRUmenuItems = new ObservableCollection<string>();
    public MostRecentFiles()
    {
        AddMRUitem(@"C:\MyDocuments\File3.txt"); //
        AddMRUitem(@"C:\MyDocuments\File2.txt"); // } Sample files
        AddMRUitem(@"C:\MyDocuments\File1.txt"); //
    }

    public void AddMRUitem(string filePath)
    {
        int result;
        result = MRUmenuItems.IndexOf(filePath);
        if (result != -1)
        {
            MRUmenuItems.Remove(filePath);
            MRUmenuItems.Insert(0, filePath);
        }
        else
            AddMenuItem(filePath);
        UpdateMRUList();
    }

    private void UpdateMRUList()
    {
        this.Clear();
        foreach (string filePath in MRUmenuItems)
        {
            this.Add(filePath);
        }
        //OnPropertyChanged("MostRecentFile"); // <= Error CS1503
    }

    private void AddMenuItem(string newMRUfile)
    {
        MRUmenuItems.Insert(0, newMRUfile);
        if (MRUmenuItems.Count > 10)
        {
            MRUmenuItems.RemoveAt(MRUmenuItems.Count - 1);
        }
    }
    private string _mostRecentFile = "";
    public string MostRecentFile
    {
        get { return _mostRecentFile; }
        set
        {
            if (_mostRecentFile == value) return;
            _mostRecentFile = value;
            AddMRUitem(_mostRecentFile);
            //OnPropertyChanged("MostRecentFile");
        }
    }
}

Undeleting OnPropertyChanged in UpdateMRUList() produces the error: Error CS1503 Argument 1: cannot convert from 'string' to 'System.ComponentModel.PropertyChangedEventArgs'

When I launch the program the menu correctly displays the three files but when I select one the displayed order doesn't change; I expect the selected file to move to the top of the list. Similarly when I open a new file the filename isn't added to the MRU.

However if I step through the code the lists are being updated in the correct order. What have I done wrong?

Andy Smith
  • 13
  • 4
  • What is `DynamicResource MostRecentFilesData`? And how is `SelectedValue="{Binding MostRecentFile}"` supposed to work when MostRecentFile is a collection? Your code is confusing. Take a look at [Binding to Collections](https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/data-binding-overview#binding-to-collections). – Clemens Jul 20 '19 at 10:09
  • The `protected` method `ObservableCollection.OnPropertyChanged` has a different signature. It takes `PropertyChangedEventArgs` as an argument and not a `string`. Change the call to `OnPropertyChanged(new PropertyChangedEventArgs("MostRecentFile"))` and it should work. – BionicCode Jul 20 '19 at 12:32
  • @BionicCode I changed the call and, although it got rid of the error, it still didn't work. – Andy Smith Jul 20 '19 at 14:09
  • @Clemens It should read . I've now corrected it. Changing MostRecentFiles to a unique name didn't work. I'll have a look at the linked site to see if it helps. Thanks. – Andy Smith Jul 20 '19 at 14:21
  • Why do you bind `SelectedValue` to a collection??? – BionicCode Jul 20 '19 at 14:24
  • Don't use your collection. Use a view model collection. I show you. Wait... – BionicCode Jul 20 '19 at 14:25
  • I missed out `MostRecentFile()` to the above which I've now added. This binds to the `SelectedValue`. – Andy Smith Jul 20 '19 at 14:37

1 Answers1

1

You are binding SelectedValue to a collection. You don't need a custom collection. Just add an ObservableCollection to your view model and move items on selected item changed:

View model:

private void OnSelectedMostRecentFileChanged()
{
  // Move the selected item to the front of the list
  this.MostRecentFiles.Move(this.MostRecentFiles.IndexOf(this.SelectedRecentFile), 0);
}

private string _selectedRecentFile;
public string SelectedRecentFile
{
    get { return _selectedRecentFile; }
    set
    {
        _selectedRecentFile= value;
        OnSelectedMostRecentFileChanged();
        OnPropertyChanged(nameof(SelectedRecentFile));
    }
}

private ObservableCollection<string> _mostRecentFiles = new ObservableCollection<string>();
public ObservableCollection<string> MostRecentFiles
{
    get { return _mostRecentFiles; }
    set
    {
        _mostRecentFiles = value;
        OnPropertyChanged(nameof(MostRecentFiles));
    }
}

View:

<ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
    <ribbon:RibbonGallery Name="RecentDocuments" CanUserFilter="False" 
        SelectedItem="{Binding SelectedRecentFile}">
        <ribbon:RibbonGalleryCategory Header="Recent Documents"
            ItemsSource="{Binding MostRecentFiles}">
        </ribbon:RibbonGalleryCategory>
    </ribbon:RibbonGallery>
</ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
BionicCode
  • 1
  • 4
  • 28
  • 44
  • Thanks. I'll give it a try. Got to go out know but I'll let you know if it works. – Andy Smith Jul 20 '19 at 14:40
  • Thank you for your help. I've made the changes as suggested but I'm not sure how to add a new file. In my OpenFileDialog code I changed my code shown above to `ViewModel mrf = new ViewModel(); mrf.AddNewFile = openFileDlg.FileName;` and added `private string _addNewFile; public string AddNewFile get { return _addNewFile; } set { _addNewFile = value; this.MostRecentFiles.Insert(0, value); OnPropertyChanged(nameof(MostRecentFiles)); }` but this didn't work. – Andy Smith Jul 22 '19 at 10:22
  • Don't create a new view model instance. Use the one which is already used as the `DataContext`. Let's assume you are opening the dialog from your _MainWindow.xaml.cs_ and `MainWindow` has the `ViewModel` as `DataContext`. Then you'd simply invoke `(this.DataContext as ViewModel).MostRecentFiles.Insert(0, openFileDlg.FileName);` which will insert the filename at the start of the collection. And don't use your custom `MostRecentFiles` collection., Use `Observable` directly as property type. – BionicCode Jul 22 '19 at 10:29
  • Thank you very much, it worked .. and your explanation has helped me to understand it better. – Andy Smith Jul 22 '19 at 10:39