Archive

Archive for the ‘MVVM’ Category

Clear history in Xamarin Android with MVVM Light NavigationService

14/03/2016 Comments off

If we’re working with Xamarin, MVVM Light provides quite useful helpers (derived from the Windows implementation) that simplify a lot our work. Among the others, we have a INavigationService class that abstracts the concept of starting a new Activity (Android) rather than activating a certain Controller (iOS), providing a unique approach to page navigation.

This approach is general and the interface is the same on all the platforms. While this helps having a common navigation system, it can leads to platform specific problem. Imagine we are on Android and we need to delete history (for example, after a login). On this platform, navigation history can be deleted only in conjunction with the start of an Activity (i.e., we need to tell Android to delete any existing task before starting the Activity). But if we’re using INavigationService, we have a NavigateTo method to which we can pass only the pageKey and an optional object that will be received by the new activity. We haven’t control on how the new activity is started. So, how can we delete history?

We can tweak the original NavigationService implementation for Android to handle this scenario. First of all, we need to define a NavigationParameter class:

public class NavigationParameter
{
    public ActivityFlags? Flags { get; }

    public object Content { get; }

    public NavigationParameter(ActivityFlags? flags = null)
    {
        Flags = flags;
    }

    public NavigationParameter(object content, ActivityFlags? flags = null)
    {
        Content = content;
        Flags = flags;
    }
}

It allows us to pass custom activation flags rather the a single object to the NavigateTo method. To use this class, a little modification to NavigationService.cs file is requested. Let’s get the original implementation from CodePlex. Then, locate the following code in the NavigateTo(string pageKey, object parameter) method:

if (parameter != null)
{
    lock (_parametersByKey)
    {
        // ...
    }
}

We need to replace it with the following code:

if (parameter != null)
{
    lock (_parametersByKey)
    {
        var guid = Guid.NewGuid().ToString();

        var flags = (parameter as NavigationParameter)?.Flags;
        var actualParameter = (parameter as NavigationParameter)?.Content 
            ?? parameter;

        _parametersByKey.Add(guid, actualParameter);
        intent.PutExtra(ParameterKeyName, guid);

        if (flags.HasValue)
            intent.SetFlags(flags.Value);
    }
}

The core of the change is at lines 7-9. We check if parameter is a NavigationParameter: in this case, we extract the activation flags and the actual parameter; otherwise, we assume we don’t have any activation flag and that the actual parameter is the parameter argument itself. So, we can continue to use the NavigateTo method in the usual way (no breaking changes). Then, at lines 14-15, if flags has value, we pass it to the Intent.SetFlags method.

So, when we need to clear history, we just need to create a NavigationParameter with the correct activation flags, like in the following example:

private NavigationParameter CreateParameter<T>(T parameter, bool clearHistory)
{
    var flags = clearHistory ? (ActivityFlags.NewTask | ActivityFlags.ClearTask) 
        : default(ActivityFlags);
    var args = new NavigationParameter(parameter, flags);

    return args;
}
        
private void NavigateToHomePage<T>(T parameter)
{
    var args = this.CreateParameter(parameter, clearHistory: true);
    navigationService.NavigateTo(Constants.HomePage, args);
}

Note that, as we use NavigationParamenter only to pass activation flags and the new activity receives always the actual parameter, the GetAndRemoveParameter(Intent) method will keep working in the usual way. Moreover, as already said, we can continue to use NavigateTo method as before if we don’t need to clear history.

Advertisements
Categories: C#, MVVM, Xamarin

Using MVVM Light ViewModelBase class with Template 10

25/01/2016 1 comment

Template10 comes with a handful ViewModelBase class that provides an easy way to handle events such as OnNavigatedTo, OnNavigatingFrom, access the Navigation Service from View Models and so on. In the previous posts we have seen how to integrate this class with MVVM Light, but what if we already have View Models that inherit from MVVM Light ViewModelBase, or as usually happens we have our ViewModelBase class, that in turns inherits from MVVM Light one?

If our View Models directly inherit from MVVM Light ViewModelBase class, we simply need to implement the Template 10 INavigable interface:

