Friday, 23 June 2017

Making a POST request to IdentityServer's token endpoint from a Xamarin client

I recently had to implement a hybrid authorisation flow from a Xamarin client to IdentityServer 4. This involved making a browser request to IdentityServer’s authorize endpoint to retrieve an authorisation code, and then making a REST request to IdentityServer’s token endpoint, exchanging the authorisation code for an access token.

However, I found the documentation on the token endpoint to be slightly lacking. It states that the token endpoint requires a POST request, along with a series of URL query parameters:
POST /connect/token client_id=client1& client_secret=secret& grant_type=authorization_code& code=hdh922& redirect_uri=https://myapp.com/callback
Every attempt at making this request failed. On top of that I wasn’t keen on sending a client secret potentially in the clear. After some digging through the IdentityServer code I was able to construct a POST request that worked for me, which I verified using Advanced REST Client. My POST request was:
POST /connect/token grant_type=authorization_code& code=hdh922& redirect_uri=https://myapp.com/callback
The value of the code parameter is the authorisation code retrieved from IdentityServer’s authorize endpoint, and the value of the redirect_uri parameter is the callback URL registered with IdentityServer. Note that this isn’t the only way of making the POST request to the token endpoint, as IdentityServer permits several variations.

So what’s happened to the client_id and client_secret query parameters listed in the first POST request? While IdentityServer can accept them as query parameters, I was unhappy about sending my client secret as a query parameter. Luckily, IdentityServer also permits both client_id and client_secret to be sent in the Authorization header of the POST request, encoded as a basic authentication value, which is what I did. For more information about basic authentication, see Specifying Basic Authentication in a Web Request.

Translating this into code produces the GetTokenAsync method, which makes the request using the PostAsync method of the RequestProvider class:
public async Task<UserToken> GetTokenAsync(string code) { string data = string.Format("grant_type=authorization_code&code={0}&redirect_uri={1}", code, WebUtility.UrlEncode(GlobalSetting.Instance.IdentityCallback)); var token = await _requestProvider.PostAsync<UserToken>(GlobalSetting.Instance.TokenEndpoint, data, GlobalSetting.Instance.ClientId, GlobalSetting.Instance.ClientSecret); return token; }
The PostAsync method in the RequestProvider class is shown in the following code example:
public async Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret) { HttpClient httpClient = CreateHttpClient(string.Empty); if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret)) { AddBasicAuthenticationHeader(httpClient, clientId, clientSecret); } var content = new StringContent(data); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); HttpResponseMessage response = await httpClient.PostAsync(uri, content); await HandleResponse(response); string serialized = await response.Content.ReadAsStringAsync(); TResult result = await Task.Run(() => JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings)); return result; }
After creating the HttpClient instance, the PostAsync method calls the AddBasicAuthenticationHeader method, which is shown in the following code example:
private void AddBasicAuthenticationHeader(HttpClient httpClient, string clientId, string clientSecret) { if (httpClient == null) return; if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret)) return; httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(clientId, clientSecret); }
The AddBasicAuthenticationHeader method adds the Authorization header to the POST request, with its value being the clientId and clientSecret values encoded using basic authentication.

Back in the PostAsync method, the other piece of the puzzle that was missing from the IdentityServer documentation is that the ContentType of the POST request must be set to application/x-www-form-urlencoded. I suspect this is the missing piece of the puzzle that caused the original request to fail.

To see the full application from which I’ve taken this code, along with more content about using IdentityServer from a Xamarin client, see Enterprise Application Patterns using Xamarin.Forms.

Monday, 5 June 2017

Enterprise Application Patterns using Xamarin.Forms

For the last few months I've been working on a Xamarin.Forms guide, about building cross-platform enterprise apps. The first edition of the guide is now published as an eBook, and can be downloaded here, or by clicking the image below.


What's it all about?

The eBook provides guidance on building cross-platform enterprise apps using Xamarin.Forms. It focuses on providing architectural guidance for developing adaptable, maintainable, and testable Xamarin.Forms enterprise apps. Guidance is provided on how to implement MVVM, dependency injection, navigation, validation, and configuration management, while maintaining loose coupling. In addition, there's also guidance on performing authentication and authorization with IdentityServer, accessing data from containerized microservices, and unit testing.

The guide comes with source code for the eShopOnContainers mobile app, and source code for the eShopOnContainers reference app. The eShopOnContainers mobile app is a cross-platform enterprise app developed using Xamarin.Forms, which connects to a series of containerized microservices known as the eShopOnContainers reference app.

