Reputation: 15
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
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