WPF 命令全局可用
本文关键字:全局 命令 WPF | 更新日期: 2023-09-27 18:32:06
我有一个包含RibbonMenue
的Window
元素。在这个Window
有一些UserControls
。在其中一个UserControl
是一个DataGrid
.我创建了一个ICommand
,它允许我在DataGrid
中添加和删除行。
问题是我不知何故需要从RibbonMenu
访问这些ICommands
,但我只能在"更高级别"(窗口)访问它们,因为它们被声明并绑定到绑定到UserControl
的ViewModel
。
如何创建可全局调用的ICommands
?请注意,ICommand
需要引用我的ViewModel
,该位于UserControl
后面,因为我需要从中删除行等等。
我希望图像使它更清晰一些
传统的MVVM执行"全局命令"的方式是使用CompositeCommand。您将有一个 GlobalCommand.cs 文件,其中包含一个名为 GlobalCommand 的静态类。
在其中,您将拥有返回复合命令实例的 ICommand 属性。然后,任何对该命令感兴趣的 VM 都可以在其构造函数中附加到它:GlobalCommands.SomeCommand.RegisterCommand(...)。您的 UI 将附加到全局命令命令。
因此,全局命令将保存复合命令,这只是一个空的外壳/持有者命令,虚拟机将使用复合命令注册一个普通的中继命令并处理该命令。多个 VM 可以使用同一命令注册,并且将调用所有 VM。
更高级的 CompositeCommand 实现还包括一个 IActiveAware 功能,该功能可以使 CompositeCommand 仅将 canexecute/execute 发送到"活动"虚拟机。
我相信CompositeCommand最初来自Prism,但许多人(包括我自己)刚刚将其分解用于非Prism应用程序。
我设法得到了你需要的东西,我在这里做了一个单例命令(对不起,长帖子只是想确保你让它正常工作):
using System;
using System.Windows.Input;
namespace WpfApplication
{
public class GlobalCommand<T> : ICommand
{
#region Fields
private readonly Action<T> _execute = null;
private readonly Predicate<T> _canExecute = null;
private static GlobalCommand<T> _globalCommand;
private static readonly object locker = new object();
#endregion
#region Constructors
public static GlobalCommand<T> GetInstance(Action<T> execute)
{
return GetInstance(execute, null);
}
public static GlobalCommand<T> GetInstance(Action<T> execute, Predicate<T> canExecute)
{
lock (locker)
{
if (_globalCommand == null)
{
_globalCommand = new GlobalCommand<T>(execute, canExecute);
}
}
return _globalCommand;
}
private GlobalCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion
}
}
视图模型
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
namespace WpfApplication
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Category> Categories { get; set; }
public ICommand AddRowCommand { get; set; }
public ViewModel()
{
Categories = new ObservableCollection<Category>()
{
new Category(){ Id = 1, Name = "Cat1", Description = "This is Cat1 Desc"},
new Category(){ Id = 1, Name = "Cat2", Description = "This is Cat2 Desc"},
new Category(){ Id = 1, Name = "Cat3", Description = "This is Cat3 Desc"},
new Category(){ Id = 1, Name = "Cat4", Description = "This is Cat4 Desc"}
};
this.AddRowCommand = GlobalCommand<object>.GetInstance(ExecuteAddRowCommand, CanExecuteAddRowCommand);
}
private bool CanExecuteAddRowCommand(object parameter)
{
if (Categories.Count <= 15)
return true;
return false;
}
private void ExecuteAddRowCommand(object parameter)
{
Categories.Add(new Category()
{
Id = 1,
Name = "Cat"+(Categories.Count+1),
Description = "This is Cat" + (Categories.Count + 1) + " Desc"
});
}
}
}
型
namespace WpfApplication
{
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
MainWindow.Xaml
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="MainWindow" Height="500" Width="525">
<Window.Resources>
<local:ViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Ribbon x:Name="RibbonWin" SelectedIndex="0" Grid.Row="0">
<Ribbon.QuickAccessToolBar>
<RibbonQuickAccessToolBar>
<RibbonButton x:Name ="Delete" Content="Delete a row" Click="Delete_Click"/>
</RibbonQuickAccessToolBar>
</Ribbon.QuickAccessToolBar>
</Ribbon>
<UserControl Grid.Row="1" x:Name="UserControl" DataContext="{StaticResource ViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Path=Categories}" AutoGenerateColumns="False" CanUserAddRows="False" Margin="0,10,0,100" Name="DataGrid1" Grid.Row="0" >
<DataGrid.Columns>
<DataGridTextColumn Header="Id" IsReadOnly="True" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Name" IsReadOnly="True" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Description" IsReadOnly="True" Binding="{Binding Description}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Add new row" Command="{Binding Path=AddRowCommand}" HorizontalAlignment="Center" Width="75" Grid.Row="1"/>
</Grid>
</UserControl>
</Grid>
</Window>
代码隐藏
using System.Windows;
namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Delete_Click(object sender, RoutedEventArgs e)
{
GlobalCommand<object>.GetInstance(null).Execute(null);// I'm not quite happy with this but it works
}
}
}