Using IValueConverter

I was down in Las Vegas all of last week for the Autodesk Forge DevCon and Autodesk University: a full four days of learning about all of the up and coming Autodesk products and technologies and networking. Lots of fun, lots of new people to talk to. I had a chat with a somewhat new’sh dev and he was complaining about the difficulties of using XAML. We delved into a bit of MVVM and he wanted to know, in effect, how to bind visual properties.

From there we went to the use of value converters and all the potential they have for presentation without having to embed visual elements and other complex logic into your view model. And it is available across WPF, UWP and Xamarin Forms.

A Simple Case

My first value converter was in WPF and making visual elements visible or not based on a value in my view model. Lets pretend we have some additional controls in our window that are only visible if the user is an accounting user. In our view model, it is a bool property named IsAccounting. If we try to bind the IsAccounting property to the control Visibility property, a binding error occurs that says the type of the value is incorrect for the target property: because of the error we won’t see the UI change.


<!-- fails -->
<TextBox Text="{Binding AccountCode}" Visibility="{Binding IsAccounting}" />

We need a value converter that will change the bool value to a Visibility value.


public class BoolToVisConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool v = (bool) value;
        return v ? Visibility.Visible : Visibility.Collapsed;
    }

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

To use it, make sure that you have the namespace that the value converter resides in declared in your Window XAML and then the converter itself referenced in the resource dictionary:

xmlns:local="clr-namespace:Demo"
<Window.Resources>
    <ResourceDictionary>
        <local:BoolToVisConverter x:Key="boolVisConverter" />
    </ResourceDictionary>
</Window.Resources>

Finally you add the converter to the binding expression of the control:


<TextBox Text={Binding SecretAccountCode} Visibility={Binding IsAccounting,Converter={StaticResource boolVisConverter}}" />

And the application looks like this:

2017-11-19boolvisconverter

I like this because it keeps the app logic free of the presentation code, and this makes your view model simpler, testable and portable.

Something More Powerful

Let’s take a look at where the IValueConverter can really help. In this example we will change the color of the text in a textbox based and an image based on the selected item in a combo box. I setup an enum with some employee types:

public enum EmployeeTypeEnum
{
	CEO,
	VicePres,
	EngDirector,
	Engineer,
}

and added EmployeeTypes and SelectedEmployeeType properties to the view model.


/// initialize this collection in the constructor with the enum values.
public ObservableCollection<EmployeeTypeEnum> EmployeeTypes { get; private set; } =
    new ObservableCollection<EmployeeTypeEnum>();

private EmployeeTypeEnum _selectedEmployeeType = EmployeeTypeEnum.CEO;
public EmployeeTypeEnum SelectedEmployeeType
{
    get => _selectedEmployeeType;
    set => SetProperty<EmployeeTypeEnum>(ref _selectedEmployeeType, value);
}

Here are our value converters, one of them will convert the SelectedEmployeeType value to a foreground color and the other will convert the SelectedEmployeeType value to an image.

public class EmployeeColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        EmployeeTypeEnum empType = (EmployeeTypeEnum) value;
        switch(empType)
        {
            case EmployeeTypeEnum.CEO:
                return Brushes.Aqua;
            case EmployeeTypeEnum.EngDirector:
                return Brushes.Green;
            case EmployeeTypeEnum.Engineer:
                return Brushes.Red;
            case EmployeeTypeEnum.VicePres:
                return Brushes.Blue;
            default:
                return Brushes.Black;
        }
    }

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

public class EmployeeImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        EmployeeTypeEnum empType = (EmployeeTypeEnum) value;
        switch (empType)
        {
            case EmployeeTypeEnum.CEO:
                return new Uri("/ceo.png", UriKind.Relative);
            case EmployeeTypeEnum.EngDirector:
                return new Uri("/director.png", UriKind.Relative);
            case EmployeeTypeEnum.Engineer:
                return new Uri("/engineer.png", UriKind.Relative);
            case EmployeeTypeEnum.VicePres:
                return new Uri("/vicepres.png", UriKind.Relative);
	    default:
	        return null;
        }
    }

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

Add our converters to the resource dictionary:

<local:EmployeeColorConverter x:Key="empColorConverter" />
<local:EmployeeImageConverter x:Key="empImageConverter" />

The markup for our XAML looks like this


<ComboBox ItemsSource="{Binding EmployeeTypes}" SelectedItem="{Binding SelectedEmployeeType}" />
<TextBox Text="{Binding SelectedEmployeeType}" Foreground="{Binding SelectedEmployeeType,Converter={StaticResource empColorConverter}}" Margin="0,2" />