public class MainViewModel : GalaSoft.MvvmLight.ViewModelBase,
    Template10.Services.NavigationService.INavigable
{
    public Task OnNavigatedToAsync(object parameter, NavigationMode mode, 
        IDictionary<string, object> state)
    {
        // ...
        return Task.CompletedTask;
    }

    public Task OnNavigatedFromAsync(IDictionary<string, object> state, 
        bool suspending)
    {
        // ...
        return Task.CompletedTask;
    }

    public Task OnNavigatingFromAsync(NavigatingEventArgs args)
    {
        // ...
        return Task.CompletedTask;
    }

    [JsonIgnore]
    public IDispatcherWrapper Dispatcher { get; set; }

    [JsonIgnore]
    public INavigationService NavigationService { get; set; }

    [JsonIgnore]
    public IStateItems SessionState { get; set; }
}

In this case the recommended approach is of course to create a base class that inherits from MVVM Light ViewModelBase class and implements the Template 10 INavigable interface:

public abstract class MyViewModelBase : GalaSoft.MvvmLight.ViewModelBase,
    Template10.Services.NavigationService.INavigable
{
    public virtual Task OnNavigatedToAsync(object parameter, 
        NavigationMode mode, IDictionary<string, object> state) 
        => Task.CompletedTask;

    public virtual Task OnNavigatedFromAsync(IDictionary<string, object> state,
        bool suspending) 
        => Task.CompletedTask;

    public virtual Task OnNavigatingFromAsync(NavigatingEventArgs args)
        => Task.CompletedTask;

    [JsonIgnore]
    public IDispatcherWrapper Dispatcher { get; set; }

    [JsonIgnore]
    public INavigationService NavigationService { get; set; }

    [JsonIgnore]
    public IStateItems SessionState { get; set; }
}

As said before, if we already have our custom ViewModelBase class, we just need to make it implement the INavigable interface in the same way. In any cases, after defining this class, we can use it as usual:

public class MainViewModel : MyViewModelBase
{
    private string name;
    public string Name
    {
        get { return name; }
        set { base.Set(ref name, value, broadcast: true); }
    }

    public override Task OnNavigatedToAsync(object parameter,
        NavigationMode mode, IDictionary<string, object> state)
    {
        var argument = parameter?.ToString();

        if (state.Any())
        {
            //...

            state.Clear();

        }

        // Calls a method from MVVM Light ViewModelBase class.
        base.Broadcast(null, "Marco", nameof(Name));

        return base.OnNavigatedToAsync(parameter, mode, state);
    }
}

We can now access all the features provided by either MVVM Light and Template 10 without further code refactoring.

Using MVVM Light Messenger with Template 10

30/11/2015 1 comment

In previous posts we shown how to integrate MVVM Light with Template 10. We still need to talk about an important component of the toolkit, the Messenger. It allows for communication within the application, i.e. between View Models, Views and View Models, and so on.

We can access the Messenger from anywhere in our code, using its static Messenger.Default property. So, for example, we can easily integrate it in a View Model even if it inherits from the Template 10 ViewModelBase class (instead of the MVVM Light base class).

The first thing we can do with the Messenger is sending object, i.e. to execute code in the View Model in response to View event handlers:

private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
    // Sends a message with a Person object.
    var person = e.ClickedItem as Person;
    Messenger.Default.Send(person);
}

// In the View Model...
// Registers for incoming Person messages.
Messenger.Default.Register<Person>(this, (p) =>
{
    // Works with the Person object.
});

MVVM Light provides also a built-in class, NotificationMessage, that we can use if we just want to send string messages:

// Sends a notification message with a string content.
Messenger.Default.Send(new NotificationMessage("CloseApp"));

// Registers for incoming Notification messages.
Messenger.Default.Register<NotificationMessage>(this, (message) =>
{
    // Checks the actual notification.
    switch (message.Notification)
    {
        case "CloseApp":
            break;

        default:
            break;
    }
});

At lines 8-15, we check the Notification property to determine the specific message that has been sent. Of course, we can use constants to avoid the repetition of string values.

