Tag: Maui

  • Writing NFC Tags in .NET

    I first added NFC code to 32feet.NET back in 2020. However, as other priorities came along it never got fully polished and released on NuGet. This year I finally got around to it and updated the code for .NET 8.0 as it had originally targeted Xamarin Forms.

    InTheHand.Nfc is available on NuGet and follows the API model of WebNFC – it supports reading and writing NDEF tags as this is the most widely used format for providing Urls, text and other custom data types. Although the original Windows codebase is still there it is largely untouched and untested because there is no definitive NFC hardware for Windows and the external Sony reader I had been using is no longer supported. I was able to install the drivers and get it running but I’m not currently able to read tags through the API. Since iOS and Android are more important platforms for this functionality I’ve not spent any more time on the Windows implementation yet.

    There is a sample .NET MAUI app to demonstrate how to use the library. However it should work on any other .NET based platform (Uno, Avalonia etc). There are some platform specific setup items to observe – NFC reading requires permission on both Android and iOS. For iOS you must also add the NFC Reading capability to your app id.

    The code is quite simple. It’s all async and uses Events for detecting tags. You can either pass a cancellation token to control when to stop scanning or if you don’t it will automatically stop after the first tag is scanned.

    Hopefully you’ll find this useful for adding NFC reading/writing to your .NET apps. I do want to re-enable the Windows functionality, and make sure everything described in the sample is also documented in the Wiki. The only missing functionality from WebNFC is the ability to make a tag read-only; if there is a demand I could look at adding this too.

  • Android deep-links and NFC

    Deep-linking is the ability to define a Url which will direct the user straight to a specific feature of your app. On Android these are called App-links, for iOS they tend to be called Universal links. These can be an app-specific uri scheme or you can, with the right configuration on your web server, link a web domain so that you can redirect web links to the equivalent page in your app. I’m going to be working with the first type here.

    .NET MAUI allows you to support deep-linking on both platforms once you’ve set up the necessary handler code in each native project. This passes the received Uri to the Application class so you can handle parsing the Uri and navigating in a single place. I’m not going to go over this process because it is described in the MAUI documented for Android and iOS.

    This allows your app to be launched from a browser, QR Code, or another app. However the default experience on Android for an NFC tag results in a “New tag collected” dialog which asks you if you want to follow the link. Luckily, there is a way around this. You have to specify an additional action in your IntentFilter. Alongside Intent.ActionView you can add NfcAdapter.ActionNdefDiscovered. With this added your app is correctly registered to receive intents from Uri tags and will launch immediately when presented with your specific app link.

    [IntentFilter(new [] { Intent.ActionView, Android.Nfc.NfcAdapter.ActionNdefDiscovered },
    Categories = new [] { Intent.CategoryDefault, Intent.CategoryBrowsable },
    DataScheme = "yourscheme")]

    The great thing about implementing this is you may already be using deep-linking and you can support links from NFC tags without actually having to implement any NFC reading code within your app. You can have a consistent mechanism for working with links from a variety of sources.

  • UI’ve Been Framed

    Recently I’ve been migrating Xamarin Forms UIs to .NET MAUI and have come across a recurring problem. The Frame control was a core part of Xamarin Forms and used for drawing a border and/or shadow around a control. With the changes introduced in MAUI there is a new Border control which has a lot more flexibility and is built from the ground up for MAUI. To provide a migration path Microsoft kept the Frame control in MAUI but it uses a legacy renderer rather than the modern handler approach and this is where the problems seem to occur. We’ve noticed that, particularly on Android, we would sometimes get exceptions from within the FrameRenderer, it wasn’t predictable and it seemed like it was more of a problem when there were multiple nested Frames within a Page.

    The solution was to bite the bullet and to begin replacing these Frames with an equivalent Border. This is fairly straightforward and you can create a style to set your Borders up with the same appearance. Below is an example of some standard Frame appearances, in a moment we’ll look at how to recreate each with the Border control.

    iOSAndroid
    Xamarin Forms Frame appearance

    The first thing to notice is that the default appearance does vary depending on platform. Shadows seem to be barely noticeable on Android but that may be dependent on the OS version used. By default the Frame control has a corner radius of 4, no shadow and a light grey border colour. In hindsight I should have picked a different example background colour!

    Heading to the Border

    Now we have a reference point we can recreate the same look using the Border control. When adding a Frame to a MAUI page the corner radius seems to have changed to 8 by default. To recreate the shape we have to specify the StrokeShape for the Border because this can be any Shape and by default will be a plain Rectangle. You can set this in XAML to a RoundRectangle and include the corner radius. The Stroke property defines the outline brush. Set the StrokeThickness to 1 to match the line style of the Frame.

    Shadows are handled differently in MAUI so you can set the Shadow property on any class derived from VisualElement. Setting the Shadow to one with a radius of 5 creates an equivalent look to the Frame. The full XAML with a side-by-side comparison looks like this:-

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui&quot;
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
    x:Class="MauiBorders.MainPage">
    <Grid Padding="16"
    RowDefinitions="60,60,60,60,60"
    ColumnDefinitions="*,*"
    ColumnSpacing="16"
    RowSpacing="16">
    <Frame Grid.Row="0" Grid.Column="0" BackgroundColor="LightGray">
    <Label Text="" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Frame>
    <Border Grid.Row="0" Grid.Column="1" BackgroundColor="LightGray" StrokeShape="RoundRectangle, 8">
    <Label Text="" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Border>
    <Frame Grid.Row="1" Grid.Column="0" BackgroundColor="LightGray" HasShadow="False">
    <Label Text="HasShadow=False" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Frame>
    <Border Grid.Row="1" Grid.Column="1" BackgroundColor="LightGray" StrokeShape="RoundRectangle, 8">
    <Label Text="HasShadow=False" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Border>
    <Frame Grid.Row="2" Grid.Column="0" BackgroundColor="LightGray" HasShadow="True">
    <Label Text="HasShadow=True" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Frame>
    <Border Grid.Row="2" Grid.Column="1" BackgroundColor="LightGray" StrokeShape="RoundRectangle, 8">
    <Border.Shadow>
    <Shadow Radius="5"/>
    </Border.Shadow>
    <Label Text="HasShadow=True" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Border>
    <Frame Grid.Row="3" Grid.Column="0" BackgroundColor="LightGray" HasShadow="True" CornerRadius="16">
    <Label Text="CornerRadius=16" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Frame>
    <Border Grid.Row="3" Grid.Column="1" BackgroundColor="LightGray" StrokeShape="RoundRectangle, 16">
    <Border.Shadow>
    <Shadow Radius="5"/>
    </Border.Shadow>
    <Label Text="CornerRadius=16" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Border>
    <Frame Grid.Row="4" Grid.Column="0" BackgroundColor="LightGray" BorderColor="Black" HasShadow="True" CornerRadius="16">
    <Label Text="BorderColor=Black" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Frame>
    <Border Grid.Row="4" Grid.Column="1" BackgroundColor="LightGray" Stroke="Black" StrokeThickness="1" StrokeShape="RoundRectangle, 16">
    <Border.Shadow>
    <Shadow Radius="5"/>
    </Border.Shadow>
    <Label Text="BorderColor=Black" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
    </Border>
    </Grid>
    </ContentPage>
    iOSAndroid
    MAUI Frame and Border appearance

    As mentioned at the start, if you have a standard look you can define these properties in a style and apply to Borders throughout your app. Because the border is a more flexible control you have a lot more options around the shape and the outline of the control. For example even when sticking with the RoundRectangle you can specify individual corner radii and the outline stroke can be a gradient rather than a solid colour.

  • Asking for Permission

    Asking for Permission

    I’ve been asked a number of times about the process to ask for Bluetooth permission for your app. I’ve been meaning to create something reusable for some time but it has languished on the To Do list for rather too long. I recently noticed that as of .NET 8.0, MAUI will incorporate a Bluetooth permission which simplifies the process for asking for permission in your app in a clean cross-platform way.

    I used this as an opportunity to refactor the library that I had started so I could create the same pattern for Xamarin Forms and .NET MAUI 7.0 so you can get started with it now and then switch to the in-box equivalent when you update your code. You can request permission with a single line of code:

    PermissionStatus status = await Permissions.RequestAsync<InTheHand.Bluetooth.Permissions.Bluetooth>();

    Today InTheHand.Bluetooth.Permissions went live on NuGet and the source is part of the existing 32feet.NET project. You can use this with either the Bluetooth Classic or Bluetooth LE libraries.

    It provides a simple way to request permission, which on some platforms won’t do anything noticeable but on others, such as Android, it will correctly prompt for permission as long as you’ve set up your manifest correctly.

  • Book Review: .Net MAUI for C# Developers

    Book Review: .Net MAUI for C# Developers

    Jesse Liberty and Rodrigo Juarez have created this book to guide the reader through all aspects of creating apps with .NET MAUI. It assumes a level of experience with C# and .NET but is not limited to previous Xamarin developers. In fact while Xamarin Forms is mentioned it does not get in the way of the .NET MAUI discussion – this is very much a guide to how to create apps with what is available now.

    The style is well structured and yet friendly in tone and throughout the book the code samples follow the creation of a single app from start to finish. Each chapter builds on the last and the code repository which accompanies the book contains branches at each chapter point in the book so that you can drop in at any point.

    Importantly the book begins with setting up your development PC with the tools required to complete the app. When discussing configuring Visual Studio, Jesse admits that screens may change with Visual Studio updates – something which is always a hazard when writing a technical book.

    While it mainly focuses on XAML and C#, it does venture into creating UI with C# and using fluent syntax to simplify this code. Beyond the .NET MAUI APIs themselves the book covers the Community Toolkit libraries which are a great set of resources beyond the core platform.

    A whole chapter is dedicated to MVVM and using the latest MVVM Community Toolkit it shows how to quickly build ViewModels using code generators to handle all the boilerplate code required for properties to support change events. There are lots of notes covering things like naming conventions and project structure. These are both helpful and non-judgmental, recognising different approaches.

    Each chapter ends with a clear summary and a set of quiz questions (if that’s your learning style).

    Ultimately this is a great resource, both for an existing developer who wants to start with .NET MAUI, or for someone transitioning with prior experience of Xamarin Forms. There are plenty of code snippets and screenshots to illustrate each chapter. I definitely recommend this book if you want to become familiar with .NET MAUI.

    Disclaimer: I received a free review copy of the eBook. No other consideration was provided for this review or the content thereof.

  • Dot Net DLLs, Droid, Dependencies

    Bears, Beets, Battlestar Galactica meme from The Office TV Show

    Natively, Android apps are built around activities. These are distinct parts of an application which include UI and are designed to perform a particular task. Android allows you to start activities and pass data between them. A number of system dialogs are just activities which run and return a result.

    Across Platforms

    When using a cross-platform framework, whether it be Xamarin Forms, Uno or .NET Maui your app will generally have only a single Activity. You can create a complex application and never touch the template generated activity code. However when you want to call into an Activity, whether it be your own, a third-party library or a system feature, you need to have a reference to your current activity in order to launch another.

    Each framework has its own method to help with this. On Xamarin Forms this is exposed in the Xamarin.Essentials library – Xamarin.Essentials.Platform.CurrentActivity. When migrating to .NET Maui this moved to the Microsoft.Maui.Essentials library – Microsoft.Maui.ApplicationModel.Platform.CurrentActivity. The new project templates add a line of code to the default MainActivity to initialize this and store a reference to the running activity.

    On Uno the Uno base library includes Uno.UI.ContextHelper.Current which stores a reference to the activity and is initialised in the base Microsoft.UI.Xaml.ApplicationActivity class.

    What’s the point of all this?

    If we want to create a .NET library which works on all these different frameworks and has access to the activity we have some work to do. We can’t create a NuGet targeting different frameworks because these are all net6.0-android – you can’t specify the UI tech. We don’t want to provide multiple virtually-identical libraries tweaked for each framework, but we also don’t want to reference multiple dependencies in one library. The solution is a bit of good old-fashioned reflection. By checking for the presence of each of these types we can query the property and have our activity reference to use as required. Of course, none of these may be present. It’s entirely property our library is used from a “native” .NET Android app and the developer is working directly with the Android APIs. We can still provide a valuable API here but provide a method to pass the activity context to any API which requires it to display some UI. That allows us to cover all bases from a single dll for all “flavours” of .NET Android development. From your cross-platform code it’s entirely transparent. I’ve put my current code into a Gist here:-

    public static bool TryGetCurrentActivity(out Activity activity)
    {
    activity = null;
    #if NET6_0_OR_GREATER
    // check for Uno without taking a hard dependency
    var t = Type.GetType("Uno.UI.ContextHelper, Uno, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null", false, true);
    if (t != null)
    {
    activity = (Activity)t.GetProperty("Current", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).GetValue(null);
    }
    else
    {
    // try Maui Essentials if not
    t = Type.GetType("Microsoft.Maui.ApplicationModel.Platform, Microsoft.Maui.Essentials, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", false, true);
    if (t != null)
    {
    activity = (Activity)t.GetProperty("CurrentActivity", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).GetValue(null);
    }
    }
    #else
    // check for Xamarin.Essentials without taking a hard dependency
    var t = Type.GetType("Xamarin.Essentials.Platform, Xamarin.Essentials, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", false, true);
    if (t != null)
    {
    activity = (Activity)t.GetProperty("CurrentActivity", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).GetValue(null);
    }
    #endif
    return activity != null;
    }

    Next Steps

    The good news from this is that InTheHand.BluetoothLE (as of the next NuGet drop) no longer has Xamarin or Maui dependencies but works just the same on these platforms. I’m interested in extending this to support Avalonia too. I may also move the code into its own library as I envision using it from a few places and it will be easier to maintain.