如果源绑定适用,如何访问快速访问工具栏命令“添加到快速访问工具”
How to access Quick Access tool bar command `Add to Quick Access Tool` if source binding applicable
如果我已经为它绑定了集合,我该如何添加 RibbonLibrary 默认的快速访问项容器。它抛出 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; }
}
时出错
如果不清楚,请添加评论。
问题是 ContextMenu 正在尝试使用 ItemCollection.Add() 将项目添加到集合中,但是如果使用 ItemsSource 来填充集合并且该方法将抛出此方法,则不支持此方法一个例外。
查看源码:ItemCollection.cs
一种解决方案是隐藏 ContextMenu 以避免调用其不受支持的功能,或者尝试将其替换为您自己的自定义 ContextMenu,它绑定到 ViewModel 上的命令并更新 QuickMenuItems。
可能存在某种使用附加属性的黑客行为。
一种解决方法是不使用 RibbonQuickAccessToolBar.ItemsSource。如果需要通知 QuickAccessToolbar 的更改,请订阅 RibbonQuickAccessToolBar.Items 的 INotifyCollectionChanged。
public RibbonWindow() {
InitializeComponent();
((INotifyCollectionChanged)QuickAccessToolBar.Items).CollectionChanged += QuickAccessToolBar_ItemsCollectionChanged;
}
private void QuickAccessToolBar_ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
//Do stuff or synchronize to observableCollection
}
}
这将允许您从功能区控件和 ViewModel 添加快捷菜单项。我使用 ListBox
作为 ViewModel 和 RibbonQuickAccessToolBar
之间的代理。当项目添加到 ListBox
时,视图会将它们添加到 RibbonQuickAccessToolBar
。
创建一个折叠的 ListBox
,它将绑定到 ViewModel 中的集合,并删除 RibbonQuickAccessToolBar
:
的 ItemsSource
绑定
<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>
在代码隐藏中,使用 ListBox 的 DataContextChanged 为 ListBox.ItemsSource 的 CollectionChanged 事件附加事件处理程序:
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;
}
ViewModel 与之前相同:
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; }
}
}
最后我能够通过代码解决我的问题,因为我正在为 RibbonWindow
使用 Attach Behavior,这里是解决方案:
public class RibbonWindowQuickAccessItemBehaviour : Behavior<RibbonWindow>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += RibbonControl_Loaded;
AssociatedObject.Closing += RibbonControl_Closing;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= RibbonControl_Loaded;
AssociatedObject.Closing -= RibbonControl_Closing;
}
private void RibbonControl_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var ribbonWindow = (RibbonWindow)sender;
var ribbon = ribbonWindow.FindAllChildrenOfType<Microsoft.Windows.Controls.Ribbon.Ribbon>().Where(a => a.Name == "ribbon").FirstOrDefault();
if (ribbon == null) return;
var dataContext = ribbon.DataContext;
if (dataContext is IRibbonMainWindowViewModel)
{
IRibbon windowsRibbon = (dataContext as IRibbonMainWindowViewModel).WindowRibbon;
windowsRibbon.QuickAccessMenuItems.Clear();
if (ribbon.QuickAccessToolBar != null && ribbon.QuickAccessToolBar.Items != null && ribbon.QuickAccessToolBar.Items.Count > 0)
{
foreach (var item in ribbon.QuickAccessToolBar.Items)
{
if (item is RibbonButton)
{
var ribbonItem = item as RibbonButton;
windowsRibbon.QuickAccessMenuItems.Add
(new Gno.Framework.ClientShell.Ribbon.MenuItem(ribbonItem.Label,
ribbonItem.SmallImageSource == null ? string.Empty : ribbonItem.SmallImageSource.ToString(),
ribbonItem.Command));
}
}
}
}
}
private void RibbonControl_Loaded(object sender, RoutedEventArgs e)
{
var ribbonWindow = (RibbonWindow)sender;
var ribbon = ribbonWindow.FindAllChildrenOfType<Microsoft.Windows.Controls.Ribbon.Ribbon>().Where(a => a.Name == "ribbon").FirstOrDefault();
if (ribbon == null) return;
var dataContext = ribbon.DataContext;
if (dataContext is IRibbonMainWindowViewModel)
{
IRibbon windowsRibbon = (dataContext as IRibbonMainWindowViewModel).WindowRibbon;
var quickAccessMenus = windowsRibbon.QuickAccessMenuItems;
if (quickAccessMenus != null && quickAccessMenus.Count > 0)
{
ribbon.QuickAccessToolBar = new RibbonQuickAccessToolBar();
foreach (var quickMenu in windowsRibbon.QuickAccessMenuItems)
{
var ribbonButton = new RibbonButton();
ribbonButton.Command = quickMenu.Command;
ribbonButton.Label = quickMenu.Header;
if (!string.IsNullOrEmpty(quickMenu.ImageUri))
ribbonButton.SmallImageSource = new BitmapImage(new Uri(quickMenu.ImageUri));
ribbon.QuickAccessToolBar.Items.Add(ribbonButton);
}
}
}
}
}
实际上这是保存QuickAccessToolBarItem的代码,因此Window关闭和加载事件需要绑定。
如果我已经为它绑定了集合,我该如何添加 RibbonLibrary 默认的快速访问项容器。它抛出 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; }
}
时出错
如果不清楚,请添加评论。
问题是 ContextMenu 正在尝试使用 ItemCollection.Add() 将项目添加到集合中,但是如果使用 ItemsSource 来填充集合并且该方法将抛出此方法,则不支持此方法一个例外。
查看源码:ItemCollection.cs
一种解决方案是隐藏 ContextMenu 以避免调用其不受支持的功能,或者尝试将其替换为您自己的自定义 ContextMenu,它绑定到 ViewModel 上的命令并更新 QuickMenuItems。
可能存在某种使用附加属性的黑客行为。
一种解决方法是不使用 RibbonQuickAccessToolBar.ItemsSource。如果需要通知 QuickAccessToolbar 的更改,请订阅 RibbonQuickAccessToolBar.Items 的 INotifyCollectionChanged。
public RibbonWindow() {
InitializeComponent();
((INotifyCollectionChanged)QuickAccessToolBar.Items).CollectionChanged += QuickAccessToolBar_ItemsCollectionChanged;
}
private void QuickAccessToolBar_ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
//Do stuff or synchronize to observableCollection
}
}
这将允许您从功能区控件和 ViewModel 添加快捷菜单项。我使用 ListBox
作为 ViewModel 和 RibbonQuickAccessToolBar
之间的代理。当项目添加到 ListBox
时,视图会将它们添加到 RibbonQuickAccessToolBar
。
创建一个折叠的 ListBox
,它将绑定到 ViewModel 中的集合,并删除 RibbonQuickAccessToolBar
:
ItemsSource
绑定
<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>
在代码隐藏中,使用 ListBox 的 DataContextChanged 为 ListBox.ItemsSource 的 CollectionChanged 事件附加事件处理程序:
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;
}
ViewModel 与之前相同:
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; }
}
}
最后我能够通过代码解决我的问题,因为我正在为 RibbonWindow
使用 Attach Behavior,这里是解决方案:
public class RibbonWindowQuickAccessItemBehaviour : Behavior<RibbonWindow>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += RibbonControl_Loaded;
AssociatedObject.Closing += RibbonControl_Closing;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= RibbonControl_Loaded;
AssociatedObject.Closing -= RibbonControl_Closing;
}
private void RibbonControl_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var ribbonWindow = (RibbonWindow)sender;
var ribbon = ribbonWindow.FindAllChildrenOfType<Microsoft.Windows.Controls.Ribbon.Ribbon>().Where(a => a.Name == "ribbon").FirstOrDefault();
if (ribbon == null) return;
var dataContext = ribbon.DataContext;
if (dataContext is IRibbonMainWindowViewModel)
{
IRibbon windowsRibbon = (dataContext as IRibbonMainWindowViewModel).WindowRibbon;
windowsRibbon.QuickAccessMenuItems.Clear();
if (ribbon.QuickAccessToolBar != null && ribbon.QuickAccessToolBar.Items != null && ribbon.QuickAccessToolBar.Items.Count > 0)
{
foreach (var item in ribbon.QuickAccessToolBar.Items)
{
if (item is RibbonButton)
{
var ribbonItem = item as RibbonButton;
windowsRibbon.QuickAccessMenuItems.Add
(new Gno.Framework.ClientShell.Ribbon.MenuItem(ribbonItem.Label,
ribbonItem.SmallImageSource == null ? string.Empty : ribbonItem.SmallImageSource.ToString(),
ribbonItem.Command));
}
}
}
}
}
private void RibbonControl_Loaded(object sender, RoutedEventArgs e)
{
var ribbonWindow = (RibbonWindow)sender;
var ribbon = ribbonWindow.FindAllChildrenOfType<Microsoft.Windows.Controls.Ribbon.Ribbon>().Where(a => a.Name == "ribbon").FirstOrDefault();
if (ribbon == null) return;
var dataContext = ribbon.DataContext;
if (dataContext is IRibbonMainWindowViewModel)
{
IRibbon windowsRibbon = (dataContext as IRibbonMainWindowViewModel).WindowRibbon;
var quickAccessMenus = windowsRibbon.QuickAccessMenuItems;
if (quickAccessMenus != null && quickAccessMenus.Count > 0)
{
ribbon.QuickAccessToolBar = new RibbonQuickAccessToolBar();
foreach (var quickMenu in windowsRibbon.QuickAccessMenuItems)
{
var ribbonButton = new RibbonButton();
ribbonButton.Command = quickMenu.Command;
ribbonButton.Label = quickMenu.Header;
if (!string.IsNullOrEmpty(quickMenu.ImageUri))
ribbonButton.SmallImageSource = new BitmapImage(new Uri(quickMenu.ImageUri));
ribbon.QuickAccessToolBar.Items.Add(ribbonButton);
}
}
}
}
}
实际上这是保存QuickAccessToolBarItem的代码,因此Window关闭和加载事件需要绑定。