We can combine both of these scenarios and use the generic version of NotificationMessage to send an object along with a string message, which for example specifies the action that we want to perform:

private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
    // Sends a message with a Person object.
    var person = e.ClickedItem as Person;

    // Sends a notification message with a Person as content.
    Messenger.Default.Send(new NotificationMessage<Person>(person, "Select"));
    //Messenger.Default.Send(new NotificationMessage<Person>(person, "Update"));
}

// In the View Model...
// Registers for incoming Notification messages.
Messenger.Default.Register<NotificationMessage<Person>>(this, (message) =>
{
    // Gets the Person object.
    var person = message.Content;

    // Checks the associated action.
    switch (message.Notification)
    {
        case "Select":
            break;

        case "Update":
            break;

        default:
            break;
    }
});

The generic version of NotificationMessage exposes a Content property (line 16) that contains the actual object. So we can retrieve it and use the Notification value to determine which action to execute (lines 19-29).

In some cases, we would need to execute an action back on the sender when the message is received. For these scenarios, MVVM Light provides the NotificationMessageAction class, which allows to specify a callback method that can be executed by the recipient to notify the sender that the message has been processed. With this object, however, we can only send a notification string message. So what about if we want to send also a content class, like in the generic NotificationMessage?

First of all, we need to define a class that inherits from the standard NotificationMessageAction class:

public class NotificationMessageAction<T, TCallbackParameter> 
    : NotificationMessageAction<TCallbackParameter>
{
    public T Content { get; }

    public NotificationMessageAction(T content, string notification, 
        Action<TCallbackParameter> callback)
      : base(notification, callback)
    {
        Content = content;
    }
}

Basically, we have added the generic Content property (line 4). Then, we can write something like this:

private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
    // Sends a message with a Person object.
    var person = e.ClickedItem as Person;

    // Sends a notification message with a Person as content, 
    // a string message and a callback to be executed at the end.
    Messenger.Default.Send(new NotificationMessageAction<Person, int>(person, 
        "Update", (response) => 
    {
        // "response" contains the response (int in this example) from the
        // receiver.
    }));
}

// In the View Model...
// Registers for incoming NotificationMessageAction messages.
Messenger.Default.Register<NotificationMessageAction<Person, int>>(this, 
    (message) =>
{
    // Gets the Person object.
    var person = message.Content;

    // Checks the associated action.
    switch (message.Notification)
    {
        case "Update":
            break;

        default:
            break;
    }

    // Executes the callback.
    message.Execute(42);
});

At lines 8-13 we send our NotificationMessageAction message, specifying that Content is a Person and the callback parameter is int. Then, in the View Model we process the message as we did in the previous example (lines 21-32) and finally we execute the callback (line 35): this instruction will cause the action defined at lines 10-13 to be executed.

If we use an IoC Container for View Models (like MVVM Light SimpleIoC), we must remember that Messenger registrations remain active even if we change View (because View Models actually stay in memory), so they always receive notifications. Instead, we usually want that a View Model receives notification only if its corresponding View is active.

To overcome this problem, we simply can register messages in the Template 10 OnNavigatedTo method and unregister them in OnNavigatedFromAsync:

public override void OnNavigatedTo(object parameter, NavigationMode mode,
    IDictionary<string, object> state)
{
    // Registers for incoming Person messages.
    Messenger.Default.Register<Person>(this, (p) =>
    {
        
    });

    base.OnNavigatedTo(parameter, mode, state);
}

public override Task OnNavigatedFromAsync(IDictionary<string, object> state, 
    bool suspending)
{
    // Unregisters all messages registered by this receiver.
    Messenger.Default.Unregister(this);

    return base.OnNavigatedFromAsync(state, suspending);
}

To unregister a messenger recipient completely, we simply need to call the Unregister method, passing the receiver reference (line 17).

NavigationService with MVVM Light and Template 10

07/10/2015 1 comment

Navigation is one of the most important part of every Universal Windows app. If you are accustomed to use MVVM Light, probably you already leverage the NavigationService this toolkit provides.