Who's it intended for?

The guide is aimed at readers who are already familiar with Xamarin.Forms. For a detailed introduction to Xamarin.Forms, see the Xamarin.Forms documentation on the Xamarin Developer Center, and Creating Mobile Apps with Xamarin.Forms.

The guide is complementary to .NET Microservices: Architecture for Containerized .NET Applications, which focuses on developing and deploying containerized microservices.

What's in the sample application?

The guide includes a sample application, eShopOnContainers, that's an online store that includes the following functionality:
  • Authenticating and authorizing against a backend service.
  • Browsing a catalog of shirts, coffee mugs, and other marketing items.
  • Filtering the catalog.
  • Ordering items from the catalog.
  • Viewing the user's order history.
  • Configuration of settings.
The following diagram provides a high-level overview of the architecture of the sample application:
The sample application ships with three client apps:
  • An MVC application developed with ASP.NET Core.
  • A Single Page Application (SPA) developed with Angular 2 and Typescript. This approach for web applications avoids performing a round-trip to the server with each operation.
  • A mobile app developed with Xamarin.Forms, which supports iOS, Android, and the Universal Windows Platform (UWP).
For information about the web applications, see Architecting and Developing Modern Web Applications with ASP.NET Core and Microsoft Azure.

The sample application includes the following backend services:
  • An identity microservice, which uses ASP.NET Core Identity and IdentityServer.
  • A catalog microservice, which is a data-driven create, read, update, delete (CRUD) service that consumes an SQL Server database using EntityFramework Core.
  • An ordering microservice, which is a domain-driven service that uses domain-driven design patterns.
  •  A basket microservice, which is a data-driven CRUD service that uses Redis Cache.
These backend services are implemented as microservices using ASP.NET Core MVC, and are deployed as unique containers within a single Docker host. Collectively, these backend services are referred to as the eShopOnContainers reference application. Client apps communicate with the backend services through a Representational State Transfer (REST) web interface. For information about the implementation of the backend services, see .NET Microservices: Architecture for Containerized .NET Applications.

What's the mobile app?

The guide focuses on building cross-platform enterprise apps using Xamarin.Forms, and uses the eShopOnContainers mobile app as an example. The following screenshots show the pages from the eShopOnContainers mobile app that provide the functionality outlined earlier:


The mobile app consumes the backend services provided by the eShopOnContainers reference application. However, it can be configured to consume data from mock services for those who wish to avoid deploying the backend services.

The eShopOnContainers mobile app exercises the following Xamarin.Forms functionality:
  • XAML
  • Controls
  • Bindings
  • Converters
  • Styles
  • Animations
  • Commands
  • Behaviors
  • Triggers
  • Effects
  • Custom Renderers
  • MessagingCenter
  • Custom Controls
In addition, unit tests are provided for some of the classes in the eShopOnContainers mobile app. For more information about this functionality, see the Xamarin.Forms documentation on the Xamarin Developer Center, and Creating Mobile Apps with Xamarin.Forms.

Where can I get help and provide feedback?

This project has a community site, on which you can post questions, and provide feedback. The community site is located at https://github.com/dotnet-architecture/eShopOnContainers. Alternatively, feedback about the eBook can be emailed to dotnet-architecture-ebooks-feedback@service.microsoft.com.

Friday, 24 March 2017

Validating User Input in Xamarin.Forms IV

Previously I discussed how validation can be automatically triggered when properties changes, or manually triggered to validate all properties. In this blog post I’m going to explore how to notify the user about any validation errors that have occurred.

Displaying Validation Errors

Validation errors are shown to the user by highlighting the control that contains invalid data, and by displaying an error message beneath the control, as shown in the following screenshot:

Validation errors

Highlighting a Control that contains Invalid Data

The Validation attached behaviour class is used to highlight controls where validation errors occur. The following code example shows how the Validation behaviour is attached to an Entry control:

<Entry Placeholder="Enter forename here" Text="{Binding User.Forename, Mode=TwoWay}" local:Validation.Errors="{Binding User.Errors[Forename]}" />

The Validation attached behaviour gets and sets the Errors bindable property, and in this example binds it to the Errors property of the User model class that’s exposed by the associated view model. The User.Errors property is provided by the ValidatableBase class, from which all model classes derive, and is an instance of the Validator class. The indexer of the Validator class returns a ReadOnlyCollection of error strings, to retrieve any validation errors for the Forename property. The Validation attached behavior class is shown in the following code example:

