Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Rebuilding the Xamarin.Forms Button with Visual States and Control Templates

In my previous post I provided a demonstrable way that visual states could be used in a Xamarin.Forms application to adjust layout. Whilst I showed how this can be used on a page to show/hide a piece of text, it can also be used to define the different states of a button. Before I get onto using visual states within a button, I’ll first show how a ContentView can be adapted to provide the basic button behaviour, whilst allowing both Content and ControlTemplate to be defined. Ok, perhaps that doesn’t make sense but let’s walk through an example:

<controls:ContentButton Pressed="ToggleButtonPressed">
    <StackLayout>
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
    </StackLayout>
</controls:ContentButton>

In this code, I can add any XAML elements as Content within the ContentButton tags. The ContentButton raises a Pressed event, instead of a Click or Clicked event, to which an event handler can be attached. In the next code snippet, I’ve overridden the default ControlTemplate, wrapping the ContentPresenter in a Grid which has a yellow background.

<controls:ContentButton VerticalOptions="Center"
                        x:Name="DisabledButton"
                        HorizontalOptions="Center">
    <ContentView.ControlTemplate>
        <ControlTemplate>
            <Grid BackgroundColor="Yellow">
                <ContentPresenter x:Name="ContentPresenter"
                                    Content="{TemplateBinding Content}" />
            </Grid>
        </ControlTemplate>
    </ContentView.ControlTemplate>
    <StackLayout>
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
        <Label Text="Hello!" />
    </StackLayout>
</controls:ContentButton>

Overriding the ControlTemplate demonstrates an easy way to override the style of the button in a way that can be replicated across all instances of the button in an application.

Let’s take a look at the inner workings of the ContentButton, in particular the default ContentTemplate which is used to define different visual states of the button. In this case there are four visual states: Normal, PointerOver, Pressed and Disabled.

<ContentView.ControlTemplate>
    <ControlTemplate>
        <Grid x:Name="RootGrid" BackgroundColor="{TemplateBinding BackgroundColor}">
            <local:VisualStateManager.VisualStateGroups>
                <x:Array Type="{x:Type local:VisualStateGroup}">
                    <local:VisualStateGroup Name="CommonStates">
                        <local:VisualState Name="Normal" />
                        <local:VisualState Name="PointerOver">
                            <local:VisualState.Setters>
                                <local:Setter Value="#33000000" Target="Background.BackgroundColor" />
                            </local:VisualState.Setters>
                        </local:VisualState>
                        <local:VisualState Name="Pressed">
                            <local:VisualState.Setters>
                                <local:Setter Value="#66000000" Target="RootGrid.BackgroundColor" />
                            </local:VisualState.Setters>
                        </local:VisualState>
                        <local:VisualState Name="Disabled">
                            <local:VisualState.Setters>
                                <local:Setter Value="#33000000" Target="Background.BackgroundColor" />
                            </local:VisualState.Setters>
                        </local:VisualState>
                    </local:VisualStateGroup>
                </x:Array>
            </local:VisualStateManager.VisualStateGroups>
           
<BoxView BackgroundColor="Transparent" x:Name="Background" />
            <ContentPresenter x:Name="ContentPresenter" Content="{TemplateBinding Content}" />
        </Grid>
    </ControlTemplate>
</ContentView.ControlTemplate>

In this code you can see how the visual states (which I introduced in my previous post) are used to adjust the background colour of the BoxView that sits behind the Content of the button.

The sample project that demonstrates the ContentButton can be downloaded here

Styling Pages and Controls in Xamarin Forms using Visual States

I’ve been a bit proponent of both Xamarin and Xamarin.Forms for a long time but recently I’ve been taking another look at some of the fundamentals. Xamarin.Forms, for all its great intent is still a poor-man’s answer to declaring layout of an application in Xaml. Unlike Xaml platforms that have come before it (WPF, Silverlight, UWP), Forms doesn’t support some of even the basic features that you’d expect. On the chopping board today is the Button control – as one of the fundamental controls for any application, the Button should be able to be completely customised. Unfortunately it’s far from easy to alter the look of the basic button.

