Learn Prism’s Composite Commands
Learn the section Composite Commands from RRISM LIBRARY Documentation.
It’s a translation of that, but as a memo.
If you don’t speak Japanese, go back to English and understand it. .. ..
In many cases, a command defined by a view model will be bound to controls in the associated view so that the user can directly invoke the command from within the view. However, in some cases, you may want to be able to invoke commands on one or more view models from a control in a parent view in the application’s UI.
For example, if your application allows the user to edit multiple items at the same time, you may want to allow the user to save all the items using a single command represented by a button in the application’s toolbar or ribbon. In this case, the Save All command will invoke each of the Save commands implemented by the view model instance for each item as shown in the following illustration.
(Google Translate)
Commands defined by the view model are often bound to the controls in the associated view, allowing users to call commands directly from within the view. However, in some cases, you may want to be able to call commands from one or more view models from a control in the parent view of your application’s UI.
For example, if your application allows users to edit multiple items at the same time, you can allow the user to save all items using a single command represented by the application’s toolbar or ribbon button. I can do it. In this case, the Save All command calls each save command implemented by the view model instance for each item, as shown in the following figure.
Prism supports this scenario through the CompositeCommand class.
The CompositeCommand class represents a command that is composed from multiple child commands. When the composite command is invoked, each of its child commands is invoked in turn. It is useful in situations where you need to represent a group of commands as a single command in the UI or where you want to invoke multiple commands to implement a logical command.
The CompositeCommand class maintains a list of child commands (DelegateCommand instances). The Execute method of the CompositeCommand class simply calls the Execute method on each of the child commands in turn. The CanExecute method similarly calls the CanExecute method of each child command, but if any of the child commands cannot be executed, the CanExecute method will return false. In other words, by default, a CompositeCommand can only be executed when all the child commands can be executed.
(Google Translate)
Prism supports this scenario through the CompositeCommand class.
The CompositeCommand class represents a command that consists of multiple child commands. When a compound command is called, each of its child commands is called in turn. This is useful if you need to represent a group of commands as a single command in the UI, or if you need to call multiple commands to implement a logical command.
The CompositeCommand class holds a list of child commands (DelegateCommand instances). The Execute method of the CompositeCommand class simply calls the Execute method of each child command in turn. The CanExecute method also calls the CanExecute method of each child command, but the CanExecute method returns false if there are child commands that cannot be executed. That is, by default, you can run CompositeCommand only if you can run all child commands.
NOTE
CompositeCommand can be found in the Prism.Commands namespace which is located in the Prism.Core NuGet package.
(Google Translate)
Caution
CompositeCommand is located in the Prism.Commands namespace in the Prism.Core NuGet package.
[]
(https://www.youtube.com/watch?v=kssprOqdfME&feature=emb_logo)
Creating a Composite Command
To create a composite command, instantiate a CompositeCommand instance and then expose it as either an ICommand or ComponsiteCommand property.
(Google Translate)
To create a compound command, instantiate a CompositeCommand instance and then expose it as either the ICommand or the CompositeCommand property.
public class ApplicationCommands
{
private CompositeCommand _saveCommand = new CompositeCommand();
public CompositeCommand SaveCommand
{
get { return _saveCommand; }
}
}
Making a CompositeCommand Globally Available
Typically, CompositeCommands are shared throughout an application and need to be made available globally. It’s important that when you register a child command with a CompositeCommand that you are using the same instance of the CompositeCommand throughout the application. This requires the CompositeCommand to be defined as a singleton in your application. This can be done by either using dependency injection (DI), or by defining your CompositeCommand as a static class.
(Google Translate)
CompositeCommands typically need to be shared across applications and made available globally. When registering a child command with CompositeCommand, it is important that the entire application uses the same instance of CompositeCommand. This requires the application to define CompositeCommand as a singleton. To do this, use Dependency Injection (DI) or define CompositeCommand as a static class.
Using Dependency Injection
The first step in defining your CompositeCommands is to create an interface.
(Google Translate)
The first step in defining CompositeCommands is to create an interface.
public interface IApplicationCommands
{
CompositeCommand SaveCommand { get; }
}
Next, create a class that implements the interface.
(Google Translate)
Next, create a class that implements the interface.
public class ApplicationCommands : IApplicationCommands
{
private CompositeCommand _saveCommand = new CompositeCommand();
public CompositeCommand SaveCommand
{
get { return _saveCommand; }
}
}
Once you have defined your ApplicationCommands class, you must register it as a singleton with the container.
(Google Translate)
After defining the ApplicationCommands class, you need to register it in the container as a singleton.
public partial class App : PrismApplication
{
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
}
}
Next, ask for the IApplicationCommands interface in the ViewModel constructor. Once you have an instance of the ApplicationCommands class, can now register your DelegateCommands with the appropriate CompositeCommand.
(Google Translate)
Then request the IApplicationCommands interface in the ViewModel constructor. Once you have an instance of the ApplicationCommands class, you can register DelegateCommands with the appropriate CompositeCommand.
public DelegateCommand UpdateCommand { get; private set; }
public TabViewModel(IApplicationCommands applicationCommands)
{
UpdateCommand = new DelegateCommand(Update);
applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
}
Using a Static Class
Create a static class that will represent your CompositeCommands
(Google Translate)
Create a static class that represents CompositeCommands
public static class ApplicationCommands
{
public static CompositeCommand SaveCommand = new CompositeCommand();
}
In your ViewModel, associate child commands to the static ApplicationCommands class.
(Google Translate)
In the ViewModel, associate the child command with the static ApplicationCommands class.
public DelegateCommand UpdateCommand { get; private set; }
public TabViewModel()
{
UpdateCommand = new DelegateCommand(Update);
ApplicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
}
NOTE
To increase the maintainability and testability of your code, it is recommended that you using the dependency injection approach.
(Google Translate)
Caution
We recommend that you use the Dependency Injection approach to improve the maintainability and testability of your code.
Binding to a Globally Available Command
Once you have created your CompositeCommands, you must now bind them to UI elements in order to invoke the commands.
(Google Translate)
Once you have created CompositeCommands, you need to bind them to the UI elements in order to call the commands.
Using Depency Injection
When using DI, you must expose the IApplicationCommands for binding to a View. In the ViewModel of the view, ask for the IApplicationCommands in the constructor and set a property of type IApplicationCommands to the instance.
(Google Translate)
If you use DI, you need to expose IApplicationCommands to bind to the view. In the view’s ViewModel, request IApplicationCommands from the constructor and set a property of type IApplicationCommands to the instance.
public class MainWindowViewModel : BindableBase
{
private IApplicationCommands _applicationCommands;
public IApplicationCommands ApplicationCommands
{
get { return _applicationCommands; }
set { SetProperty(ref _applicationCommands, value); }
}
public MainWindowViewModel(IApplicationCommands applicationCommands)
{
ApplicationCommands = applicationCommands;
}
}
In the view, bind the button to the ApplicationCommands.SaveCommand property. The SaveCommand is a property that is defined on the ApplicationCommands class.
(Google Translate)
In the view, bind the button to the ApplicationCommands.SaveCommand property. SaveCommand is a property defined in the ApplicationCommands class.
<Button Content="Save" Command="{Binding ApplicationCommands.SaveCommand}"/>
Using a Static Class
If you are using the static class approach, the following code example shows how to bind a button to the static ApplicationCommands class in WPF.
(Google Translate)
If you’re using the static class approach, the following code example shows how to bind a button to WPF’s static ApplicationCommands class.
<Button Content="Save" Command="{x:Static local:ApplicationCommands.SaveCommand}" />
Unregister a Command
As seen in the previous examples, child commands are registered using the CompositeCommand.RegisterCommand method. However, when you no longer wish to respond to a CompositeCommand or if you are destroying the View/ViewModel for garbage collection, you should unregister the child commands with the CompositeCommand.UnregisterCommand method.
(Google Translate)
As we saw in the previous example, child commands are registered using the CompositeCommand.RegisterCommand method. However, if you no longer need to respond to CompositeCommand, or if you want to destroy the garbage collection View / ViewModel, you must unregister the child command using the CompositeCommand.UnregisterCommand method.
public void Destroy()
{
_applicationCommands.UnregisterCommand(UpdateCommand);
}
IMPORTANT
You MUST unregister your commands from a CompositeCommand when the View/ViewModel is no longer needed (ready for GC). Otherwise you will have introduced a memory leak.
(Google Translate)
important
When you no longer need the View / ViewModel (when the GC is ready), you need to unregister the command from CompositeCommand. Otherwise, you will get a memory leak.
Executing Commands on Active Views
Composite commands at the parent view level will often be used to coordinate how commands at the child view level are invoked. In some cases, you will want the commands for all shown views to be executed, as in the Save All command example described earlier. In other cases, you will want the command to be executed only on the active view. In this case, the composite command will execute the child commands only on views that are deemed to be active; it will not execute the child commands on views that are not active. For example, you may want to implement a Zoom command on the application’s toolbar that causes only the currently active item to be zoomed, as shown in the following diagram.
(Google Translate)
Composite commands at the parent view level are often used to coordinate how commands are invoked at the child view level. You may want to execute a command for all displayed views, as in the Save All command example above. Otherwise, you only need to run the command in the active view. In this case, the compound command executes the child command only in the view that is considered active. Do not execute child commands in inactive views. For example, implementing the zoom command on the application toolbar will zoom only the currently active item, as shown in the following figure.
To support this scenario, Prism provides the IActiveAware interface. The IActiveAware interface defines an IsActive property that returns true when the implementer is active, and an IsActiveChanged event that is raised whenever the active state is changed.
You can implement the IActiveAware interface on views or ViewModels. It is primarily used to track the active state of a view. Whether or not a view is active is determined by the views within the specific control. For the Tab control, there is an adapter that sets the view in the currently selected tab as active, for example.
The DelegateCommand class also implements the IActiveAware interface. The CompositeCommand can be configured to evaluate the active status of child DelegateCommands (in addition to the CanExecute status) by specifying true for the monitorCommandActivity parameter in the constructor. When this parameter is set to true, the CompositeCommand class will consider each child DelegateCommand’s active status when determining the return value for the CanExecute method and when executing child commands within the Execute method.
(Google Translate)
To support this scenario, Prism provides an IActiveAware interface. The IActiveAware interface defines an IsActive property that returns true when the implementer is active, and an IsActiveChanged event that fires every time the active state changes.
You can implement the IActiveAware interface in a view or ViewModel. It is primarily used to track the active state of a view. Whether a view is active depends on the view within a particular control. For example, the tab control has an adapter that activates the view of the currently selected tab.
The DelegateCommand class also implements the IActiveAware interface. You can configure CompositeCommand to evaluate the active status of child DelegateCommands in addition to the CanExecute status by specifying true for the monitorCommandActivity parameter in the constructor. If this parameter is set to true, the CompositeCommand class considers the active status of each child DelegateCommand when determining the return value of the CanExecute method and when executing child commands within the Execute method.
public class ApplicationCommands : IApplicationCommands
{
private CompositeCommand _saveCommand = new CompositeCommand(true);
public CompositeCommand SaveCommand
{
get { return _saveCommand; }
}
}
When the monitorCommandActivity parameter is true, the CompositeCommand class exhibits the following behavior:
・ CanExecute: Returns true only when all active commands can be executed. Child commands that are inactive will not be considered at all.
-Execute: Executes all active commands. Child commands that are inactive will not be considered at all.
By implementing the IActiveAware interface on your ViewModels, you will be notified when your view becomes active or inactive. When the view’s active status changes, you can update the active status of the child commands. Then, when the user invokes the composite command, the command on the active child view will be invoked.
(Google Translate)
If the monitorCommandActivity parameter is true, the CompositeCommand class behaves as follows:
-CanExecute: Returns true only if all active commands can be executed. Inactive child commands are not considered at all.
-Execute: Executes all active commands. Inactive child commands are not considered at all.
By implementing the IActiveAware interface in your ViewModel, you will be notified when a view becomes active or inactive. You can update the active status of child commands when the active status of the view changes. Then, when the user calls the compound command, the command in the active child view is called.
public class TabViewModel : BindableBase, IActiveAware
{
private bool _isActive;
public bool IsActive
{
get { return _isActive; }
set
{
_isActive = value;
OnIsActiveChanged();
}
}
public event EventHandler IsActiveChanged;
public DelegateCommand UpdateCommand { get; private set; }
public TabViewModel(IApplicationCommands applicationCommands)
{
UpdateCommand = new DelegateCommand(Update);
applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
}
private void Update()
{
//implement logic
}
private void OnIsActiveChanged()
{
UpdateCommand.IsActive = IsActive; //set the command as active
IsActiveChanged?.Invoke(this, new EventArgs()); //invoke the event for all listeners
}
}