What’s my next experience with MAUI? I’m probably going to continue playing around with the MonkeyFinder application, just to get to know the framework better, maybe even do another workshop.

Then I’ll start building my very own multi-platform application, using .NET MAUI.

How I got in touch with .NET MAUI

Let’s start right at the beginning. It took me the first 10 years of my life to realise I like technology. Then it took me almost another 10 years to realise I like the bright and shiny, very new bits of technology. For example the first touchscreen smartphones that were introduced during that second 10 year timespan. You know, the ones with the plastic screen you had to push almost 1mm before they registered your “touch”…

The apps I knew of at that time were all play though. There was the app which you could use to pretend to drink a beer, or the one that sounded like a gunshot. So, when you combine a geeky/nerdy kid, who’s already fascinated by all things tech & IT, with these funny, entertaining (and back then fascinating) new stuff, he’ll just want to get started building stuff by himself! But creating apps for these brand new toys wasn’t easy though. One of the options that was also available around that time was Xamarin, which I didn’t know about. And Xamarin, like me, just needed some time.

Some people are lucky to (seemingly) know exactly what they want. Every decision they make is part of the big goal like becoming a teacher, lawyer, carpenter, technician, etc. I didn’t have a big plan or a goal… I just kinda rolled into IT, as it was one of the few things that could keep me focused for more than – Oh look! Pretty bird just flew by! – right, focused for more than 5 minutes. So obiously I wanted to explore some different career options. The IT-bug in my head was one I couldn’t fix though, so I decided to make it a feature, and thanks to AllPhi, I am where I am today.

In the meantime, Xamarin too had evolved, first into Xamarin.Forms, and while I’m writing this blog, .NET MAUI is almost out of its cocoon and ready for the big crowds. On the 23rd of May .NET MAUI reached general availability (in VS2022 preview) and soon it will be available on all Visual Studio editions.

I’m saying here that Xamarin.Forms evolved into .NET MAUI, but that’s not entirely true. The look and feel of the environment and apps resembled that of Xamarin, but under the hood the entire framework has been built up from scratch to be more efficient, more flexible and more modern (.net6) than Xamarin in order to enable the Developer to build the best applications we can build.

