Thursday, 22 December 2016

Xamarin.Forms Behaviors: Revising InvokeMethodAction

Earlier in the year I blogged about using the EventHandlerBehavior, DataChangedBehavior, and InvokeMethodAction classes to invoke one or more methods when an event fires, or when data changes. The Behaviors Library for Xamarin.Forms has the notion of behaviors and actions. A behavior is attached to a control and listens for something to happen, such as an event firing. When the “something” happens, it triggers one or more actions, such as invoking a method or command. Actions are invoked by behaviors and executed on a selected control.

A number of people have contacted me over the year about difficulties in getting the InvokeMethodAction class to invoke their methods. In all cases it was possible to invoke their methods – it just required more of an understanding about using the class. Therefore, in this blog post I’ll explore the InvokeMethodAction class in more depth.

Invoking a Method when an Event Fires

The EventHandlerBehavior class listens for a specified event to occur, and executes one or more actions in response. It requires you to set an EventName property to the event that you want the behavior to listen to, and an Actions property to one or more actions that should be executed in response to the event firing.

The InvokeMethodAction class executes a specified method when invoked. It requires you to set a TargetObject property to an object that exposes the method of interest, and a MethodName property to the name of the method to be invoked. The InvokeMethodAction class allows the following methods to be called:

  • A public method with zero parameters.
  • A public event handler method with two parameters.

Each scenario will now be explored.

Calling a Public Method without Parameters

The first scenario the InvokeMethodAction class targets is calling a public method with zero parameters, as demonstrated in the following code example:

<StackLayout.BindingContext> <local:InvokeMethodDemoPageViewModel /> </StackLayout.BindingContext> ... <Button Text="Invoke ViewModel Method"> <Button.Behaviors> <behaviors:EventHandlerBehavior EventName="Clicked"> <behaviors:InvokeMethodAction TargetObject="{Binding}" MethodName="IncrementCounter" /> </behaviors:EventHandlerBehavior> </Button.Behaviors> </Button>

The IncrementCounter method is public and doesn’t take any parameters:

public void IncrementCounter () { Counter++; OnPropertyChanged ("Counter"); }

When the Button.Clicked event fires, the IncrementCounter method is executed. The TargetObject property value specifies that the BindingContext exposes the method of interest. Therefore, the InvokeMethodAction class will search the BindingContext of the attached control for the method, which in this case is the InvokeMethodDemoPageViewModel instance.

Calling a Public Method with Two Parameters

The second scenario the InvokeMethodAction class targets it calling a public method with two parameters. This scenario is designed to handle invoking event handler methods.

Example 1

The following code shows an example of using the EventHandlerBehavior and InvokeMethodAction classes to invoke a Button.Clicked event handler method:

<StackLayout BindingContext="{x:Reference page}"> ... <Button Text="Invoke Page Method"> <Button.Behaviors> <behaviors:EventHandlerBehavior EventName="Clicked"> <behaviors:InvokeMethodAction TargetObject="{Binding}" MethodName="OnButtonClicked" /> </behaviors:EventHandlerBehavior> </Button.Behaviors> </Button> </StackLayout>

The OnButtonClicked method is public and takes two parameters:

public void OnButtonClicked (object sender, EventArgs args) { Counter++; }

Note that the OnButtonClicked method must accept parameters of the correct type for the Clicked event of a Button. Then, when the Button.Clicked event fires, the OnButtonClicked method is executed (which has a signature of object sender, EventArgs args). The TargetObject property value specifies that the BindingContext exposes the method of interest. Therefore, the InvokeMethodAction class will search the BindingContext of the attached control for the method, which in this case is the ContentPage. Note that the Actions property of the EventHandlerBehavior is set indirectly by creating the InvokeMethodAction instance as a child of the EventHandlerBehavior instance.

Example 2

The following code shows an example of using the EventHandlerBehavior and InvokeMethodAction classes to invoke a Label.PropertyChanged event handler method:

<ContentPage ... x:Name="page"> ... <Label Text="{Binding Message}"> <Label.Behaviors> <behaviors:EventHandlerBehavior EventName="PropertyChanged"> <behaviors:InvokeMethodAction TargetObject="{Binding Source={x:Reference page}}" MethodName="OnMessagePropertyChanged" /> </behaviors:EventHandlerBehavior> </Label.Behaviors> </Label> </ContentPage>

The OnMessagePropertyChanged method is public and takes two parameters:

public void OnMessagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs args) { ... }
Note that the OnMessagePropertyChanged method must accept parameters of the correct type for the PropertyChanged event of a Label (technically the PropertyChanged event is defined in the BindableObject class, which the Label class ultimately inherits from). Then, when the Label.PropertyChanged event fires, the OnMessagePropertyChanged method is executed (which has a signature of object sender, PropertyChangedEventArgs args). The TargetObject property value specifies that the page class exposes the method of interest. Therefore, the InvokeMethodAction class will search the accompanying code-behind file for the OnMessagePropertyChanged method.

Invoking a Method when Data Changes

Previously, I’ve demonstrated using the DataChangedBehavior and InvokeMethodAction classes to invoke one or methods when data changes. Specifically, I demonstrated using the InvokeMethodAction class to invoke a public method with zero parameters.

The following code shows an example of using the DataChangedBehavior and InvokeMethodAction classes to invoke a method with two parameters:

<ContentPage ... x:Name="page"> ... <Label x:Name="doneLabel" Text="{Binding IsDone}"> <Label.Behaviors> <behaviors:DataChangedBehavior Binding="{Binding Source={x:Reference doneLabel}, Path=Text}" ComparisonCondition="Equal" Value="True"> <behaviors:InvokeMethodAction TargetObject="{Binding Source={x:Reference page}" MethodName="OnIsDoneChanged" /> </behaviors:DataChangedBehavior> </Label.Behaviors> </Label> </ContentPage>

The OnIsDoneChanged method is public and takes two parameters:

public void OnIsDoneChanged(object sender, string value) { ... }

Note that the OnIsDoneChanged method resembles an event handler, with the second parameter being of the type of the bound data (specified by the Binding property value of the DataChangedBehavior – in this case the string value of the bound Label.Text property). When the value of the Label.Text property becomes equal to “True”, the OnIsDoneChanged method is executed. The TargetObject property value specifies that the page class exposes the method of interest. Therefore, the InvokeMethodAction class will search the accompanying code-behind file for the OnIsDoneChanged method.

Summary

The InvokeMethodAction class executes a specified method in response to an event firing, or an item of data changing. Methods invoked by the class must be public and either have zero parameters, or two parameters. When methods have two parameters, their parameters must match the event being fired, or the type of the data that’s changed.

No comments:

Post a Comment