If you look at how a simple button renders on different platforms, there is a clear difference between the style of the button. The following image shows how the button control renders across UWP, iOS and Android (UWP has an extra visual state as it supports mouse/pointer over on desktop).

image

Button States Across Three Platforms

The difference in style is by design – the rendering of the button on each platform uses the built in style for each platform. This works well if you’re interesting in building a generic looking application (eg perhaps a simple LOB data capture application). However, if you want to style the button in line with your branding and the design of your application, you’re going to want to make the buttons look and behave the same across all devices. You’d think this would be as simple as defining a common style, or perhaps overriding the default layout of the button. This is in fact doable – for example on UWP you can use an implicit style to override the look of every button across the application.

Having to override the style of the button on each platform defeats the purpose of using Forms in the first place – once you’ve overridden the style, you then need to make platform specific changes every time you want to alter the style of the button control; imagine doing this for every control in your application – pretty soon, you might as well have developed the application using the traditional Xamarin model, rather than Forms. So, let’s look at the limitations of the Button control and see what we can do to address some of its shortcomings in a way that will help us build better applications quicker.

There are two issues that I have with the Button control. The first is that it’s not easy to customise the content within the Button itself. For example if you wanted to have two lines of text, with the first being centred in bold, and the second justified italics with word wrapping enabled to allow it to flow to multiple lines. In UWP this would be easy with the Button control as the Content could be customised to have multiple TextBlock elements, with the style set accordingly.

<Button>
    <StackPanel>
        <TextBlock Text="Hello World!"
                    TextAlignment="Center"
                    FontWeight="Bold" />
        <TextBlock Text="This is a short message that should word wrap with all the lines being set to justified"
                    FontStyle="Italic"
                    TextWrapping="WrapWholeWords"
                    TextAlignment="Justify" />
    </StackPanel>
</Button>

This would look similar to the following:

image

UWP Button with Custom Content

Attempting to do this with the Forms Button control is not trivial. The second issue is how the different visual states are handled. Again in UWP the different Button visual states are defined in XAML (generic.xaml) where appropriate properties are set based on the active states (eg Normal, Pressed, PointerOver). Forms doesn’t support Visual States, so attempting to define Button states declaratively is going to take more work.

My solution to these issues comes in two steps: firstly, provide a declarative way to define Visual States in XAML, similar to what’s available in UWP; and secondly to provide an alternative to the Button which can not only take custom content but also has a template that can be override to customise behaviour for different button states. If you’ve ever drilled into a control template for a UWP control, chances are that it has some visual states defined. Rather than having some predefined behaviour that’s not accessible or customisable, the various states of UWP controls are defined in XAML in the template for the control. In addition to defining the different states within a control, visual states can be used to define different states of a page too.

In this post we’ll focus on the first step, which is how to define in XAML and then apply visual states. Visual states are defined in visual state groups, and only a single state within a given states group can be active at any time (ie they’re mutually exclusive). However, it is possible that two states, from different groups can be active at any point in time.

So far we know we need visual states, defined within visual state groups. Within each visual state we need to be able to define which properties are going to change when the visual state is made active. UWP allows the property changes to be defined either as a storyboard, potentially with animation, or as a series of setters where the property is updated to the new value immediately. For the purposes of getting the basics working, we’re just going to work with property setters.

Let’s work with a very simple scenario where we have a simple Label which we need to be able to show and hide based on the state of the page:

<Label FontSize="30"
        x:Name="HelloLabel"
        Text="Hello World!" />

In our very contrived example we have a button that will be used to toggle whether the Label is visible.

private bool visible = true;
public void ToggleButtonPressed(object sender, EventArgs e)
{
    visible = !visible;
    VisualStateManager.GoToState(this, visible ? "Show":"Hide");
}

