Categories
.NET MAUI Deep Dive Desktop Developer Migration Mobile MVVM Templates Visual Studio Xamarin

Introducing Shared Class Library – Multi-target Xamarin.Forms and .NET MAUI from a single project

Share more code between Xamarin.Forms and .NET MAUI. Migrate effortlessly to the latest and greatest.

Very happy to announce the availability of the Shared Class Library project template, a library that targets both Xamarin.Forms and .NET MAUI from one single project.

We all know that .NET MAUI is an evolution of Xamarin.Forms targeting .NET 6 (as of this writing), a cross-platform UI stack (native UI) targeting the Android, iOS, macOS, and Windows platforms to complement the .NET toolchain (SDK & Workloads), and a single base class library (BCL).

Unlike Xamarin.Forms, .NET MAUI is multi-targeted from a single project. In Xamarin.Forms, there is a head project (follows conventional Franken style) for each of the platforms it targets and a class library that is referenced in each of the head projects. This class library contains the common definitions that can be shared with all the platforms, which could be UI, services, resources, business logic, models, etc … and it targets .NET Standard 2.x and is an SDK-style project.

And existing applications/libraries targeting Xamarin.Forms can be migrated to .NET MAUI and the migration effort depends on how complex the solution is. For a well-architected solution, the migration would be straightforward.

Apart from XAML UI definitions and static resources such as fonts and images, the majority of the shared artifacts would have been developed in C# and they’re compatible with .NET MAUI to a greater extent.

This paved the way for the idea of the Shared Class Library as described in the intro of this article. With the introduction of Global Usings in C# 10.0 in .NET 6, it’s super easy to move all the using statements into a single place which allows sharing more code among frameworks/platforms.

As described, both the Xamarin.Forms shared library and .NET MAUI project are SDK-style projects, the below project file is the definition of the conceptualized Shared Class Library.

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
        <Nullable>enable</Nullable>
        <LangVersion>latest</LangVersion>
        <ImplicitUsings>enable</ImplicitUsings>
        <RootNamespace>MyApp.UI</RootNamespace>
        <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
    </PropertyGroup>

    <PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
        <DefineConstants>$(DefineConstants);FORMS</DefineConstants>
    </PropertyGroup>

    <PropertyGroup Condition="'$(TargetFramework)' == 'net6.0'">
        <UseMaui>true</UseMaui>
        <SingleProject>true</SingleProject>
        <DefineConstants>$(DefineConstants);MAUI</DefineConstants>
    </PropertyGroup>

    <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
        <Compile Include="**\*.shared.cs;**\*.logic.cs;**\*.forms.cs" />
        <None Include="**\*.maui.cs" />
    </ItemGroup>

    <ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
        <Compile Include="**\*.shared.cs;**\*.logic.cs;**\*.maui.cs" />
        <None Include="**\*.forms.cs" />
    </ItemGroup>

    <!-- Xamarin.Forms NuGet packages -->
    <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
        <PackageReference Include="Xamarin.Forms" Version="5.0.0.2401" />
        <PackageReference Include="Xamarin.Essentials" Version="1.7.3" />
        <PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.2" />
        <PackageReference Include="Xamarin.CommunityToolkit.Markup" Version="2.0.2" />
    </ItemGroup>

    <!-- .NET MAUI NuGet packages -->
    <ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
        <PackageReference Include="CommunityToolkit.Maui" Version="1.0.0"/>
        <PackageReference Include="CommunityToolkit.Maui.Markup" Version="1.0.0" />
    </ItemGroup>

    <!-- Common NuGet packages -->
    <ItemGroup>
        <PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0-preview3" />
    </ItemGroup>

</Project>

As highlighted on line #4, it’s a multi-targeted library producing assemblies targeting .NET Standard 2.0 and .NET 6.0. If required, .NET Standard 2.0 can be updated to .NET Standard 2.1 by updating the corresponding definitions or by introducing new conditional sections.