Template 10 too comes with its own NavigationService. It is accessible from the Application BootStrapper class and directly from classes inheriting from ViewModelBase.

First versions of Template 10 allowed to navigate by page type, which is the standard on the XAML Frame. If, however, we are following the MVVM pattern, and so we control navigation from view models, this approach isn’t correct, because view models shouldn’t know anything about pages. In fact, NavigationService from MVVM Light requires to associate each page type to a key and then use it to handle navigation from views models (as described in the article Navigation Service in MVVM Light 5).

Last version of Template 10 (1.0.4 on NuGet) improves its NavigationService by providing a similar approach based on keys, in order to isolate view types from view models. Let’s see how to use it.

The first step is creating a custom Enum that represents the pages of our app:

public enum Pages
{
    MainPage,
    DetailsPage,
    OtherPage,
    //...
}

Then, we need to populate the PageKeys dictionary exposed by BootStrapper. We can do this in the OnInitializeAsync method:

public override Task OnInitializeAsync(IActivatedEventArgs args)
{
    var keys = PageKeys<Pages>();
    keys.Add(Pages.MainPage, typeof(MainPage));
    keys.Add(Pages.DetailsPage, typeof(DetailsPage));

    return base.OnInitializeAsync(args);
}

We’re associating each page type with the corresponding enum value (lines 4-5). In this way, we can use an overload of the NavigationService.Navigate method that takes as first argument the enum value corresponding to the page type we want to navigate to (instead of use the page type directly). For example:

NavigationService.Navigate(Pages.MainPage);

// Navigation with parameters.
NavigationService.Navigate(Pages.DetailsPage, "parameters");

You can find a sample of this approach in the official Template 10 GitHub repository (thanks to Jerry Nixon for accepting my pull request!). As said, this is strongly recommended when we use the MVVM pattern.

Using IoC and Design time support with MVVM Light and Template 10

23/09/2015 3 comments

In the last post we talked about Integrating Template10 with MVVM Light. Now let’s see how to use some basic services provided by MVVM Light, starting with SimpleIoC and Design time support.

In the previous post we already used it to register a simple ViewModel, but it is more powerful, and for example we can leverage it to inject dependencies. For example, let’s define the following service:

public interface IDataService
{
    IEnumerable<Person> GetPeople();
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class DesignTimeDataService : IDataService
{
    public IEnumerable<Person> GetPeople()
    {
        var people = from n in Enumerable.Range(1, 100)
                     select new Person
                     {
                         FirstName = "Design Time First Name " + n,
                         LastName = "Design Time Last Name " + n
                     };

        return people;
    }
}

public class RuntimeDataService : IDataService
{
    public IEnumerable<Person> GetPeople()
    {
        var people = new List<Person>();

        // In a real scenario, these people are retrived for example via 
        // a Web API.
        people.Add(new Person { FirstName = "Donald", LastName = "Duck" });
        people.Add(new Person { FirstName = "Mickey", LastName = "Mouse" });
        people.Add(new Person { FirstName = "Daisy", LastName = "Duck" });

        return people;
    }
}

The IDataService interface defines a method the retrieves a list of people. Then we have a design and a runtime implementation. We need to register them in the ViewModelLocator class:

public class ViewModelLocator
{
    static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
 
        if (GalaSoft.MvvmLight.ViewModelBase.IsInDesignModeStatic)
        {
            // We are at design time.
            SimpleIoc.Default.Register<IDataService, DesignTimeDataService>();
        }
        else
        {
            // We are at runtime.
            SimpleIoc.Default.Register<IDataService, RuntimeDataService>();
        }
 
        SimpleIoc.Default.Register<MainViewModel>();
    }
 
    // ...
}

At line 7 we use the MVVM Light ViewModelBase.IsInDesignModeStatic property to check if we are at design time (in fact, since the ViewModelLocator is declared as a resource in the App.xaml file, it is automatically instantiated, even if the app isn’t explicitly executed). If so, we register in the MVVM Light SimpleIoC container the type DesignTimeDataService for the interface IDataService. Otherwise, if we are at execution time we register the type RuntimeDataService.

