Harry
Harry

Reputation: 15

Can't Select MRU Recent Files Item Twice

I have an application which uses a RibbonApplicationMenu which I've created using the example at WPF ObservableCollection not updating in ribbon view. This lists the most recently opened files. However it's not possible to select the same item twice. When an item is selected for the first time the selection is highlighted and the file opens using the XAML SelectionChanged event. However if selected again the selection is ignored. The only way around this seems to be to select another item then select the original one.

I suspect I need to set the selected item to null after opening but when I've tried adding a 'SelectedItem' event in the xaml and assigning null to it it doesn't work.

XAML

SelectedValue="{Binding SelectedValue, Mode=TwoWay}"

C#

public object SelectedValue
{
    get => selectedValue;
    set => SetProperty(ref selectedValue, null);
}

How can the original selected item be reselected to reopen the file?

Added: Code in MySettings.cs relating to reading the MRU file names

private ObservableCollection<KeyValuePair<string, string>> mostRecentFiles = new ObservableCollection<KeyValuePair<string, string>>();
public ObservableCollection<KeyValuePair<string, string>> MostRecentFiles
{
    get => mostRecentFiles;
    set
    {
        mostRecentFiles = value;
        OnPropertyChanged(nameof(MostRecentFiles)); // Not shown
    }
}

private KeyValuePair<string, string> selectedRecentFile;
public KeyValuePair<string, string> SelectedRecentFile
{
    get => selectedRecentFile;
    set
    {
        selectedRecentFile = value;
        OnSelectedMostRecentFileChanged(); // Not shown
        OnPropertyChanged(nameof(SelectedRecentFile));
    }
}

Added: Relevant XAML MRU list code

<ribbon:Ribbon.ApplicationMenu>
    <!-- Additional commands (e.g. Open, New, Save...) removed -->
    <ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
        <ribbon:RibbonGallery Name="RecentDocuments" CanUserFilter="False"
              SelectionChanged="MRUSelectionChanged"
              SelectedItem="{Binding SelectedRecentFile, Mode=TwoWay}"
              ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ribbon:RibbonGalleryCategory Header="Recent Documents"
                    HeaderTemplate="{StaticResource recentFilesTemplate}"
                    Background="Transparent"
                    ItemsSource="{Binding MostRecentFiles}" DisplayMemberPath="Value">
                <ribbon:RibbonGalleryCategory.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Vertical" IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </ribbon:RibbonGalleryCategory.ItemsPanel>
            </ribbon:RibbonGalleryCategory>
        </ribbon:RibbonGallery>
    </ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
    <!-- Additional Code -->
    <ribbon:RibbonApplicationMenu.ItemContainerStyle>
        <Style TargetType="RibbonApplicationMenuItem">
            <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType=RibbonApplicationMenu}, Path=DataContext.MyCommand}" />
            <Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource AncestorType=RibbonApplicationMenu}, Path=SelectedItem}" />
        </Style>
    </ribbon:RibbonApplicationMenu.ItemContainerStyle>
</ribbon:RibbonApplicationMenu>

Upvotes: 1

Views: 95

Answers (1)

BionicCode
BionicCode

Reputation: 29028

The binding only updates the target/source when the value of the SelectedItem has changed. Clicking the item multiple times does not change anything.

After you have provided even more context, I revoke my previous suggestions. The final solution would have to rely on handling the PreviewMouseLeftButtonUp event instead of using the command.

You can associate a command with the items of the ribbon gallery (via the RibbonGaller.Command property), but this command would be only executed once - on item selection.
You could override the RibbonGalleryItem template, but this would break the native look and feel unless you completely replicate the original template.

Registering an event handler is much simpler. The key changes are the addition of the RibbonGalleryCategory.ItemContainerStyle to add the EventSetter to register the PreviewMouseLeftButtonUp event handler. The MyCommand is no longer needed in this context.

The solution could look as follows:

<Ribbon>
  <Ribbon.ApplicationMenu>
    <!-- Additional commands (e.g. Open, New, Save...) removed -->

    <RibbonApplicationMenu>
      <RibbonApplicationMenu.AuxiliaryPaneContent>   
        <RibbonGallery Name="RecentDocuments"
                       CanUserFilter="False"
                       ScrollViewer.VerticalScrollBarVisibility="Auto">  
          <RibbonGalleryCategory Header="Recent Documents"
                                 HeaderTemplate="{StaticResource recentFilesTemplate}"
                                 Background="Transparent"
                                 ItemsSource="{Binding MostRecentFiles}" 
                                 DisplayMemberPath="Value">
            <RibbonGalleryCategory.ItemContainerStyle>
              <Style TargetType="RibbonGalleryItem">
                <EventSetter Event="PreviewMouseLeftButtonUp"
                             Handler="OnRibbonGalleryItemClicked" /> 
              </Style>  
            </RibbonGalleryCategory.ItemContainerStyle> 

            <RibbonGalleryCategory.ItemsPanel>
              <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical"
                            IsItemsHost="True" />
              </ItemsPanelTemplate>
            </RibbonGalleryCategory.ItemsPanel>
          </RibbonGalleryCategory>
        </RibbonGallery>
      </RibbonApplicationMenu.AuxiliaryPaneContent>

      <!-- Additional Code -->

    </RibbonApplicationMenu>
  </Ribbon.ApplicationMenu>
</Ribbon>
private void OnRibbonGalleryItemClicked(object sender, MouseButtonEventArgs e)
{
  var clickedRibbonGalleryItem = (RibbonGalleryItem)sender;

  // You may want to perform the following cast in the class that actually 
  // handles the file. This reduces the coupling. 
  // For example, changing the source collection type 
  // won't break this code if the cast is avoided. 
  // Instead, pass the clicked file entry as type 'object'
  // to the context that has the full knowledge of the type it handles 
  // and can gracefully perform the cast.
  // I only added this cast to demonstrate how the data 
  // can be extracted from the clicked container.
  var clickedMostRecentFilesEntry = (KeyValuePair<string, string>)clickedRibbonGalleryItem.Content;

  // TODO::From here you can invoke a command or a method of the class 
  // that is responsible for opening the file 
}

Upvotes: 1

Related Questions