CHAPTER 10
Earlier in this book we looked at how to update the user interface by setting states from the view model of the Math module. In this chapter, we'll look at how users of the UI can trigger code in the View Model by using Prism 4 commands.
Prism 4 commands are based on the GoF command design pattern. They are designed to allow users of user interfaces to trigger actions in code. In the case of MVVM, controls in the view (Buttons, Comboboxes etc.) trigger commands in the view model that carry out needed functionally in the solution.
Commands are a communication mechanism that can be used to communicate between views and view models or between Prism 4 modules. If the objective is to immediately react to an interface interaction, commands are a good choice.
WPF provides a routed command that is dependent on the visual tree when servicing commands. This type of command does not work well with Prism 4 solutions because in these solutions the view is separate from the entity that actually processes the UI interaction request.
Prism 4 commands on the other hand, uses a delegate command to overcome this issue. Delegate commands call a delegate method in response to a triggered action in the UI. This allows for interaction between the UI and view model, without dependency on the view's visual tree, This helps to maintain loose coupling between the view and view model while allowing communications between the two entities.
Prism 4 commands consist of three required and one optional part.
The Prism 4 command property is a public property that implements the ICommand interface. The command property is used to expose the view model based command to the view though data binding. The command can then be triggered from the view by an interaction trigger or behavior. Listing 40 shows an example of a Prism 4 command property.
Listing 40: A Prism 4 Delegate Command:
private ICommand CALCULATE_BUTTON_CLICK_COMMAND; public ICommand CalculateButtonClickCommand { get { return CALCULATE_BUTTON_CLICK_COMMAND; } set { if (CALCULATE_BUTTON_CLICK_COMMAND != value) { CALCULATE_BUTTON_CLICK_COMMAND = value; NotifyPropertyChanged("CalculateButtonClickCommand"); } } } |
The only code in Listing 40 that you may not be familiar with, is the NotifyPropertyChanged code in the property setter. If you recall from cahpter 7, this call is required to insure that the user interface is updated when changes are made to the property. It relies on the INotifyPropertyChanged interface.
The Prism 4 Code that Instantiates the Command:
The view model's class constructor is used to instantiate the command. This is necessary not only because the generic command class can take a payload but also because one or two associated methods are used with the delegate command type. Listing 41 shows an example of this code.
Listing 41: Instantiation of a Prism 4 Delegate Command:
CALCULATE_BUTTON_CLICK_COMMAND = new DelegateCommand(OnCalculateButtonClick); |
The new delegate command is applied to the associated command's private member. Note that the OnCalculateButtonClick method name is passed as an argument to the Delegate command's constructor.
The On method is the execute method that is associated with the command. This is the method (or if you prefer delegate), that actually carries out the functionally when the command is triggered. As we saw in the previous section, the On method name is the first argument that is passed to the delegate command's constructor when it is instantiated. Listing 42 shows an example of the On method.
Listing 42: A Prism 4 Delegate Command On Method:
private void OnCalculateButtonClick() { TextBoxTotalText = SubtractIntegers.SubtractIntegers(IntegerStrings); if (IntegerStrings != null && TextBoxTotalText != null) { FormulaTextboxText = SubtractIntegers.GetFormula(IntegerStrings, TextBoxTotalText); } } |
The code in Listing 42 is actuated when the Calculate button is clicked. We'll look at the XAML needed to trigger this command later in this chapter.
The Can method is an optional method that is used to set the enabled state of the associated command. This is the second (optional - overloaded) argument passed to the constructor of the delegate command. Our solution does not use this method, but it is a useful method when you want to add code to the command to change its enabled state. Listing 43 shows an example of what this method would look like if used with the Calculate button click code.
Listing 43: A Prism 4 Delegate Command Can Method:
private bool CanCalculateButtonClick() { Return true; } |
You can of course add code to the method to determine if the returned Boolean value is true or false. This method is not used in our view model because the UI uses state based navigation to set the different states.
It is also necessary to change the code in the constructor for the command if this method is used. Listing 44 shows an example.
Listing 44: A Prism 4 Delegate Command Instantiation with Can method:
CALCULATE_BUTTON_CLICK_COMMAND = new DelegateCommand(OnCalculateButtonClick, CanCalculateButtonClick); |
Listing 44 simply overloads the delegate command constructor with both the On and Can methods.
Note: I use On and Can prefixs to describe these methods because this is the accepted method of naming these methods. It is not strictly necessary to use these conventions when naming command methods.
Triggering Commands from the View:
Now that we know how to setup delegate commands in the view model, how do we trigger these commands from the view?
The Virtual Calculator solution triggers commands by using WPF interaction triggers. Interaction triggers identify the event for a particular control that will serve as the trigger and then uses the invoke command action tag to identify the command to invoke. Listing 45 shows and example of the CalculateButton XAML markup with an interaction trigger.
Listing 45: The Calculate Button XAML Markup:
<Button Name="CalculateButton" Content="Calculate" Grid.Row="0" Grid.Column="2" Grid.RowSpan="4" FontWeight="Bold" Foreground="DarkBlue" FontSize="16" Margin="5,0" Padding="10" IsDefault="True"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding CalculateButtonClickCommand}"> </i:InvokeCommandAction> </i:EventTrigger>
</i:Interaction.Triggers> </Button> |
First an Interaction.Triggers tag is added to the CalculateButton control. This allows for the addition of multiple Event Triggers if necessary. Next an event trigger is added and its EventName is set to Click. This name corresponds to the button's Click event. An Invoke Command Action is added to the Event Trigger. The command property of this tag is bound to the CalculateButtonClickCommand view model property.
When the Calculate button is clicked, the CalculateButtonClickCommand is triggered. This action executes the code in the OnCalculateButtonClick method.
Triggering a Prism 4 Command with Parameters:
So far we've looked at how to setup commands that operate without parameters. The delegate command class can be used as a generic class that takes a payload from the view. This of course allows for the passing of properties or entities that correspond to the current state of the associated control or the current state other controls in the view.
Passing parameters to the view model can be used when determining the item selected in a combobox or listbox. It can also be used to pass objects to the view model when needed.
The Virtual Calculator solution uses this technique to pass the textbox text property to the view model when the text changed event in either one of the two textboxes is triggered. Passing parameters to the view model means that modifications need to be made to command object, enabling it to take the parameter. Listing 46 shows an example of the Integer1TextBox command property.
Listing 46: The Integer1TextBox Command Property:
private ICommand TEXT_BOX_ONE_TEXT_CHANGED_COMMAND; public ICommand TextBoxOneTextChangedCommand { get { return TEXT_BOX_ONE_TEXT_CHANGED_COMMAND; } set { if (TEXT_BOX_ONE_TEXT_CHANGED_COMMAND != value) { TEXT_BOX_ONE_TEXT_CHANGED_COMMAND = value; NotifyPropertyChanged("TextBoxOneTextChangedCommand"); } } } |
The property for the TextBoxOneTextChangedCommand is no different from any other Prism 4 command, we'll start to see changes when we look at the command instantiation code in the constructor. Listing 47 shows an example of this code.
Listing 47: A Prism 4 Delegate Command Instantiation With Parameter:
TEXT_BOX_ONE_TEXT_CHANGED_COMMAND = new DelegateCommand<string>(OnTextBoxOneTextChanged); |
The thing to that we are concerned with in Listing 47, is that we are now passing a payload to the generic class of type string. This tells the class to expect a string value each time that the command is triggered.
The next change that we see is in the OnTextboxOneTextChanged method. Listing 48 shows an example of this code.
Listing 48: The OnTextBoxOneTextChanged Method:
private void OnTextBoxOneTextChanged(string Textbox1Text) { if (ClearFlag == false) { TextBoxOneText = Textbox1Text; }
ResetUserInterface(); IntegerStrings.Clear(); IntegerStrings.Add(TextBoxOneText); IntegerStrings.Add(TextBoxTwoText); IntegerTotal = TextBoxTotalText; CurrentUIState = UIModel.TextBox1HasValueState(IntegerStrings, IntegerTotal); StatusTextboxText = CurrentUIState.StatusText; FormulaTextboxText = CurrentUIState.FormulaText;
TextBox1Exception = CurrentUIState.TextBox1ExceptionState; TextBox2Exception = CurrentUIState.TextBox2ExceptionState; TextBox1HasValue = CurrentUIState.TextBox1HasValueState; TextBox1AndTextbox2HasValue = CurrentUIState.TextBox1AndTextBox2HasValueState; BothTextBoxesBlankOrClear = CurrentUIState.BothTextBoxesBlankOrClearState;
} |
The main change that interests us at this point is the fact that the method is passed an argument (Textbox1Text) with a type of string. Each time that a character is entered into the textbox the text that is currently in the textbox is passed from the view to the view model.
We'll next look at the XAML trigger code to see how parameters are passed. Listing 49 shows an example of the Integer1Textbox markup.
Listing 49: The Integer1TextBox XAML Markup:
<TextBox Name="Integer1TextBox" Grid.Row="0" Grid.Column="1" FontWeight="Bold" FontSize="14" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Margin="0,0,0,3" Text="{Binding Path=TextBoxOneText}"> <i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextBoxOneTextChangedCommand}" CommandParameter="{Binding ElementName=Integer1TextBox, Path=Text}"> </i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers> </TextBox> |
The trigger markup in Listing 49 is almost identical to the markup in Listing 45. The only exceptions are that the Event name is set to the TextChanged event, the Command property is set to the TextBoxOneTextChangedCommand property and that an extra property is added to the InvokeCommandAction tag. The CommandParameter property is used to pass a payload to the command object. This parameter is then passed as an argument to the command's On method and the Can method if it used.
In this case element binding is used to pass the text value of the Integer1Textbox to the view model command property.
We could have just as easily passed the textbox object by removing the path property and passing the Integer1Textbox element, as the parameter. Then we'd just need to change the type that the delegate object and the On method argument expects to textbox.
Prism 4 global commands are command classes that are visible to all of the other projects in the solution. These classes are one of the methods that is used for intra - module communications in Prism 4 solutions. The classes can be used in the same manner as the property based command classes that we spoke about earlier, with the added value of being visible between Prism 4 modules.
Prism 4 composite commands are used to allow a single parent command, to trigger multiple child commands. Composite commands work well in situations where it is necessary to call the same type of command across different modules. A good example is where it is necessary to implement "save all" functionally across a number of different modules. A single composite command can be used to trigger save commands in each of the associated modules.
I'm not going to talk in detail about global and composite commands because they are not used in the Virtual Calculator solution. But keep in mind that these two command types are also included in the Prism 4 framework.
In this chapter we looked at Prism 4 commanding. We learned the different parts of a command and were shown how to use them.
We also looked at WPF triggers. We learned how to integrate triggers with commands. We also worked with both types of triggers, Triggers without parameters and triggers with parameters.
In the next chapter we'll continue to look at Prism 4 communications. This time we'll work with event aggregation.