Let me take you with me on my first hands-on experience building a .NET MAUI application following a workshop/guide I found on Github (https://github.com/dotnet-presentations/dotnet-maui-workshop https://www.youtube.com/watch?v=DuNLR_NJv8U)

The Workshop

The app we’ll be making is called the MonkeyFinder. The result is a pretty straight-forward application that allows you to fetch a list of monkeys, found around the world. When tapping/clicking on one of the monkey entries the user will be able to see some more details about the monkey, and as a bonus we’ll leverage the GPS functionality of the device to locate the monkey closest to us.

The workshop is divided into parts 1 through 6, so as developers we obviously start at Part 0, which is more of an overview/setting up. At the time of writing, you need Visual Studio 2022 17.3 Preview with the .NET Multi-platform App UI development workload in order to get started. You will then be able to choose a new .NET MAUI App when creating a new project. If you just want to write the code, you’re basically good to go! However, I think it’s safe to assume you also want to debug your code, in which case there’s a couple more steps to complete.

I won’t go into detail of getting set for all different platforms, but there’s some things to keep in mind when setting everything up. If you want to develop for Android, you’re going to need a Windows machine, If you want to develop for MacOS (not iOS) you’re going to need a Mac, and to run iOS apps from a Windows machine, you’ll still need a Mac (in your same network).

Those who have already worked (and debugged) with Xamarin.Forms before should have very few things left to setup, as emulators etc can be used across Xamarin and .NET MAUI.

Part 0

After the initial setup and creating a MAUI solution, we’ll take a look at the overall structure of a .NET MAUI application. The first big thing to notice is the fact that there’s only one Project to be found. Xamarin.Forms has a separate project for each platform that’s to be targeted, but .NET MAUI has reduced this to a truly single project solution. For platform specifics (like AndroidManifest.xml or Info.plist) there’s a Platforms folder that contains all these to be edited when/if required. In-code you can also write platform-specifics as shown below

#if ANDROID
                  handler.NativeView.SetBackgroundColor(Colors.Red.ToNative());
#elif IOS
                  handler.NativeView.BackgroundColor = Colors.Red.ToNative();
                  handler.NativeView.BorderStyle = UIKit.UITextBorderStyle.Line;
#elif WINDOWS
                  handler.NativeView.Background = Colors.Red.ToNative();
#endif

Aside from the Platforms folders, another big change is the Resources folder. Also no longer separated for each different platform, you can now use the exact same resources for each platform. These go from Fonts & Images to Icons & SplashScreens, and any type of resource in between. As a bonus the fight with icon image sizes is over! You just provide the image you want to use (preferably as svg file), and when compiling/deploying .NET MAUI automatically creates the required sizes for each image you want to use.

Part 1

Part 1 is all about getting some data on the screen. No calls to external sources, no need for any of the API’s provided by MAUI to communicate with the subsystems of your device. We’re going back to basics! We’ve got a class, got some properties in there, and then define our array right in our xaml code, because the goal of this (admittedly very) short part is to just get, well, SOMETHING on our screen. If you’ve ever worked with WPF or Xamarin before, then the CollectionView, along with the Item- and DataTemplate will feel very familiar. If we’re really going to look for new things, then the main aspect you’ll notice are some new “Layouts”. Most of these are self-explanatory. The brand-new HorizontalStackLayout is – as you might have guessed – a StackLayout, without the need to set the Orientation to Horizontal.

We’re back to running the app, just in time to prove me wrong, as we are getting some external data. The online pictures of our monkey are nicely downloaded and shown as expected. On to some more exciting stuff – Part 2!

Part 2

Welcome to Part 2, the biggest and the – I think – most innovative part of .NET MAUI. MVVM & Data Binding. While working on a .NET MAUI app you’ll encounter what is called, the .NET MAUI Community Toolkit (https://github.com/CommunityToolkit/Maui). It’s thanks to this toolkit that MAUI comes pre-equipped with some pretty nice features. One of the main features of the Community Toolkit that’s being handled in this workshop is the MVVM boilerplate code, which you no longer have to worry about. Here’s an example of how the BaseViewModel looks when using the MVVM capabilities offered by the Community Toolkit. Notice how our ViewModel becomes a partial class, for the simple reason that the auto-generated code is also usable in the rest of our class.

namespace MonkeyFinder.ViewModel;
 
public partial class BaseViewModel : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsNotBusy))]
bool isBusy;

[ObservableProperty]
string title;

public bool IsNotBusy => !IsBusy;

The only thing we still have to do is define our private properties, and all public properties – including things like the OnPropertyChanged – will automatically be generated for us. This yields the following result for the isBusy property.

[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public bool IsBusy
{
    get => isBusy;
    set
    {
        if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(isBusy, value))
        {
            OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.IsBusy);
            isBusy = value;
            OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.IsBusy);
            OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.IsNotBusy);
        }
    }
} 

Piece of advice here though.. capitalization is KEY. Private variables start with a lowercase, public properties start with an uppercase. It took me a while to figure out why my busy-indicator wasn’t working properly…

In the next bit, a service is defined in order to fetch the list of monkeys from the web, which isn’t really specific to .NET MAUI. It gets more interesting when we’re calling this service from within our VM. We’ll obviously create a function that calls upon our service, and saves the returned monkeys in a list, along with some basic error-handling. Then we can create a Command to expose this function to our xaml OR we can once again rely on the MVVM functionalities provided by MAUI and just use the [RelayCommand] attribute above our function to have our command automatically generated as shown here.

[RelayCommand]
async Task GetMonkeysAsync()
{
    // all our code fetching the monkeys
}
global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public bool IsBusy
{
    get => isBusy;
    set
    {
        if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(isBusy, value))
        {
            OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.IsBusy);
            isBusy = value;
            OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.IsBusy);
            OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.IsNotBusy);
        }
    }
} 