<Image Width="48" Height="48" Margin="4" Source="{Binding SelectedEmployeeType,Converter={StaticResource empImageConverter}}" />

And that is it. We have a simple and portable view model that can be used on other platforms.

2017-11-19_6-35-24

Feel free to check out the sample code in GitHub.

I also would like to give a shout-out to SyncFusion as I used their free MetroStudio icon generator to build my icons.

Upcoming AI Bot Workshop

Are you ready to help explore and colonize Mars?

Actually, that is just the scenario for a one day workshop on learning how to create and deploy an AI bot that uses Microsoft tools and services. And it’s free. Just bring your laptop, power supply and either:

or

It is a hands on lab so be prepared to code. You will create your first bot, learn how to use natural language processing, deployment and other topics!

This event is being held in Vancouver (Nov 29), Calgary (Dec 01), Toronto (Nov 21) and Montreal (Nov 23). Find all the details and sign up links on the event page itself.

Visual Studio, Xamarin Forms and Intellisense Errors

This is a bit of an odd one … I was working away on a Xamarin Forms project and suddenly started getting dozens of the red squigglies in the code editor. I could compile the project, but intellisense kept showing all of these errors.

I tried cleaning the solution, manually deleting all the bin and obj folders in all the projects, reloading projects, re-referencing projects. It didn’t matter, whatever I did still showed all kinds of errors, most of them being of the type “the type or namespance name could not be found”.

Finally I was able to track it down. The solution was to find the offending project in the solution (i.e. the one that contains the types that are throwing the errors) and remove it as a reference from the other projects. Then add the removed reference back in to each of the projects (Note you only have to do this for the projects that are showing the errors). After that, the intellisense is reset and all of the squigglies are gone!

 

Google Play Services on Marshmallow

When it comes to working on Android apps, I really feel like I am starting completely from scratch … or maybe even a bit less than that. And I end up running across weird problems that end up causing me all kinds of issues. This post will talk about one of those problems and how I combined the posts of two others (who I am sure know much more than I about Android) to address my issue.

I am working on an app that uses maps. I am planning on targeting Marshmallow at a minimum. I am also using the Visual Studio Android Emulator. If you don’t already know, the Visual Studio emulator doesn’t come with the Google Play Services, which are required if you want to use maps in your app.

In a previous app, I was able to spin up an emulator, go to http://teamandroid.com/gapps, download the appropriate zip file and drag and drop to the emulator to install. This worked great for me on both KitKat and Lollipop emulators. On Marshmallow … not so much.

I started surfing … I mean researching. And in my research I came across an article from Cheesebaron which you can see here. In a nutshell, he recommends (with references) two different gapps packages to download. The only problem was that when I tried his method of downloading and drag and drop installing, my emulator just started an endless loop of optimizing packages and rebooting.

I then found another article from Rob Prouse about how to do a low level install of gapps packages from the SD card. When I tried his method, which used the gapps package from teamandroid.com, that failed as well. But at least it seemed to install without any obvious errors.

I finally deleted my emulator, started up a new one and combined the two articles. I downloaded the two packages recommended by Cheesebaron. I installed the first one in the manner recommended by Rob Prouse and after installation, was able to setup my Google account on the emulator. I then installed the second package from Cheesebaron using the same SD card installation method. After rebooting the emulator, I was able to deploy my app and run it. I am not sure if this is normal behavior, but I had to go into the app settings on the emulator and enable location for it. In the past, I seem to remember it asking me on first install, but I could be mistaken.

My new Marshmallow emulator setup is as follows:

  • Download gapps packages from Cheesebaron’s article:
  • Install the first package via the emulator SD card as per Rob Prouse
  • Create your google account in the settings/accounts
  • Install the second package via the emulator SD card as per Rob Prouse.

Hopefully this helps someone else and if not, writing it down will help me.

 

Xamarin Dev Days Lab – Prism Step 3

Introduction

In our last post, we set up the view model for the SpeakersPage and made use of the built in view model functionality included in Prism. We setup a base view model for our app to contain common functionality. The SpeakersViewModel class has the implementation code for retrieving the list of speakers embedded in the view model itself. Fine for a demo, but in a real app, we would be better served by moving this functionality out and using it via an interface. By implementing the functionality as an interface, we will be able to reuse the code in other view models. Since the SpeakersPageViewModel only knows it via the interface, we will also be able to mock the service with known values for use in unit tests, without changing the view model. And we can use the Prism framework and the dependency injection that is built in to make this easier. So lets take a look at this.

