WPF команды доступны по всему миру

У меня есть Window элемент, который содержит RibbonMenue, В этом Window Есть некоторые UserControls, В одном из UserControl это DataGrid, Я создал ICommand что позволяет мне добавлять и удалять строки из DataGrid,

Проблема в том, что мне как-то нужен доступ к этим ICommands от RibbonMenu, но я просто могу получить к ним доступ на "более высоком уровне" (в окне), поскольку они объявлены и привязаны к ViewModel который связан с UserControl,

Как я могу создать ICommands который можно назвать глобально? Обратите внимание, что ICommand нужна ссылка на мой ViewModel который находится за UserControl так как мне нужно удалить строки из него и так далее.

Надеюсь, изображение делает это немного яснее

2 ответа

Мне удалось получить то, что вам нужно, я сделал единственную команду, вот целый пример (извините за длинный пост, просто хотел убедиться, что он работает правильно):

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
    }
}


ViewModel

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
        }
    }
}

Традиционный способ MVVM выполнять "глобальные команды" - использовать CompositeCommand. У вас будет файл GlobalCommands.cs, который содержит статический класс GlobalCommands.

В нем вы будете иметь свои свойства ICommand, которые возвращают экземпляр CompositeCommand. Затем любая виртуальная машина, заинтересованная в команде, может присоединиться к ней в своем конструкторе: GlobalCommands.SomeCommand.RegisterCommand(...). Ваш пользовательский интерфейс будет привязан к командам GlobalCommands.

Таким образом, GlobalCommands будут содержать CompositeCommand, которая является просто пустой командой shell / holder, а виртуальные машины будут регистрировать обычную RelayCommand с помощью составной команды и обрабатывать команду. Несколько виртуальных машин могут зарегистрироваться с помощью одной и той же команды, и все они будут вызваны.

Более продвинутые реализации CompositeCommand также включают в себя функцию IActiveAware, которая может заставить CompositeCommand только отправлять canexecute / execute к "активным" виртуальным машинам.

Я полагаю, что CompositeCommand изначально пришел из Prism, но многие люди (включая меня) просто взломали его для использования в приложениях, не относящихся к Prism.

Другие вопросы по тегам