public static class Validation { public static readonly BindableProperty ErrorsProperty = BindableProperty.CreateAttached( "Errors", typeof(ReadOnlyCollection<string>), typeof(Validation), Validator.EmptyErrorsCollection, propertyChanged: OnPropertyErrorsChanged); public static ReadOnlyCollection<string> GetErrors(BindableObject element) { return (ReadOnlyCollection<string>)element.GetValue(ErrorsProperty); } public static void SetErrors(BindableObject element, ReadOnlyCollection<string> value) { element.SetValue(ErrorsProperty, value); } static void OnPropertyErrorsChanged(BindableObject element, object oldValue, object newValue) { var view = element as View; if (view == null | oldValue == null || newValue == null) { return; } var propertyErrors = (ReadOnlyCollection<string>)newValue; if (propertyErrors.Any()) { view.Effects.Add(new BorderEffect()); } else { var effectToRemove = view.Effects.FirstOrDefault(e => e is BorderEffect); if (effectToRemove != null) { view.Effects.Remove(effectToRemove); } } } }

The Errors attached property is registered as a ReadOnlyCollection of strings, by the BindableProperty.CreateAttached method. When the value of the Errors bindable property changes, the OnPropertyErrorsChanged method is invoked. The parameters for this method provide the instance of the control that the Errors bindable property is attached to, and any validation errors for the control. The BorderEffect class is added to the control’s Effects collection if validation errors are present, otherwise it’s removed from the control’s Effects collection.

The BorderEffect class subclasses the RoutingEffect class, and is shown in the following code example:

class BorderEffect : RoutingEffect { public BorderEffect() : base("Xamarin.BorderEffect") { } }

The RoutingEffect class represents a platform-independent effect that wraps an inner effect that’s usually platform-specific. This simplifies the effect removal process, since there is no compile-time access to the type information for a platform-specific effect. The BorderEffect calls the base class constructor, passing in a parameter consisting of a concatenation of the resolution group name, and the unique ID that’s specified on each platform-specific effect class.

The following code example shows the Xamarin.BorderEffect implementation for iOS:

[assembly: ResolutionGroupName("Xamarin")] [assembly: ExportEffect(typeof(BorderEffect), "BorderEffect")] namespace MVVMUtopia.iOS { public class BorderEffect : PlatformEffect { protected override void OnAttached() { try { Control.Layer.BorderColor = UIColor.Red.CGColor; Control.Layer.BorderWidth = 1; } catch (Exception ex) { Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message); } } protected override void OnDetached() { try { Control.Layer.BorderWidth = 0; } catch (Exception ex) { Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message); } } } }

The OnAttached method sets Control.Layer properties to values to create the red border, and the OnDetached method removes the red border. This functionality is wrapped in a try/catch block in case the control that the effect is attached to does not have the required Control.Layer properties.

For more information about effects, see Effects.

Displaying an Error Message

Each control that undergoes validation also has a Label beneath it, which is used to display the error message associated with the control. The following code example shows the Label displayed underneath the Entry that accepts a user’s forename:

<Label Text="{Binding User.Errors[Forename], Converter={StaticResource FirstErrorConverter}" />

The Label binds its Text property to the Errors property of the User model class that’s exposed by the associated view model. The User.Errors property is provided by the ValidatableBase class, from which all model classes derive, and is an instance of the Validator class. The indexer of the Validator class returns a ReadOnlyCollection of error strings, with the FirstErrorConverter retrieving the first error from the collection, for display.

Summary

Any mobile app that accepts user input should ensure that the input is valid. This could involve, for example, checking that input contains only characters in a particular range, or is of a certain length. Without validation, a user can supply data that can cause the app to fail.

In the context of MVVM, user input can be synchronously validated client-side in view model objects or in model objects. However, validating data in view models often means duplicating model properties. Instead, view models can delegate validation to the model objects they contain, with validation then being performed on the model objects. Validation rules can be specified on the model properties by using data annotations that derive from the ValidationAttribute class.

To participate in validation, model classes must derive from the ValidatableBase class, which provides an error container whose contents are updated whenever a model class property value changes. The Validator and ValidatableBase classes both implement INotifyPropertyChanged in order to provide property change notification.

The SetProperty method in the ValidatableBase class performs validation when a model property is set to a new value. The validation rules come from data annotation attributes that derive from the ValidationAttribute class. The attributes are taken from the declaration of the model property being validated.

