WPF 命令全局可用

本文关键字:全局 命令 WPF | 更新日期: 2023-09-27 18:32:06

我有一个包含RibbonMenueWindow元素。在这个Window有一些UserControls。在其中一个UserControl是一个DataGrid.我创建了一个ICommand,它允许我在DataGrid中添加和删除行。

问题是我不知何故需要从RibbonMenu访问这些ICommands,但我只能在"更高级别"(窗口)访问它们,因为它们被声明并绑定到绑定到UserControlViewModel

如何创建可全局调用的ICommands?请注意,ICommand需要引用我的ViewModel,该位于UserControl后面,因为我需要从中删除行等等。

我希望图像使它更清晰一些

WPF 命令全局可用

传统的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
        }
    }
}