Search code examples
c#wpfribboncontrolslibrary

How to access Quick Access tool bar command `Add to Quick Access Tool` if source binding applicable


How can I add Quick Access Item container default by RibbonLibrary if I have binded collection for it. Its throws Operation is not valid while ItemSource is in use while is I add Quick Access tool item from UI.

<r:Ribbon Name="ribbon">

        <r:Ribbon.QuickAccessToolBar>

            <r:RibbonQuickAccessToolBar ItemsSource ="{Binding QuickMenuItems, Mode=OneWay}">
                <r:RibbonQuickAccessToolBar.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <r:RibbonButton QuickAccessToolBarId="{Binding RibbonId}" Label="{Binding Label}" SmallImageSource="{Binding ImageUri}" Command="{Binding Command}"/>
                        </StackPanel>
                    </DataTemplate>
                </r:RibbonQuickAccessToolBar.ItemTemplate>
            </r:RibbonQuickAccessToolBar>

        </r:Ribbon.QuickAccessToolBar>

        <r:RibbonTab Header="Home">
            <r:RibbonGroup x:Name="Clipboard" ItemsSource ="{Binding MenuItems, Mode=OneWay}" >

                <r:RibbonGroup.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <r:RibbonButton QuickAccessToolBarId="{Binding RibbonId}" Label="{Binding Label}" SmallImageSource="{Binding ImageUri}" Command="{Binding Command}"/>
                        </StackPanel>
                    </DataTemplate>
                </r:RibbonGroup.ItemTemplate>

            </r:RibbonGroup>
        </r:RibbonTab>

    </r:Ribbon>


 ObservableCollection<RibbonItem> _MenuItems;
 ObservableCollection<RibbonItem> _QuickMenuItems;

 public ObservableCollection<RibbonItem> MenuItems
 {
      get { return _MenuItems; }
 }
 public ObservableCollection<RibbonItem> QuickMenuItems
 {
      get { return _QuickMenuItems; }
 }
public class RibbonItem
{
    public RibbonItem(string label, string imageUri, ICommand command, string ribbonId)
    {
        Label = label;
        ImageUri = imageUri;
        Command = command;
    }

    public string Label { get; private set; }

    public string ImageUri { get; private set; }

    public ICommand Command { get; private set; }

    public string RibbonId { get; private set; }
}

Error while

enter image description here

enter image description here
Add comment if not clear.


Solution

  • This will allow you to add quick menu items from both ribbon control and ViewModel. I have used a ListBox to act as a proxy between the ViewModel and the RibbonQuickAccessToolBar. When items are added to the ListBox, the view will add them to the RibbonQuickAccessToolBar.

    Create a collapsed ListBox that will bind to the collection in the ViewModel and remove the ItemsSource binding for the RibbonQuickAccessToolBar:

    <ListBox ItemsSource="{Binding QuickMenuItems, Mode=OneWay}"
                x:Name="ProxyListBox"
                Visibility="Collapsed"/>
    <ribbon:Ribbon Name="ribbon">
        <ribbon:Ribbon.QuickAccessToolBar>
            <ribbon:RibbonQuickAccessToolBar x:Name="QuickAccessToolBar" DataContextChanged="QuickAccessToolBar_OnDataContextChanged">
                <ribbon:RibbonQuickAccessToolBar.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <ribbon:RibbonButton QuickAccessToolBarId="{Binding RibbonId}" Label="{Binding Label}" SmallImageSource="{Binding ImageUri}" Command="{Binding Command}"/>
                        </StackPanel>
                    </DataTemplate>
                </ribbon:RibbonQuickAccessToolBar.ItemTemplate>
            </ribbon:RibbonQuickAccessToolBar>
        </ribbon:Ribbon.QuickAccessToolBar>
        <ribbon:RibbonTab Header="Home">
            <ribbon:RibbonGroup x:Name="Clipboard" ItemsSource ="{Binding MenuItems, Mode=OneWay}" >
                <ribbon:RibbonGroup.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <ribbon:RibbonButton QuickAccessToolBarId="{Binding RibbonId}" Label="{Binding Label}" SmallImageSource="{Binding ImageUri}" Command="{Binding Command}"/>
                        </StackPanel>
                    </DataTemplate>
                </ribbon:RibbonGroup.ItemTemplate>
            </ribbon:RibbonGroup>
        </ribbon:RibbonTab>
    </ribbon:Ribbon>
    

    In the code-behind use the DataContextChanged of the ListBox to attach an event handler for the ListBox.ItemsSource's CollectionChanged event:

    private void ProxyListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (var newItem in e.NewItems)
            {
                QuickAccessToolBar.Items.Add(newItem);
            }
        }
    
        if (e.OldItems != null)
        {
            foreach (var oldItem in e.OldItems)
            {
                QuickAccessToolBar.Items.Remove(oldItem);
            }
        }
    }
    
    private void QuickAccessToolBar_OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        ((INotifyCollectionChanged)ProxyListBox.Items).CollectionChanged += ProxyListBox_CollectionChanged;
    }
    

    The ViewModel is the same as before:

    class RibbonViewModel
    {
        ObservableCollection<RibbonItem> _MenuItems;
    
        ObservableCollection<RibbonItem> _QuickMenuItems;
    
        public ObservableCollection<RibbonItem> MenuItems
        {
            get { return _MenuItems; }
        }
    
        public ObservableCollection<RibbonItem> QuickMenuItems
        {
            get { return _QuickMenuItems; }
        }
    
        public RibbonViewModel()
        {
            _QuickMenuItems = new ObservableCollection<RibbonItem>();
            _MenuItems = new ObservableCollection<RibbonItem>();
        }
    
        public class RibbonItem
        {
            public RibbonItem(string label, string imageUri, ICommand command)
            {
                Label = label;
                ImageUri = imageUri;
                Command = command;
            }
    
            public string Label { get; private set; }
    
            public string ImageUri { get; private set; }
    
            public ICommand Command { get; private set; }
    
            public string RibbonId { get; private set; }
        }
    }