On line #6, the LangVersion property is set to the value latest, as Global Usings is a feature of C# 10.0 and .NET Standard 2.x is based on earlier versions of C# language.

On line #9, the ability to associate default-compile items in the project is disabled so that it is conditionally compiled.

On lines #12 and #16, additional project constants are defined so that they can be used to segregate code blocks in the shared source files.

And .NET MAUI is based on the workload concept, so it’s defined in the section that corresponds to .NET 6.0 (net6.0).

Now source files need to be segregated according to the target frameworks, though it’s a shared class library, there’re certain artifacts that are specific to the framework it targets.

In which:

  • *.forms.cs source files will be specific to Xamarin.Forms
  • *.maui.cs source files will be specific to .NET MAUI
  • *.shared.cs / *.logic.cs will be common to both Xamarin.Forms and .NET MAUI

To share code between targets in the same shared source file, use compiler directives. A sample is shown below

#if FORMS
global using static Xamarin.Forms.Color;
#elif MAUI
global using static Microsoft.Maui.Graphics.Colors;
#endif

With this definition, the named color values can be used directly like White, Black, … without any type prefixes.

Starting line #33 through line #38, the NuGet package references specific to Xamarin.Forms including the core Xamarin.Forms package

Starting line #41 through line #44, the NuGet package references specific to .NET MAUI.

This project template allows to include all the officially supported packages out of the box.

Starting line #47 through line #49, the NuGet package references are common to both the target frameworks.

This template is made available to install from both via the NuGet package or VS extension. To install this VS extension check out this article.

While installing from the command line:

dotnet new --install VijayAnand.MauiTemplates

To create a new Shared Class Library project:

dotnet new sharedclasslib --name MyApp.UI

And it takes the following parameters to include those NuGet packages and all these parameters are of type Boolean.

So, specifying the parameter name, either in short or full notation, implies that it is defined.

Parameter Name
(Short and Long notation)
Description
-asp | --all-supported-packagesIncludes all the below 6 supported
NuGet packages (added to the package released on Jun 7, 2022)
-ife | --include-forms-essentialsXamarin.Forms Essentials
-ift | --include-forms-toolkitXamarin.CommunityToolkit
-ifm | --include-forms-markupXamarin.CommunityToolkit.Markup
-imt | --include-maui-toolkitCommunityToolkit.Maui
-imm | --include-maui-markupCommunityToolkit.Maui.Markup
-inmt | --include-mvvm-toolkitCommunityToolkit.Mvvm
Project template parameters in CLI

Project creation with all those packages included.

dotnet new sharedclasslib --name MyApp.UI -asp

Or even an easy way, use Visual Studio 2022. In the Start Window, click on Create new project button, then in the All project types dropdown (the last one), filter by category MAUI and scroll to the bottom to locate this Shared Class Library project template. Click on it and provide the name of the project and other options and click Next to arrive at the UI shown below.

Shared class library project template parameters within Visual Studio 2022
Project template parameters within Visual Studio 2022

Now off to the most important thing, the purpose behind this concept, with the C# markup style UI definition, it’s possible to share the UI definitions too with both Xamarin.Forms and .NET MAUI. If you’ve already done so, then half of your migration job is done as it’s very easy to move to .NET MAUI with minimal effort.

And glad to introduce the preview release of the VijayAnand.Toolkit.Markup NuGet package that targets both Xamarin.Forms and .NET MAUI using this Shared Class Library concept are loaded with a set of fluent helper methods and classes to facilitate rapid UI development and better reuse in C#. The source for this NuGet package is made available here on GitHub. And this complements the Xamarin.CommunityToolkit.Markup or CommunityToolkit.Maui.Markup NuGet package in doing so.

Check out the UnifiedDateCalculator, a sample project that shares the UI definition and other stuff between Xamarin.Forms and .NET MAUI is hosted on GitHub linked above. It’s an initial preview, styles can also be shared. Will continue to update this sample and try to post some advanced scenarios when time permits.

Happy coding. Stay connected as we continue to learn and share the experiences from this exciting journey of being a .NET developer.