The Interface

The first thing we want to do is look at the functionality that is currently in our view model class. All it is doing is calling a web api to return a list of speakers. That seems pretty straight forward. Let’s start our interface with that:


public interface ISpeakersService
{
    Task<List<Speaker> GetAllSpeakersAsync();
}

Implementing will be easy: we already have all the code that we need in the view model from the demo app:


public class SpeakersService : ISpeakersService
{
    public async Task<List<Speaker>> GetAllSpeakersAsync()
    {
        using (var client = new HttpClient())
        {
            //grab json from server
            var json = await client.GetStringAsync(
                "http://demo4404797.mockable.io/speakers");

             //Deserialize json
             var items =
                JsonConvert.DeserializeObject<List<Speaker>>(json);
             return items;
        }
    }
}

//

From above, you can see that we have just moved all of the implementation code from the view model into a separate service object. So how do we start using this new code in our SpeakersViewModel?
The first thing we need to do is register this service in our App object. Remember that we are registering via an interface: this means we can change out the implementation at any time.
Here is the RegisterTypes function in the App object updated to register the service:


protected override void RegisterTypes()
{
    Container.RegisterType
        <Services.ISpeakersService, Services.SpeakersService>();
    Container.RegisterTypeForNavigation
        <View.SpeakersPage, ViewModel.SpeakersViewModel>();
}

Next we need to refactor the SpeakersViewModel: we will add the interface to the view model constructor and change the GetSpeakersAsync to call the interface to retrieve the speakers data:


public class SpeakersViewModel : BaseViewModel
{
    private Services.ISpeakersService _speakersService = null;

    public SpeakersViewModel(Services.ISpeakersService speakersService)
        : base()
    {
        _speakersService = speakersService;
    }

    /// other parts of the view model go here
    /// now lets look at GetSpeakers ...
    private async Task GetSpeakers()
    {
        if (IsBusy)
            return;

        Exception error = null;
        try
        {
            IsBusy = true;

            var items =
                await _speakersService.GetAllSpeakersAsync();
            Speakers.Clear();

            foreach (var item in items)
                Speakers.Add(item);
        }
        catch (Exception ex)
        {
            error = ex;
        }
        finally
        {
            IsBusy = false;
        }

        if (error != null)
            await Application.Current.MainPage.DisplayAlert(
                "Error!",
                error.Message,
                "OK");
    }

}

So what do we have above? All of a sudden the ViewModel has a dependency on ISpeakersService. But we weren’t creating the view model in the first place: instead, Prism and the AutoWireViewModel attached property (see the previous blog post) are creating the view model for us and attaching it to the page. When Prism does this, it uses the container that was selected (step 01 blog) to create the view model and all the dependencies that are required for the view model, and it does this via reflection and then looking at what you specified in the App.RegisterTypes method. I hope that you can see that you can change up the implementation details of the interface without ever affecting the view model itself. You can even unit test your view model by mocking the service.

Now the view model just makes a call to retrieve the speakers. It has no idea if the speaker are read from a web api, a database, a file or even from memory! It just knows to make the call and it will get the data.

I really want to emphasize this: the view model knows nothing. It has no dependency on any implementation and you can change that around as you see fit! In fact, I am going to change the code of the SpeakersService implementation. I captured the json payload that was returned from the web api and saved it as a constant in the implementation. Now instead of calling out to the web api, I just return the json payload directly, and now for testing, everything is much quicker. And as mentioned earlier, using these known values, I can use this implementation for unit testing of the view model.


public class SpeakersService : ISpeakersService
{
    /// you can see this actual string value in the GitHub repo
    /// under step 03
    private const string JsonResponse =
        "...json response string captured from web api";

    public async Task<List<Speaker>> GetAllSpeakersAsync()
    {
        var items =
            JsonConvert.DeserializeObject<List<Speaker>>(JsonResponse);
        return items;
    }
}

Recap

We have taken the next step in making the dev days app a little more production worthy. We took out the implementation of our speakers service away from the view model, defined it as an interface and injected that into our view model. We used the dependency injection that is built into Prism to inject the implementation of the service into the view model. And then for fun (and quicker testing), we changed the implementation of the interface to serve up the results from a json payload that had been captured previously. Now we can do testing much quicker.

All of this code can be found in the Step03 portion of the repository. Make sure you check out the two different implementations of ISpeakersService contained in the SpeakersService.cs file and how they could be registered in App.cs.

The next step will be to add navigation to the app so we can see the details of a selected speaker.