🕓 5 MIN Our first release of …
As Xamarin.Forms inches closer to the end of support, you may consider your options for migrating your mobile applications and maybe even custom controls for your ‘old’ Xamarin.Forms app, which you need to bring with you. In this article, we will work through migrating the CardView demo originally provided as one of the sample apps in Xamarin.Forms repo to an Uno Platform app to show you how easy it is to port Xamarin.Forms apps to Uno Platform.
The CardView is a sample control based on Xamarin.Forms’ ContentView and is designed to display an image, header and body text in a card format (often used in a list).
A custom control consists of two files – the XAML file defining the UI and a code-behind file which contains some boilerplate code and any custom properties you have defined for the control. The class for your control inherits from ContentView and is marked as partial because some functionality is added by compiler-generated code. The standard empty class contains a constructor with a call to InitializeComponent. As with pages (and any other XAML-based class), this calls a generated method which is responsible for loading the associated XAML file and defining in code any named members (marked with x:Name attributes) so that you can refer to them in your code.
To expose additional bindable properties on your custom control, these are created as static instances of BindableProperty in Xamarin Forms. The BindableProperty.Create method takes a name, property type, and the type of the defining class (in our example, the CardView type) and a default value and returns an instance of BindableProperty.
public static readonly BindableProperty CardTitleProperty = BindableProperty.Create(nameof(CardTitle), typeof(string), typeof(CardView), string.Empty);
With this defined, you can set these properties from XAML when you create instances of your custom control, including data-binding them to your View Model. In Uno and WinUI, the equivalent class is DependencyProperty, and it has a static Register method which has a very similar signature to the BindableProperty.Create() method. You just need to pass the default value in the constructor for PropertyMetadata – the equivalent becomes:-
public static readonly DependencyProperty CardTitleProperty = DependencyProperty.Register(nameof(CardTitle), typeof(string), typeof(CardView), new PropertyMetadata(string.Empty));
To easily use these properties from code it is common to define instance properties on the class which wrap, and strongly type, these binding properties by calling the GetValue and SetValue methods e.g.
public string CardTitle
get => (string)GetValue(CardView.CardTitleProperty);
set => SetValue(CardView.CardTitleProperty, value);
The Uno/WinUI DependencyObject contains these same GetValue/SetValue methods so this code doesn’t need to change.
The XAML file will always have the root element of ContentView and the content defines the layout and child controls required. The root element of Frame, in the case of the CardView, has its BindingContext set to this which means you can bind this and any child controls to any binding properties defined for the class. This is how the CardView UI binds the labels and image controls to these data-bound properties which in turn have been data-bound to a View Model in the consuming code. You cannot re-use the XAML directly because there are some syntax differences but there is an equivalent WinUI type for each of the Xamarin Forms primitives.
For Uno the root element in the XAML file becomes ContentControl and the default namespace is xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation rather than the http://xamarin.com/schemas/2014/forms version created for Xamarin Forms. Several common components exist with different names between these two flavours of XAML – Label in Xamarin Forms is equivalent to TextBlock in Uno, BoxView to Rectangle.
In Xamarin Forms, you can set the preferred size for elements with the HeightRequest and WidthRequest properties. WinUI doesn’t have these – instead, you set the Height and Width properties as required, but these may change based on layout constraints.
Once you get down to the properties of these elements, there are more changes to make. One of the big differences is that Xamarin.Forms often rely on Color properties for controls where WinUI uses Brushes which can be solid colours, gradients and more. The good news is that you can pass a Color as one of these properties and it is converted to a SolidColorBrush under the hood. Preset colors in Uno are defined on the Colors type which sits alongside the Color structure itself – all of these members are an instance of Color. Colors defines a set of named color constants which will be familiar to Xamarin and web developers.
In Xamarin you could use Color.Accent to apply the user’s chosen accent color (which you can override globally for your app). This doesn’t exist in WinUI, instead there is a global color resource called SystemAccentColor which you can refer to from your XAML.
Another common difference is in the Alignment properties used – For Xamarin.Forms the HorizontalOptions and VerticalOptions properties both use the same enumeration which accepts Start, End, Fill and Center. In Uno/WinUI there are specific horizontal and vertical enumerations and the properties are called Alignment rather than options so:-
VerticalOptions=”Center” becomes VerticalAlignment=”Center”
Controls, such as the Label, which have a Text property also have properties for changing the text appearance. These differ between the two flavours of XAML – FontAttributes in Xamarin is used to set bold or italic styling. In Uno FontWieght allows you to set bold, and FontStyle is for setting italic.
In short you will see from the full sample code that there are lots of naming changes but the general structure of the XAML remains the same. The more of these properties you have set centrally in styles rather than on individual elements, the easier it is to change them to the Uno/WinUI syntax.
In the CardView sample, one of the views shows how the appearance of the custom control can be overridden by applying a new control template. This is possible with any ContentControl (ContentView in Xamarin Forms) derived control. The control template defines its XAML tree to display the control as required and can use the TemplateBinding markup to use property values set on the custom control.
Other than the property names and types already discussed, the main difference here is that in Uno Platform, the relevant property on the control is Template rather than ControlTemplate. Also, the ControlTemplate XAML must define the TargetType. As well as ensuring the template is not misused on a different type of control, this also enables Intellisense when editing the template XAML as it knows what properties exist on the target control type.
One missing feature is the support for shadows. The Xamarin Forms Frame type uses the HasShadow property to enable or disable a simple drop shadow on the control. In WinUI the same effect can be achieved by setting the Shadow property of the equivalent Border to an instance of ThemeShadow.
Then you must set a Translation on the border control to raise it up in the z axis so that the shadow can draw correctly to indicate the height of the control. The WinUI design guidelines specify 32 pixels for a standard look, but we must be careful because this is not currently supported in Uno:-
CardBorder.Translation += new System.Numerics.Vector3(0, 0, 32);
The result is that the control on iOS and Android has a simple border whereas the Windows version has a drop shadow like the original control.
For those new to the Uno Platform, it allows for creating pixel-perfect, single-source C# and XAML apps that run natively on Windows, iOS, Android, macOS, Linux and Web via WebAssembly. In addition, it offers Figma integration for design-development handoff and a set of extensions to bootstrap your projects. Uno Platform is free, open-source (Apache 2.0), and available on GitHub.
The full sample project shows how to migrate a Xamarin.Forms custom control to Uno Platform by updating the XAML and the C# code behind. Hopefully, it gives some valuable hints on migrating other XAML content too. The complete code is on GitHub here.
If you are new to Uno Platform, following our official getting started guide is the best way to get started. (5 min to complete) You can also explore our documentation to understand better how Uno Platform works so you can build your next desktop, mobile, and web apps.