Now we can pass an object implementing the IDataService interface to our View Model:

public class MainViewModel : Template10.Mvvm.ViewModelBase
{
    private IEnumerable<Person> people;
    public IEnumerable<Person> People
    {
        get { return people; }
        set { this.Set(ref people, value); }
    }

    private readonly IDataService dataService;

    public MainViewModel(IDataService dataService)
    {
        this.dataService = dataService;

        if (GalaSoft.MvvmLight.ViewModelBase.IsInDesignModeStatic)
        {
            // Loads people to enable desgin time support.
            People = dataService.GetPeople();
        }
    }

    public override void OnNavigatedTo(object parameter, NavigationMode mode, 
        IDictionary<string, object> state)
    {
        // Loads real data.
        People = dataService.GetPeople();

        base.OnNavigatedTo(parameter, mode, state);
    }
}

In this way, when we are at design time, the type passed to MainViewModel is DesignTimeDataService, while at execution time we’ll have a RuntimeDataService. In fact, in the MainViewModel constructor (lines 12-20) we check whether we are actually at design time, and so we call the GetPeople method, in order to show dummy data (from DesignTimeDataService) to enable design time support. Note that we again use a method available in the ViewModelBase class that comes from MVVM Light, and not the Template10 ViewModelBase implementation from which the View model inherits:

Design Time data

Design Time data

Finally, in the Template 10 OnNavigatedTo method (lines 23-30), we call again GetPeople: as this method is executed when we are at runtime, now we’re retrieving information from RuntimeDataService:

Runtime data

Runtime data

Working with interfaces and implementations is strongly recommended. Besides allowing design time support, as we have just seen, it make easier to test our code. MVVM Light provides all the tools that we need to do that (like a simple IoC container), and integrating it in a project built with Template 10 is straightforward.

You can download the complete example using the link below:
Using IoC with MVVM Light and Template10

Integrating Template10 with MVVM Light

15/09/2015 4 comments

Template10 is an advanced project template for the Unversal Windows Platform. It’s an open source project available on GitHub at https://github.com/Windows-XAML/Template10. If you don’t know about it, I suggest you to read the great article Template10: a new template to create Universal Windows apps – The basics by Matteo Pagani (Support Engineer at Microsoft).

The template has been created with MVVM in mind, and in fact contains some useful classes that simplify the adoption of this pattern (like BindableBase and ViewModelBase). But it isn’t an MVVM Framework (or toolkit). However, it’s easy to integrate Template10 with, for example, MVVM Light. Let’s see how to do that.

First of all, let’s add the template to our app, as described in the post linked above (you can also find it on NuGet). Then, we need to add MVVM Light using NuGet: search for the MvvmLightLibs package and install it. As NuGet 3.0 doesn’t allow anymore to run installation script, even if we choose the MvvmLight package, it will not install additional files like ViewModelLocator, nor it will modify the App.xaml file with its reference. So, we need to perform these steps manually.

Let’s create a folder called ViewModels with a MainViewModel.cs file:

public class MainViewModel : Template10.Mvvm.ViewModelBase
{
    public string HelloMessage { get; }

    public MainViewModel()
    {
        HelloMessage = "Hello from Mvvm!";
    }

    public override void OnNavigatedTo(object parameter, NavigationMode mode, 
        IDictionary<string, object> state)
    {
        base.OnNavigatedTo(parameter, mode, state);
    }
}

Note that MainViewModel inherits from a class contained in the Template10 (that, among the others, define the OnNavigatedTo method: as we’ll see, it we’ll be invoked when the user navigates to the associated page.

Now we need to implement a ViewModelLocator:

public class ViewModelLocator
{
    static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        SimpleIoc.Default.Register<MainViewModel>();
    }

    public MainViewModel MainViewModel 
        => ServiceLocator.Current.GetInstance<MainViewModel>();
}

Finally, let’s insert the ViewModelLocator in the Application Resources (App.xaml):