Users are notified of validation errors by highlighting the controls that contain the invalid data with red borders, and by displaying error messages that inform the user why the data is invalid.

The code can be downloaded from GitHub.

Thursday, 16 March 2017

Validating User Input in Xamarin.Forms III

Previously I discussed how validation rules are specified by adding data annotation attributes, that derive from the ValidationAttribute class, to properties in model classes that require validation. In this blog post I’m going to explore how validation is triggered, both automatically and manually.

Triggering Validation

This validation approach can automatically trigger validation when properties change, and manually trigger validation of all properties. Each will be discussed in turn.

Triggering Validation when Properties Change

Validation is automatically triggered when a bound property changes. When a two way binding in a view sets a bound property in a model class, that class should invoke the SetProperty method, provided by the BindableBase class, which sets the property values and raises the PropertyChanged event. However, the SetProperty method is also overridden by the ValidatableBase class. The SetProperty method in the ValidatableBae class calls the SetProperty method in the BindableBase class, and performs validation if the property has changed. The following code example shows how validation occurs after a property change:

public bool ValidateProperty(string propertyName) { if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentNullException("propertyName"); } var propertyInfo = entityToValidate.GetType().GetRuntimeProperty(propertyName); if (propertyInfo == null) { throw new ArgumentException("The entity does not contain a property with that name.", propertyName); } var propertyErrors = new List<string>(); bool isValid = TryValidateProperty(propertyInfo, propertyErrors); bool errorsChanged = SetPropertyErrors(propertyInfo.Name, propertyErrors); if (errorsChanged) { OnErrorsChanged(propertyName); OnPropertyChanged(string.Format(CultureInfo.CurrentCulture, "Item[{0}]", propertyName)); } return isValid; }

This method retrieves the property to be validated, and attempts to validate it by calling the TryValidateProperty method. If the validation results change, for example, when new validation errors are found or when previous errors have been corrected, then the ErrorsChanged and PropertyChanged events are raised for the property. The following code example shows the TryValidateProperty method:

bool TryValidateProperty(PropertyInfo propertyInfo, List<string> propertyErrors) { var results = new List<ValidationResult>(); var context = new ValidationContext(entityToValidate) { MemberName = propertyInfo.Name }; var propertyValue = propertyInfo.GetValue(entityToValidate); bool isValid = System.ComponentModel.DataAnnotations.Validator.TryValidateProperty(propertyValue, context, results); if (results.Any()) { propertyErrors.AddRange(results.Select(c => c.ErrorMessage)); } return isValid; }

This method calls the TryValidateProperty method from the Validator class, to validate the property value against the validation rules for the property. Any validation errors are added to a list of errors.

Trigger Validation Manually

Validation can be triggered manually for all properties of a model object. For example, in the sample application when the user clicks the Navigate button on the FirstPage. The button’s command delegate calls the NavigateAsync method, which is shown in the following code example:

async Task NavigateAsync() { if (user.ValidateProperties()) { await navigationService.NavigateAsync("SecondPage"); } }

This method calls the ValidateProperties method of the ValidatableBase class (because the User model class derives from the ValidatableBase class), which in turn calls the ValidateProperties method of the Validator class. Providing that validation succeeds, page navigation occurs, otherwise validation errors are displayed. The following code example shows the ValidateProperties method from the Validator class:

public bool ValidateProperties() { var propertiesWithChangedErrors = new List<string>(); var propertiesToValidate = entityToValidate.GetType() .GetRuntimeProperties() .Where(c => c.GetCustomAttributes(typeof(ValidationAttribute)).Any()); foreach (PropertyInfo propertyInfo in propertiesToValidate) { var propertyErrors = new List<string>(); TryValidateProperty(propertyInfo, propertyErrors); bool errorsChanged = SetPropertyErrors(propertyInfo.Name, propertyErrors); if (errorsChanged && !propertiesWithChangedErrors.Contains(propertyInfo.Name)) { propertiesWithChangedErrors.Add(propertyInfo.Name); } } foreach (string propertyName in propertiesWithChangedErrors) { OnErrorsChanged(propertyName); OnPropertyChanged(string.Format(CultureInfo.CurrentCulture, "Item[{0}]", propertyName)); } return errors.Values.Count == 0; }