From our function, with just that attribute, we get a fully usable GetMonkeysCommand

If you’ve also been following the workshop itself, you might have noticed that we injected our MonkeyService through the constructor. There’s really not much more to say about it, MAUI comes with built-in Dependency Injection! All that’s left to do is register our services in our MauiProgram.cs and we’re good to go.

For the last bit of Part 2 it’s just the UI being built. But the main things to take away here is the x:DataType attribute in the ContentPage tag, which enables intellisense in the rest of the document. Another new thing is the fact that we can now use bindings just like in Xamarin or WPF.

Now that we can fetch data from an external source, and show it in our views by binding to our VM properties we’re ready for the next part: Navigation!

Part 3

The built-in shell navigation of .NET MAUI is a URI based system that can automatically serialize and deserialize any object we want to pass to our new page. So in order to navigate to our MonkeysDetailsPage we can create the following Command.

[RelayCommand]
async Task GoToDetails(Monkey monkey)
{
    if (monkey == null)
    return;
 
    await Shell.Current.GoToAsync(nameof(DetailsPage), true, new Dictionary<string, object>
    {
        {"Monkey", monkey }
    });
}

This way we don’t even have to fully type out our URI, we use the nameof(DetailsPage) to define the page we want to go to, a boolean – to determine if we want to animate the switch to the next page – and then a dictionary that contains the objects we want to pass to the next page. In order to make our navigation work we need to bind our command to the items we have made in our list using a TapGestureRecognizer. Next we need the QueryProperty tag to let our DetailsPage know that it can expect a Monkey-object when it’s being navigated to, and lastly we need to register our route. In order to successfully navigate to our new page we need to do three things to make sure our routing works correctly. First we need to register our route in the AppShell.xaml.cs to make sure the application knows which page to navigate to depending on the URI. Then we need to inject our page and VM in the MauiProgram.cs. Lastly we need to inject our VM into our DetailsPage and set the BindingContext.

This part also ends with some layout work, similar to the ones we did previously. Onto Part 4!

Part 4

In the workshop this part focuses on 3 specific Platform Features: Connectivity, Geolocation and Maps integration. All together .NET MAUI supports over 60 platform features from a single api. Since each of these features have their own way to be used I’m not gonna go into specifics. In order to use any of these there’s 3 required steps they all have in common.

  1. Register the feature in the MauiProgram.cs
  2. Inject the feature in the constructor of the VM you want to use it in
  3. Access the injected service for the specific function you need

It’s that simple! You can now leverage a ton of functionalities provided by the devices that run your application!

Part 5

Part 5 is a small one. It focuses on a View that you’ll probably use quite a lot, the CollectionView. We start of with a simple pull-to-refresh, and then we take a look at some of the possible layouts. There’s lists, grids, horizontal, vertical. There’s a few options explained in the workshop, and the rest are described in the Microsoft documentations. We end this part by implementing an EmptyView in our CollectionView, for the moments we haven’t yet loaded our monkeys.

Looks like we’re ready for Part 6!

Part 6

We’ve been working on layout quite a bit so far. This means it’s time for the finishing touches with Theming!

Starting off is the App.xaml file, which is where we can define our reusable resources, from colors to complete styles for our components. After that we’re going to make a light/dark theme for our app. First of all we have to rename and add some colors to our ResourceDictionary. Then we can create setters that depend on the AppThemeBinding property that looks at the light/dark theme settings of our device to choose the specific resource being used.

We’re done!

That was it! We’ve built our very first .NET MAUI application and I had a great time doing so!

Most things felt very familiar, even the new stuff. For me that’s mostly due to some experience with WPF and Xamarin. In this workshop we’ve seen the capabilities of .NET MAUI along with the .NET Community Toolkit. People that are used to other MVVM frameworks, component suites, DI containers, etc can still use them! Many of these frameworks have updates to work with MAUI, which can make it even easier to switch to MAUI and leverage all its benefits.

Thanks for making it this far and reading my blog!

Written by Mathias Peene

Overzicht