<common:BootStrapper
    ...>

    <Application.Resources>
        <ResourceDictionary>
            <vm:ViewModelLocator x:Key="Locator" 
                            xmlns:vm="using:Template10MvvmLight.ViewModels" />
        </ResourceDictionary>
    </Application.Resources>
    
</common:BootStrapper>

And set the DataContext of MainPage.xaml:

<Page
    x:Class="Template10MvvmLight.MainPage"
    DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}"
    ...>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding HelloMessage}" HorizontalAlignment="Center"
                   VerticalAlignment="Center" />
    </Grid>
</Page>

We can now test the app. Set a breakpoint in the OnNavigatedTo method of the MainViewModel class and run the project: as we expect, it will be called on page activation and then the TextBlock will show the HelloMessage property of the view model.

You can download the complete example using the link below:
Integrating Template10 with MVVM Light

In the next post, we’ll see how to use the other services provided by MVVM Light, so stay tuned!

Binding to Enum in Universal apps with localization

19/03/2015 1 comment

When we work with enums, we may want to display description messages associated to symbolic names. For example, suppose we have the following model in our app that uses the MVVM pattern:

public enum Status
{
    Free,
    NotAvailable,
    DoNotDisturb
}

public class Contact
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Status CurrentStatus { get; set; }
}

If we bind the CurrentStatus property of Contact class, the desired result is to have values such as Free, Not Available and Do Not Disturb.

This task can be simply accomplished using Custom Attributes and a Converter. First of all, create a Display attribute and use it to decorate the enum:

public class DisplayAttribute : Attribute
{
    public string Name { get; private set; }

    public DisplayAttribute(string name)
    {
        Name = name;
    }
}

public enum Status
{
    [Display("Free")]
    Free,
    [Display("Not Available")]
    NotAvailable,
    [Display("Do Not Disturb")]
    DoNotDisturb
}

Then, we define the following Converter:

public class EnumToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null || !(value is Enum))
            return null;

        var @enum = value as Enum;
        var description = @enum.ToString();

        var attrib = this.GetAttribute<DisplayAttribute>(@enum);
        if (attrib != null)
            description = attrib.Name;

        return description;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        string language)
    {
        throw new NotImplementedException();
    }

    private T GetAttribute<T>(Enum enumValue) where T : Attribute
    {
        return enumValue.GetType().GetTypeInfo()
            .GetDeclaredField(enumValue.ToString())
            .GetCustomAttribute<T>();
    }
}

At line 11 we check whether the enum value is decorated with the Display attribute. If so, we retrieve its Name property, that contains the actual string we want to use.

The last thing to do is to use the EnumToStringConverter in our binding declaration. For example:

<TextBlock Text="{Binding CurrentStatus, 
    Converter={StaticResource EnumToStringConverter}}" />

And what’s about localization? In a real world scenario we want strings localized according to user culture. Thanks to the built-in support in WinRT, this is straightforward. We just need to modify a bit our Converter:

public class EnumToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null || !(value is Enum))
            return null;

        var @enum = value as Enum;
        var description = @enum.ToString();

        var attrib = this.GetAttribute<DisplayAttribute>(@enum);
        if (attrib != null)
        {
            var resource = new ResourceLoader();
            if (!string.IsNullOrWhiteSpace(resource.GetString(attrib.Name)))
                description = resource.GetString(attrib.Name);
            else
                description = attrib.Name;
        }

        return description;
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
        string language)
    {
        throw new NotImplementedException();
    }

    private T GetAttribute<T>(Enum enumValue) where T : Attribute
    {
        return enumValue.GetType().GetTypeInfo()
            .GetDeclaredField(enumValue.ToString())
            .GetCustomAttribute<T>();
    }
}

At lines 14-16 we create a ResourceLoader and try to get the string that corresponds to the Name property of the attribute. So, to enable localization of enum values we simply need to add a Resource file (resw) for each language and define resource names accordingly.

If, instead, the string isn’t found with the ResourceLoader, the original Display.Name property is used (line 18), as in the first version of the Converter.

Categories: .NET, C#, MVVM, Windows Phone, WinRT