This code, by design, is similar to what you might see within a UWP application that uses visual states to control the layout – in this case showing and hiding an element. Currently visual states don’t exist in Xamarin Forms but here’s a first pass on an implementation of visual states:

<vsm:VisualStateManager.VisualStateGroups>
    <x:Array Type="{x:Type vsm:VisualStateGroup}">
        <vsm:VisualStateGroup Name="LabelStates">
            <vsm:VisualState Name="Show" />
            <vsm:VisualState Name="Hide">
                <vsm:VisualState.Setters>
                    <vsm:Setter Value="false"
                                Target="HelloLabel.IsVisible" />
                </vsm:VisualState.Setters>
            </vsm:VisualState>
        </vsm:VisualStateGroup>
    </x:Array>
</vsm:VisualStateManager.VisualStateGroups>

For those familiar with the syntax in UWP this should look familiar – there’s a few kinks to work out (for example the use of the Array tag) but in its initial form it works.

Sample project can be downloaded here

NuGet does my head in….. No thanks to Xamarin.Forms

This is a bit of a rant with hopefully a fix that will help others. Firstly, the rant:

In my post on Building Cross Platform Apps I used the new project templates in Visual Studio 2017 to create a new Xamarin.Forms application that targets iOS, Android and UWP. What I didn’t mention is the time I wasted trying to upgrade NuGet packages in order to get the thing to build and run. Namely I get the following exception when attempting to build the newly created application.