This method retrieves any properties that have attributes that derive from the ValidationAttribute data annotation, and attempts to validate them by calling the TryValidateProperty method for each property. If the validation state changes, the ErrorsChanged and PropertyChanged events are raised for each property whose errors have changed. Changes occur when new errors are seen or when previously detected errors are no longer present.

Users are notified of validation errors by highlighting the controls that contain the invalid data with red borders, and by displaying error messages that inform the user why the data is invalid. In my next blog post I’ll explore this topic further. In the meantime, the code can be downloaded from GitHub.

Wednesday, 8 March 2017

Validating User Input in Xamarin.Forms II

Previously I introduced a high-level overview of an approach to validating user input in Xamarin.Forms. This approach involves specifying validation rules on model objects, invoking validation when a model property values changes, and then displaying any error messages. In this blog post I’m going to explore how to specify validation rules on model objects.

Specifying Validation Rules

Validation rules are specified by adding data annotation attributes, that derive from the ValidationAttribute class, to properties in model classes that require validation.

The following code example shows the User class, where the properties are decorated with validation attributes:

public class User : ValidatableBase { string forename, surname; const string NAMESREGEXPATTERN = @"\A\p{L}+([\p{Zs}\-][\p{L}]+)*\z"; [Required(ErrorMessage = "This field is required.")] [RegularExpression(NAMESREGEXPATTERN, ErrorMessage = "This field contains invalid characters.")] [StringLength(10, MinimumLength = 2, ErrorMessage = "This field requires a minimum of 2 characters and a maximum of 10.")] public string Forename { get { return forename; } set { SetProperty(ref forename, value); } } [Required(ErrorMessage = "This field is required.")] [RegularExpression(NAMESREGEXPATTERN, ErrorMessage = "This field contains invalid characters.")] [StringLength(15, MinimumLength = 2, ErrorMessage = "This field requires a minimum of 2 characters and a maximum of 15.")] public string Surname { get { return surname; } set { SetProperty(ref surname, value); } } }

The Required attribute specifies that a validation failure occurs if the field is null, contains an empty string, or contains only white-space characters. The RegularExpression attribute specifies that a property must match the regular expression given by the NAMESREGEXPATTERN constant. This regular expression allows user input to consist of all unicode name characters as well as spaces and hyphens, as long as the spaces and hyphens don’t occur in sequences and are not leading or trailing characters. The StringLength attribute specifies the minimum and maximum length of characters that are allowed. Each attribute also sets an ErrorMessage property, which stores the error message to be displayed if validation fails.

It’s worth browsing the DataAnnotations namespace in order to discover all the different validation attributes. As well as the general attributes mentioned above, it contains specific attributes capable of validating email addresses, credit card numbers, and phone numbers. In addition, it also provides a CustomValidation attribute, which specifies an application-provided method to be invoked to validate the property whenever a value is assigned to it.

To participate in validation a model class must derive from the ValidatableBase class, so that model property values will be validated when they change. In my next blog post I’ll explore this topic further. In the meantime, the code can be downloaded from GitHub.

Friday, 3 March 2017

Validating User Input in Xamarin.Forms

Any mobile app that accepts user input should ensure that the input is valid. This could involve, for example, checking that input contains only characters in a particular range, or is of a certain length. Without validation, a user can supply data that can cause the app to fail.

In the context of MVVM, user input can be synchronously validated client-side in view model objects or in model objects. However, validating data in view models often means duplicating model properties. Instead, view models can delegate validation to the model objects they contain, with validation then being performed on the model objects. Validation rules can be specified on the model properties by using data annotations that derive from the ValidationAttribute class.

I’ve created a validation sample implements validation using this approach, which can be found on GitHub. The following diagram shows a high-level overview of the classes involved in validation:

Validation classes

To participate in validation, model classes must derive from the ValidatableBase class, which provides an error container whose contents are updated whenever a model class property value changes. The Validator and ValidatableBase classes both implement INotifyPropertyChanged in order to provide property change notification.

The SetProperty method in the ValidatableBase class performs validation when a model property is set to a new value. The validation rules come from data annotation attributes that derive from the ValidationAttribute class. The attributes are taken from the declaration of the model property being validated.

Users are notified of validation errors by highlighting the controls that contain the invalid data with red borders, and by displaying error messages that inform the user why the data is invalid, as shown in the following screenshot:

Validation errors

Over the coming weeks I’m going to explore this validation approach in a series of blog posts:

  1. Specifying validation rules.
  2. Triggering validation.
  3. Displaying validation errors.

In the meantime, the code can be downloaded from GitHub.

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.