I have a Window
element which contains a RibbonMenue
. Within this Window
there are some UserControls
. In one of the UserControl
is a DataGrid
. I created a ICommand
which lets me add and delete rows from the DataGrid
.
The problem is that I somehow need access to these ICommands
from the RibbonMenu
, but I just can access them at the "higher level" (the Window) since they are declared and bound to the ViewModel
which is bound to the UserControl
.
How can I create ICommands
which can be called globally? Note that the ICommand
needs a reference to my ViewModel
which is behind the UserControl
since I need to delete rows from it and so on.
The traditional MVVM way to do "global commands" is to use CompositeCommand. You'll have a GlobalCommands.cs file which contains a static class called GlobalCommands.
In it, you'll have your ICommand properties which return a CompositeCommand instance. Then, any VM that is interested in the command can attach to it in thier constructor: GlobalCommands.SomeCommand.RegisterCommand(...). Your UI would attach to the GlobalCommands commands.
So GlobalCommands will hold the CompositeCommand which is just an empty shell / holder command and the VMs will register a normal RelayCommand with the composite command and handle the command. Multiple VMs can register with the same command and all will be called.
The more advanced CompositeCommand implementations also include a IActiveAware feature that can make the CompositeCommand only send the canexecute/execute to the "active" vm's.
I believe CompositeCommand originally came from Prism, but many folks (including myself) have just broken it out for use in non Prism applications.
I managed to get what you need, I made a singleton command here is whole example (sorry for long post just wanted to make sure you get it work correcly):
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"
});
}
}
}
Model
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>
Code behind
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
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.