Severity    Code    Description    Project    File    Line    Suppression State
Error        Exception while loading assemblies: System.IO.FileNotFoundException: Could not load assembly 'Xamarin.Android.Support.v7.RecyclerView, Version=1.0.0.0, Culture=neutral, PublicKeyToken='. Perhaps it doesn't exist in the Mono for Android profile?
File name: 'Xamarin.Android.Support.v7.RecyclerView.dll'
   at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve(AssemblyNameReference reference, ReaderParameters parameters)
   at Xamarin.Android.Tasks.ResolveAssemblies.AddAssemblyReferences(DirectoryAssemblyResolver resolver, ICollection`1 assemblies, AssemblyDefinition assembly, Boolean topLevel)
   at Xamarin.Android.Tasks.ResolveAssemblies.Execute(DirectoryAssemblyResolver resolver)    App5.Droid           

I figured that there was something wrong with the Xamarin.Forms and/or Xamarin Android NuGet packages, so I thought I’d just go an upgrade the Xamarin.Forms NuGet package. Note: Don’t every try to upgrade the Xamarin Android packages independently – let the Xamarin.Forms NuGet package determine which versions of those libraries you need. Anyhow, unfortunately that just generates another exception:

Severity    Code    Description    Project    File    Line    Suppression State
Error        Unable to resolve dependencies. 'Xamarin.Android.Support.Compat 24.2.1' is not compatible with 'Xamarin.Android.Support.Design 24.2.1 constraint: Xamarin.Android.Support.Compat (= 24.2.1)'.            0   

At this point I was starting to get annoyed – it looks like there was already an inconsistency between the Xamarin.Forms package and the Xamarin Android packages included in the template. Luckily it was easier to fix than I thought it was. I opened up the packages.config file for the Android head project and delete the Xamarin.Android libraries.

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Xamarin.Android.Support.Design" version="23.4.0.1" targetFramework="monoandroid60" />
  <package id="Xamarin.Android.Support.v4" version="23.4.0.1" targetFramework="monoandroid60" />
  <package id="Xamarin.Android.Support.v7.AppCompat" version="23.4.0.1" targetFramework="monoandroid60" />
  <package id="Xamarin.Android.Support.v7.CardView" version="23.4.0.1" targetFramework="monoandroid60" />
  <package id="Xamarin.Android.Support.v7.MediaRouter" version="23.4.0.1" targetFramework="monoandroid60" />

  <package id="Xamarin.Forms" version="2.3.2.127" targetFramework="monoandroid60" />
</packages>

Then, I just upgraded Xamarin.Forms to the latest NuGet package and all was good again. Lastly, I upgraded all the other NuGet packages (excluding the Xamarin.Android packages) to the latest stable versions.

When I grow up I want to be a .NETStandard Library!

A month or so ago we made the decision to upgrade some of the portable libraries we use for projects from being based on PCL Profiles (eg profile 111 or profile 259) across to .NET Standard. To do this, we followed the upgrade prompt in the project properties page in Visual Studio.

image

After clicking the “Target .NET Platform Standard” you can then pick which .NET Standard you want. Where possible we try for .NETStandard 1.0, which aligns with PCL Profile 259, or .NETStandard 1.1, which aligns with PCL Profile 111 (see https://docs.nuget.org/ndocs/schema/target-frameworks). The theory being that a .NETStandard 1.0 library can be consumed by any .NETStandard library 1.0 and above, as well as any PCL profile library that is profile 259 – in other words, we’re aiming for maximum reach.

Take for example, BuildIt.General, which is a general purpose utility library. This is now a .NETStandard 1.0 library….. or so we thought. I was doing some testing on the current stable version of the library to make sure it could be added into a Xamarin Forms application. In my previous post I showed how easily you can create a new Xamarin Forms project that has a PCL that contains all the UI for the application, and then head projects for each target platform (iOS, Android, UWP). Before adding in BuildIt.General I upgraded all the existing NuGet references – always good practice when creating new project (disclaimer: this sounds easier than it is due to an issue with the current Xamarin.Forms template which makes it hard to upgrade – more on this in a future post).

Next I added a reference to the BuildIt.General NuGet package – I made sure it was added to all projects as it includes a UWP library that has some useful extensions such as converters that are specific to UWP. At this point everything was able to build and I was able to run the UWP application (I didn’t get round to running the other platforms but I assumed that they would also work since I hadn’t really modified anything from what comes out of the box). I then wanted to test that I could invoke functions from the BuildIt.General library, so I added the following into the MainPage.xaml.cs file within the Xamarin.Forms PCL library:

LogHelper.Log("test");

Now, when I attempted to build the library I got the following error:

The primary reference "BuildIt.General, Version=1.0.0.20, Culture=neutral, processorArchitecture=MSIL" could not be resolved because it was built against the ".NETPortable,Version=v5.0" framework. This is a higher version than the currently targeted framework ".NETPortable,Version=v4.5,Profile=Profile259".

This completely confused me….. what’s this reference to .NETPortable and what’s the difference between v5.0 and v4.5. Immediately I thought that it was an issue with Visual Studio 2017 but after a bit of investigating it turns out that it’s a more fundamental issue with the way that we, actually MSBuild/Visual Studio, is creating the BuildIt.General library. I came across this thread which includes this comment:

image

(Oren has one of the best explanations on the whole .NET Standard v’s PCL Profile discussion at https://oren.codes/2016/06/23/portable-is-dead-long-live-netstandard/)

Anyhow, I wondered whether our library was suffering from the same ill fate. Using ILSpy I took a look at what was included as the TargetFramework and sure enough it’s listed as .NETPortable, v5.0.

image

The workaround for this was listed in this changeset. In summary it requires a change to the project file to override the default generation of the Target Framework Moniker (See bold pieces in the following extract). There are two parts to the change: the first specifies the filename where the TargetFrameworkMoniker assembly attribute will be written to, and then included in the build of the application; the second overwrites the generated TargetFrameworkMoniker.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="
http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
    ...
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <TargetFrameworkProfile>
    </TargetFrameworkProfile>
    <TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
   <TargetFrameworkMonikerAssemblyAttributesPath>$(IntermediateOutputPath)AssemblyTFMAttribute.cs.exclude</TargetFrameworkMonikerAssemblyAttributesPath>
    ...
  </PropertyGroup>
  ...
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
  <Target Name="_SetTargetFrameworkMonikerAttribute" BeforeTargets="GenerateTargetFrameworkMonikerAttribute">
    <PropertyGroup>
      <RealTargetFrameworkMoniker>.NETStandard,Version=v1.0</RealTargetFrameworkMoniker>
      <RealTargetFrameworkMonikerDisplayName>$(RealTargetFrameworkMoniker)</RealTargetFrameworkMonikerDisplayName>
      <TargetFrameworkMonikerAssemblyAttributeText Condition="'$(RealTargetFrameworkMoniker)' != '' and '$(TargetingClr2Framework)' != 'true'">
        // &lt;autogenerated /&gt;
        using System%3b
        using System.Reflection%3b
        [assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(&quot;$(RealTargetFrameworkMoniker)&quot;, FrameworkDisplayName = &quot;$(RealTargetFrameworkMonikerDisplayName)&quot;)]
      </TargetFrameworkMonikerAssemblyAttributeText>
    </PropertyGroup>
  </Target>
</Project>

After making this change, and building a new version of the BuildIt.General library I can now see the correct Target Framework:

image

Out of interest, it appears that if you’re using the xproj project format targeting .NETStandard, it creates a library with the correct target framework. Hopefully this fix, will help you if you’re not.

Building Cross Platform Applications with Visual Studio 2017

Ok, before I jump into this, I want to point out a couple of things:

- Visual Studio 2017 is still in RC

- When you install the RC of Visual Studio 2017 and enable the options to install Xamarin, you will break any existing Xamarin support for earlier versions of Visual Studio (see https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes#KIXamarin).

I noticed the other day when I was creating a throwaway project is that there’s a new dialog when creating cross platform applications within Visual Studio 2017. In the New Project dialog, select the Cross Platform node and there is a single Cross Platform App option.

image

Selecting this option presents you with some project templates – there are only a couple at the moment but I’m hoping that they provide more examples.

image

Unfortunately, and it’s a little hard to see in this screenshot, for the Master Detail template, you can only select Shared Projects. Without exploring the template, I can only assume it was setup this way to share UI code that has platform specific code that compiles based on conditional flags. I really like the way that you can switch between a Forms application and a Native (and no they don’t literally mean “native” (eg Objective-C/Swift or Java/C++), they mean traditional Xamarin where you have platform specific UI.

I opted for the “Blank App (XAML)” which requires Forms, I selected PCL, but unfortunately the “Host in the cloud” option was then disabled. After hitting Accept, the new solution was created and I was immediately able to build and run the application across iOS, Android and Windows (UWP). Very nice.

Adding Xamarin Components via Visual Studio

Sometimes you’ll want to use some of the components from the Xamarin component store. This can be done directly in Visual Studio. Right-click Components node within iOS or Android project and select Get More Components

image

Once added you’ll see the component listed in Solution Explorer under the Components node. However, the Components, like NuGet packages, are located at a solution level. This makes them easy to reference from other projects.

image

In this case the Xamarin.Mobile component can be referenced by our Universal, Windows Phone 8.0, iOS and Android projects. This will make performing operations such as selecting or capturing photos much easier. However, be aware that there are some APIs that aren’t supported across all platforms. For example whilst you can reference the component from the WP8.1 application, MediaPicker fails as it’s not supported.

A Simple ViewModelLocator for Spawning ViewModels for XAML Based Applications

There are numerous frameworks out there that provide mechanisms for instantiating view models. Long again, when I first started building XAML based applications and became familiar with MVVM, I stepped through a number of different ways of creating and wiring up view models. In this post I’m going to show a very basic implementation of a locator to instantiate view models. Into the Core library I will add a ViewModelLocator class which exposes a property, Main, that will return a new instance of the MainViewModel.

public class ViewModelLocator
{
    public MainViewModel Main
    {
        get { return CreateViewModel<MainViewModel>(); }
    }

    private T CreateViewModel<T>() where T:new()
    {
        return new T();
    }
}

I’m going to want a single instance of this class to be created and kept around for the duration of my application’s lifecycle. One option would be to instantiate it within my ApplicationCore class I introduced previously. However, I actually want the instance of the ViewModelLocator to be accessible via XAML, so for this reason it makes more sense to instantiate it as a XAML resource. In the WPF and Universal (Win/WP8.1) applications I can simply add this to the app.xaml file to the Application.Resource element.

<Application.Resources>
        <core:ViewModelLocator x:Key="Locator" />
</Application.Resources>

In the MainWindow (WPF) and MainPage (Universal) I can now specify the DataContext in the opening element, removing the need to create the MainViewModel in code in the codebehind file. Eg

<Window x:Class="RealEstateInspector.Desktop.MainWindow"
        xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        DataContext="{Binding Main, Source={StaticResource Locator}}" >

The current implementation of Xamarin.Forms doesn’t seem to support creating Application resources in XAML. However, by tweaking the App constructor, I can add an instance of the ViewModelLocator:

public App()
{
    Resources = new ResourceDictionary();
    Resources.Add("Locator", new ViewModelLocator());

    MainPage =new MainPage();
}

The syntax in the MainPage of the XForms application is similar except BindingContext replaces DataContext:

BindingContext="{Binding Main, Source={StaticResource Locator}}"

There seems to be a quirk in XForms at the moment in that the Binding expression calls to the Main property repeatedly – since it gets a different instance of the MainViewModel back each time, it ends up in an endless loop trying to get a consistent value. To prevent this, we can add a view model dictionary to the ViewModelLocator and change the behaviour to always return the same instance of the view model.

private readonly Dictionary<Type, object> viewModels = new Dictionary<Type, object>();

private T CreateViewModel<T>() where T:new()
{
    var type = typeof (T);
    object existing;
    if (!viewModels.TryGetValue(type, out existing))
    {
        existing = new T();
        viewModels[type] = existing;
    }
    return (T)existing;
}

And there you have it – view model location across all applications.

Xamarin.Forms, Windows Phone, Azure Active Directory and iOS

In my previous post I thought that there was an issue with using the Azure Active Directory Authentication Library (ADAL) and the updated Xamarin for iOS. This has been confirmed by the team at Microsoft responsible for ADAL with a view that it will be resolved in a future version (no specifics on which version or when). This is unfortunate as it means my iOS version will have to be put on hold for the timebeing.

On a different note I saw a mention in the forums that apparently Xamarin.Forms 1.4 will support Windows Phone 8.1 (and I hope as part of a universal app, also supporting Windows 8.1). Of course, there’s no timing on this one but does pose the question as to whether I should wait for the next iteration of Xamarin.Forms before proceeding with the sample apps. For the timebeing I’ll continue and see how far I get.

Active Directory Authentication with iOS with Xamarin.Forms

Essentially this doesn’t appear to currently work. Due to the alignment of iOS projects to the Unified APIs I think there is currently a compatibility issue between the ADAL prerelease library and the Xamarin.Forms implementation. I’ll come back to this once we have a resolution for this.

Adding Xamarin.Forms Support

So far I’ve only had support for the new Windows platform clients (ie Windows and Windows Phone) for my Real Estate Inspector sample app. However, to make it successful we need to target more platforms. To this end I’m going to add support for iOS, Android and Windows Phone 8.0 using Xamarin.Forms.

After installing the Xamarin tooling for Visual Studio, I added a new project using the Blank App (Xamrain.Forms Portable) template. This actually look multiple attempts as I hadn’t updated the Xamarin tooling prior to creating the projects. I would highly recommend upgrading the tooling first!

image

This will give you three target projects Driod, iOS and WinPhone, in addition to a PCL which contains the common code and ui for the applications. You should be able to build and run each of these targets – however, you’ll need to either register for a trial or have a Xamarin Business subscription. Since the UI is help in the PCL project, it’ll be the same across all the targets, although there may be platform rendering differences.

Now that we have these three targets, we’re going to have to connect up our Azure Mobile Service, work out how we’re going to deal with navigation and structure our solution to maximise code reuse across our Windows platform projects.