<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: David Ortinau</title>
    <description>The latest articles on Forem by David Ortinau (@davidortinau).</description>
    <link>https://forem.com/davidortinau</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F540502%2Fcf87abd0-f18e-4af9-80c6-b31bfde0e4b1.jpeg</url>
      <title>Forem: David Ortinau</title>
      <link>https://forem.com/davidortinau</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/davidortinau"/>
    <language>en</language>
    <item>
      <title>5 years of work in 2 weeks</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Sun, 15 Feb 2026 02:03:00 +0000</pubDate>
      <link>https://forem.com/davidortinau/5-years-of-work-in-2-weeks-okg</link>
      <guid>https://forem.com/davidortinau/5-years-of-work-in-2-weeks-okg</guid>
      <description>&lt;p&gt;These past two weeks have been some of the most exciting days I’ve had as a developer. I need to commemorate them.&lt;/p&gt;

&lt;p&gt;The shift I'm seeing is simple but profound: when Copilot can run the app, see the UI/DOM, and read logs and screenshots without me babysitting it, iteration speed stops being linear. It feels like years of work compressing into days.&lt;/p&gt;

&lt;p&gt;Here’s the haul so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MAUI Sherpa&lt;/li&gt;
&lt;li&gt;MauiDevFlow&lt;/li&gt;
&lt;li&gt;PolyPilot&lt;/li&gt;
&lt;li&gt;.NET MAUI Terminal&lt;/li&gt;
&lt;li&gt;.NET MAUI Linux&lt;/li&gt;
&lt;li&gt;.NET MAUI macOS and tvOS&lt;/li&gt;
&lt;li&gt;.NET MAUI Bootstrap Themes&lt;/li&gt;
&lt;li&gt;.NET MAUI Skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common thread here is simple: once Copilot can run the app, inspect UI/DOM, and read logs/screenshots without me babysitting it, iteration speed goes nonlinear. Below is the proof and it's B-A-N-A-N-A-S!&lt;/p&gt;

&lt;h2&gt;
  
  
  MAUI Sherpa
&lt;/h2&gt;

&lt;p&gt;MAUI Sherpa is a Blazor Hybrid app that puts a face to a set of CLI tools for managing your Android and iOS developer environment. That includes SDKs, Devices, Simulators, Emulators, keystores, certificates, provisioning profiles, and so much more. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdadvwbf7ofz37qns9dd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdadvwbf7ofz37qns9dd.png" alt="screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing your environment, installing the right things, knowing what is even installed, and then handling all the certs and provisioning can be among the most frustrating aspect of cross-platform development. This app makes it much easier for you.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Redth" rel="noopener noreferrer"&gt;
        Redth
      &lt;/a&gt; / &lt;a href="https://github.com/Redth/MAUI.Sherpa" rel="noopener noreferrer"&gt;
        MAUI.Sherpa
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Let MAUI Sherpa guide you through all your .NET MAUI dev environment needs!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Redth/MAUI.Sherpa/docs/maui.sherpa.logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMAUI.Sherpa%2Fdocs%2Fmaui.sherpa.logo.png" width="150" alt="MAUI Sherpa Logo"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;MAUI Sherpa&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;em&gt;Your guide to .NET MAUI development&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/Redth/MAUI.Sherpa/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Redth/MAUI.Sherpa/actions/workflows/build.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;
  &lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;MAUI Sherpa is a desktop application for &lt;strong&gt;macOS&lt;/strong&gt;, &lt;strong&gt;Windows&lt;/strong&gt;, and &lt;strong&gt;Linux&lt;/strong&gt; that helps manage your .NET MAUI development environment. It provides a unified interface for Android SDK management, Apple Developer tools, environment diagnostics, DevFlow app inspection, and GitHub Copilot integration.&lt;/p&gt;

&lt;p&gt;🌐 &lt;strong&gt;&lt;a href="https://redth.github.io/MAUI.Sherpa/" rel="nofollow noopener noreferrer"&gt;Visit the website →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Redth/MAUI.Sherpa/website/images/screenshots/Dashboard.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMAUI.Sherpa%2Fwebsite%2Fimages%2Fscreenshots%2FDashboard.png" alt="MAUI Sherpa Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🩺 MAUI Doctor&lt;/h3&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Check your development environment health&lt;/li&gt;
&lt;li&gt;Diagnose .NET SDK, workloads, and dependencies&lt;/li&gt;
&lt;li&gt;AI-powered fix suggestions via Copilot&lt;/li&gt;
&lt;li&gt;One-click environment repairs&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📦 Android SDK Management&lt;/h3&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Browse and install SDK packages&lt;/li&gt;
&lt;li&gt;Manage platform tools, build tools, and system images&lt;/li&gt;
&lt;li&gt;Search and filter packages&lt;/li&gt;
&lt;li&gt;Track installed vs available packages&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📱 Android Emulators&lt;/h3&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Create, edit, and delete emulators&lt;/li&gt;
&lt;li&gt;Start and stop emulators&lt;/li&gt;
&lt;li&gt;Create snapshots for quick boot&lt;/li&gt;
&lt;li&gt;View emulator details and configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🔑 Android Keystores&lt;/h3&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Create and manage Android signing keystores&lt;/li&gt;
&lt;li&gt;View certificate signatures and details&lt;/li&gt;
&lt;li&gt;Export PEPK keys for Google Play&lt;/li&gt;
&lt;li&gt;Cloud sync keystores…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Redth/MAUI.Sherpa" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




&lt;p&gt;&lt;a href="https://github.com/redth" rel="noopener noreferrer"&gt;Jon Dick&lt;/a&gt; built this app in just a few hours, and the key thing he did here that blew my mind involved his new tool MauiDevFlow which essentially &lt;em&gt;gave eyes and hands to Copilot so it could work uninterrupted&lt;/em&gt; to build and validate the app. In very short order this app was built, beautiful, and shipped. &lt;/p&gt;
&lt;h2&gt;
  
  
  MauiDevFlow
&lt;/h2&gt;

&lt;p&gt;This CLI tool builds upon the Apple and Android tools Jon built over the years for Xamarin and .NET MAUI (and Avalonia and Uno). Onto it he added all the things we have been bolting onto our Copilot experiences to help Copilot do more for .NET MAUI projects without needing our intervention. &lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Redth" rel="noopener noreferrer"&gt;
        Redth
      &lt;/a&gt; / &lt;a href="https://github.com/Redth/MauiDevFlow" rel="noopener noreferrer"&gt;
        MauiDevFlow
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Agentic Dev Loop Assists for .NET MAUI apps
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;MauiDevFlow&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Unified tooling for automating and debugging .NET MAUI apps — both native MAUI and Blazor Hybrid
Built to enable AI agents (and humans) to build, deploy, inspect, and debug MAUI apps entirely
from the terminal.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What This Is (and Isn't)&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;MauiDevFlow is designed for &lt;strong&gt;agentic development workflows&lt;/strong&gt; — giving AI coding agents full
autonomy over the MAUI dev loop: build, deploy, inspect, interact, diagnose, fix, and repeat.&lt;/p&gt;
&lt;p&gt;It is &lt;strong&gt;not&lt;/strong&gt; a UI testing framework, and it is &lt;strong&gt;not&lt;/strong&gt; meant to ship in your app. The agent and
debug bridge are intended for &lt;code&gt;#if DEBUG&lt;/code&gt; only. Think of it as giving your AI pair-programmer
eyes and hands inside the running app so it can close the feedback loop on its own — verify its
changes work, see what went wrong when they don't, and iterate without waiting for a human to
manually check the simulator.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native MAUI Automation&lt;/strong&gt; —…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Redth/MauiDevFlow" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




&lt;p&gt;Features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App automation&lt;/strong&gt; - it can see the UI tree, tap, type, interact, swipe, scroll, and all the things plus screenshot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BlazorWebView debugging&lt;/strong&gt; - doesn't just stop at the native elements, but goes deep in the DOM and JS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified logging&lt;/strong&gt; - quit asking me what the error is and to paste it into the session! Copilot can read for itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port broker&lt;/strong&gt; to work with multiple apps simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI tool&lt;/strong&gt; for calling commands to automate all the things&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver Library&lt;/strong&gt; for platform specific orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills!&lt;/strong&gt; to superpower Copilot (and Claude)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The skill will install a NuGet in your project, wire up the host builder to create hooks needed, and you're all set. Kick back and watch Copilot got fully autonomous at your command.&lt;/p&gt;

&lt;h2&gt;
  
  
  PolyPilot
&lt;/h2&gt;

&lt;p&gt;PolyPilot assists you in orchestrating as many Copilot sessions as your tokens and terminals will allow from a beautiful dashboard, but that's just the start. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pureween" rel="noopener noreferrer"&gt;Shane Neuville&lt;/a&gt; at the time of this writing ranks among the heaviest users of Copilot in all of Microsoft, and his creation PolyPilot is a big reason why. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuq9pxih5r3eda8e2y8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuq9pxih5r3eda8e2y8k.png" alt="PolyPilot multi-session"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week the MAUI team has gone wild evolving PolyPilot and crushing work at the same time. It has been one of the most invigorating team experiences I've ever had!&lt;/p&gt;

&lt;p&gt;The secret sauce is that &lt;strong&gt;PolyPilot helps you work on PolyPilot while you use PolyPilot&lt;/strong&gt;. See something you want to improve or change? Do it. PolyPilot will do the work, rebuild, and reload the app in flight. It has all the observability we just talked about with MauiDevFlow and so much more.&lt;/p&gt;

&lt;p&gt;You've got 5 sessions running on different tasks and want to step away? No sweat. PolyPilot is on your phone too, participating over a devtunnel (and recently added direct connection on your LAN). &lt;/p&gt;

&lt;p&gt;Tell PolyPilot what you need from the couch, the dinner table, the...um, gym. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faw94u46k456x3833mkmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faw94u46k456x3833mkmj.png" alt="PolyPilot mobile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the same self improvement loop works here too. PolyPilot can build and deploy the mobile app and refresh you on the fly. It's completely intoxicating the feeling of productivity and power I get. &lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/PureWeen" rel="noopener noreferrer"&gt;
        PureWeen
      &lt;/a&gt; / &lt;a href="https://github.com/PureWeen/PolyPilot" rel="noopener noreferrer"&gt;
        PolyPilot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/PureWeen/PolyPilot/PolyPilot/wwwroot/PolyPilot_logo_lg.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FPureWeen%2FPolyPilot%2FPolyPilot%2Fwwwroot%2FPolyPilot_logo_lg.png" alt="PolyPilot Logo" width="200"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;PolyPilot&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;strong&gt;Your AI Fleet Commander — Run an army of GitHub Copilot agents from a single app.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;em&gt;Multi-agent orchestration • Real-time streaming • Cross-platform • Remote access from your phone&lt;/em&gt;
&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is PolyPilot?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;PolyPilot is a &lt;strong&gt;multi-agent control plane for GitHub Copilot&lt;/strong&gt;. It's a cross-platform native app (macOS, Windows, Android, iOS) built with .NET MAUI and Blazor that lets you spin up, orchestrate, and monitor &lt;strong&gt;dozens of parallel Copilot coding agents&lt;/strong&gt; — each with its own model, working directory, and conversation — all from one dashboard.&lt;/p&gt;

&lt;p&gt;Think of it as &lt;strong&gt;mission control for AI-powered development&lt;/strong&gt;: you launch agents, assign them tasks across different repos, watch them work in real time, and manage everything from a single pane of glass — or from your phone while you're away from your desk.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why PolyPilot?&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;The Copilot CLI is powerful, but it's one agent in one terminal. What if you could:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;…&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/PureWeen/PolyPilot" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  .NET MAUI Terminal
&lt;/h2&gt;

&lt;p&gt;MAUI.TUI is a terminal UI backend for .NET MAUI. Of minimal value in practical use, this experiment demonstrates how well (and quickly) Copilot can build a fresh backend for .NET MAUI. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk509okdy497kpo4o2m1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk509okdy497kpo4o2m1x.png" alt="Maui.TUI screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're unfamiliar with this use of "backend", .NET MAUI backend is the rendering/input implementation for a platform: UIKit for iOS, WinUI for Windows, etc.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Redth" rel="noopener noreferrer"&gt;
        Redth
      &lt;/a&gt; / &lt;a href="https://github.com/Redth/Maui.TUI" rel="noopener noreferrer"&gt;
        Maui.TUI
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      TUI (Terminal UI) Backend for .NET MAUI
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Maui.TUI&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A terminal UI backend for &lt;a href="https://github.com/dotnet/maui" rel="noopener noreferrer"&gt;.NET MAUI&lt;/a&gt;. Write your app with the familiar MAUI API — &lt;code&gt;ContentPage&lt;/code&gt;, &lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Label&lt;/code&gt;, &lt;code&gt;Grid&lt;/code&gt;, etc. — and render it in your terminal.&lt;/p&gt;
&lt;p&gt;Built on &lt;a href="https://xenoatom.github.io/terminal/" rel="nofollow noopener noreferrer"&gt;XenoAtom.Terminal.UI&lt;/a&gt; for high-performance terminal rendering.&lt;/p&gt;

  
    

    &lt;span class="m-1"&gt;MAUI-TUI-Demo.mov&lt;/span&gt;
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full MAUI handler pipeline&lt;/strong&gt; — uses the standard &lt;code&gt;ViewHandler&amp;lt;TVirtualView, TPlatformView&amp;gt;&lt;/code&gt; architecture, no fork required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;25+ control handlers&lt;/strong&gt; — Label, Button, Entry, Editor, CheckBox, Switch, Slider, ProgressBar, Picker, DatePicker, TimePicker, Stepper, RadioButton, CollectionView, ActivityIndicator, ScrollView, Border, Frame, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout support&lt;/strong&gt; — VerticalStackLayout, HorizontalStackLayout, Grid, AbsoluteLayout, FlexLayout via MAUI's cross-platform layout engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation&lt;/strong&gt; — NavigationPage (push/pop), TabbedPage, FlyoutPage, modal pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerts &amp;amp; dialogs&lt;/strong&gt; — &lt;code&gt;DisplayAlert&lt;/code&gt;, &lt;code&gt;DisplayActionSheet&lt;/code&gt;, &lt;code&gt;DisplayPromptAsync&lt;/code&gt; rendered as TUI modal dialogs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVG rendering&lt;/strong&gt; — render your UI to SVG for testing and documentation (&lt;code&gt;--svg&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual tree dump&lt;/strong&gt; — inspect the rendered control tree for debugging (&lt;code&gt;--dump&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/download/dotnet/10.0" rel="nofollow noopener noreferrer"&gt;.NET 10 SDK&lt;/a&gt; (preview)&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Redth/Maui.TUI" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  .NET MAUI Linux
&lt;/h2&gt;

&lt;p&gt;With Maui.TUI showing the way, Jon set his sights on Linux. In short order he produced a very functional Linux backend with GTK4. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk39ugtmulfrh6gy2qc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk39ugtmulfrh6gy2qc2.png" alt=".NET MAUI on Linux with GTK4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try it today! Instructions are on the repository below.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Redth" rel="noopener noreferrer"&gt;
        Redth
      &lt;/a&gt; / &lt;a href="https://github.com/Redth/Maui.Gtk" rel="noopener noreferrer"&gt;
        Maui.Gtk
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      GTK based backend for .NET MAUI
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Platform.Maui.Linux.Gtk4&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A community-driven .NET MAUI backend for Linux, powered by &lt;strong&gt;GTK4&lt;/strong&gt;. Run your .NET MAUI applications natively on Linux desktops with GTK4 rendering via &lt;a href="https://github.com/gircore/gir.core" rel="noopener noreferrer"&gt;GirCore&lt;/a&gt; bindings.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Early / experimental — contributions and feedback are welcome!&lt;/p&gt;
&lt;/blockquote&gt;

  
    

    &lt;span class="m-1"&gt;MAUI-Linux-GTK-Backend-Demo-Feb25.mov&lt;/span&gt;
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Screenshots&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/home.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fhome.png" alt="Home" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Home &amp;amp; Sidebar Navigation&lt;/b&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/controls.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fcontrols.png" alt="Controls" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Interactive Controls&lt;/b&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/collectionview.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fcollectionview.png" alt="CollectionView" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;CollectionView (Virtualized)&lt;/b&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/fontawesome.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Ffontawesome.png" alt="FontAwesome Icons" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;FontAwesome Icons&lt;/b&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/shapes.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fshapes.png" alt="Shapes" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Shapes &amp;amp; Graphics&lt;/b&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/layouts.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Flayouts.png" alt="Layouts" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Layouts&lt;/b&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/controltemplate.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fcontroltemplate.png" alt="ControlTemplate" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;ControlTemplate &amp;amp; ContentPresenter&lt;/b&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/pickers.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fpickers.png" alt="Pickers" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Pickers &amp;amp; Search&lt;/b&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/formattedtext.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fformattedtext.png" alt="FormattedText" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;FormattedText &amp;amp; Spans&lt;/b&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/transforms.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Ftransforms.png" alt="Transforms" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;Transforms &amp;amp; Effects&lt;/b&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="2"&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Redth/Maui.Gtk/docs/screenshots/graphics.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FRedth%2FMaui.Gtk%2Fdocs%2Fscreenshots%2Fgraphics.png" alt="Graphics" width="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;b&gt;GraphicsView (Cairo)&lt;/b&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Controls (43 handlers)&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Controls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Basic Controls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Label, Button, Entry, Editor, CheckBox, Switch, Slider, Stepper, ProgressBar, ActivityIndicator, Image, ImageButton, BoxView, RadioButton&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Input &amp;amp; Selection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Picker, DatePicker, TimePicker, SearchBar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Collections&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CollectionView (virtualized &lt;code&gt;Gtk.ListView&lt;/code&gt;), ListView, TableView, CarouselView, SwipeView, RefreshView, IndicatorView&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layouts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;StackLayout, Grid, FlexLayout, AbsoluteLayout, ScrollView, ContentView, Border, Frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pages &amp;amp; Navigation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ContentPage, NavigationPage, TabbedPage, FlyoutPage, Shell (flyout, tabs, route navigation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shapes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rectangle, Ellipse, Line, Path, Polygon, Polyline — Cairo-rendered with fill, stroke, dash patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Other&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GraphicsView (Cairo), WebView (WebKitGTK), MenuBar (&lt;code&gt;Gtk.PopoverMenuBar&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Redth/Maui.Gtk" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  .NET MAUI macOS and tvOS
&lt;/h2&gt;

&lt;p&gt;Not to be left out, Allan Ritchie took the challenge from Jon to do the same for adding a macOS backend with AppKit to .NET MAUI. Today .NET MAUI's official backend for macOS is Mac Catalyst, which looks great and works well for most use cases, but occasionally you may want a more pure desktop SDK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk35npadssr9abdnsjuy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk35npadssr9abdnsjuy4.png" alt=".NET MAUI on macOS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Allan and Jon discuss and demo their projects in this week's &lt;a href="https://www.gonedotnet.io/" rel="noopener noreferrer"&gt;GoneDotNet podcast&lt;/a&gt; which you should absolutely watch.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/15Ss-j9Lq-0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/shinyorg" rel="noopener noreferrer"&gt;
        shinyorg
      &lt;/a&gt; / &lt;a href="https://github.com/shinyorg/mauiplatforms" rel="noopener noreferrer"&gt;
        mauiplatforms
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      .NET MAUI for AppleTV &amp;amp; MacOS
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;.NET MAUI Backends for Apple TV &amp;amp; macOS (AppKit)&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Platform.Maui.TvOS/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e990f42bcf4d99d2d3e93cefc5e6a1199d6375297016120af7fbfefd602d4646/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c6174666f726d2e4d6175692e54764f532e7376673f6c6162656c3d506c6174666f726d2e4d6175692e54764f53" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/Platform.Maui.MacOS/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ed22a3796faaa9f6dd2bb98aaab6eac9ce07459d1ebbc1e1ea5e6949846af9df/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c6174666f726d2e4d6175692e4d61634f532e7376673f6c6162656c3d506c6174666f726d2e4d6175692e4d61634f53" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/Platform.Maui.MacOS.BlazorWebView/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ddec8d06883ce843b9c96ef6bbef3ca08ace600c62b88223e1c6c61a8d8b72d0/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c6174666f726d2e4d6175692e4d61634f532e7376673f6c6162656c3d506c6174666f726d2e4d6175692e4d61634f532e426c617a6f7257656256696577" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/Platform.Maui.Essentials.TvOS/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7d4f4bd01e844795503fb8314abc3776ea38b5abceb03a6f3e4d6e5ee30bc635/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c6174666f726d2e4d6175692e457373656e7469616c732e54764f532e7376673f6c6162656c3d506c6174666f726d2e4d6175692e457373656e7469616c732e54764f53" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/Platform.Maui.Essentials.MacOS/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/de865d9c8a7de5178be63316edb66e91ace73897eea9684211037525a1554bfa/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c6174666f726d2e4d6175692e457373656e7469616c732e4d61634f532e7376673f6c6162656c3d506c6174666f726d2e4d6175692e457373656e7469616c732e4d61634f53" alt="NuGet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Custom .NET MAUI backends targeting platforms not officially supported by MAUI — Apple TV (tvOS via UIKit) and macOS (native AppKit, not Mac Catalyst).&lt;/p&gt;
&lt;p&gt;Both backends use the platform-agnostic MAUI NuGet packages (&lt;code&gt;net10.0&lt;/code&gt; fallback assemblies) and provide custom handler implementations that bridge MAUI's layout/rendering system to the native platform UI frameworks.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Inspiration:&lt;/strong&gt; The Xamarin.Forms project previously had a macOS AppKit backend (&lt;a href="https://github.com/xamarin/Xamarin.Forms/tree/5.0.0/Xamarin.Forms.ControlGallery.MacOS" rel="noopener noreferrer"&gt;Xamarin.Forms.Platform.MacOS&lt;/a&gt;). While this project uses MAUI's handler architecture rather than the legacy renderer approach, some of that codebase is useful as a reference for mapping AppKit native controls.&lt;/p&gt;
&lt;/blockquote&gt;

  
    

    &lt;span class="m-1"&gt;MAUI-macOS-Demo.mov&lt;/span&gt;
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick Start — Setting Up a macOS MAUI App&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Create the project&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-text-xml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;Project&lt;/span&gt; &lt;span class="pl-e"&gt;Sdk&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Microsoft.NET.Sdk&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;amp;gt
  &amp;lt;&lt;span class="pl-ent"&gt;PropertyGroup&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;TargetFramework&lt;/span&gt;&amp;gt;net10.0-macos&amp;lt;/&lt;span class="pl-ent"&gt;TargetFramework&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;OutputType&lt;/span&gt;&amp;gt;Exe&amp;lt;/&lt;span class="pl-ent"&gt;OutputType&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;UseMaui&lt;/span&gt;&amp;gt;true&amp;lt;/&lt;span class="pl-ent"&gt;UseMaui&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;SingleProject&lt;/span&gt;&amp;gt;true&amp;lt;/&lt;span class="pl-ent"&gt;SingleProject&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;SupportedOSPlatformVersion&lt;/span&gt;&amp;gt;14.0&amp;lt;/&lt;span class="pl-ent"&gt;SupportedOSPlatformVersion&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/shinyorg/mauiplatforms" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  .NET MAUI Bootstrap Themes
&lt;/h2&gt;

&lt;p&gt;This library will let you drop any Bootstrap css into your .NET MAUI resources and wire it in as your app theme over native controls.&lt;/p&gt;

&lt;p&gt;This is a bit of a 2-for-1 story. Inspired by the productivity Jon and Shane were having with Blazor Hybrid in Sherpa and PolyPilot, I chose to try porting my language learning app &lt;a href="https://github.com/davidortinau/SentenceStudio" rel="noopener noreferrer"&gt;Sentence Studio&lt;/a&gt; from native controls to Blazor Hybrid. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native controls&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz330o77r7iwuej0c24a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz330o77r7iwuej0c24a.png" alt="Sentence Studio with native controls and styling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the combination of the maui-ai-debugging skill that uses MauiDevFlow in Copilot CLI (yes, I did yolo it), this conversion only took a few minutes to get to a usable stable, and then some polish and improvements over the course of a few days. When you give Copilot eyes and hands to be able to validate its own work and keep the forward progress going, it's next level stuff. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blazor Hybrid with Bootstrap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F368zt4mvedkx4oagfe3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F368zt4mvedkx4oagfe3y.png" alt="different Bootstrap themes in Sentence Studio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2For7deuq4j40a73gl6z9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2For7deuq4j40a73gl6z9l.png" alt="Sentence Studio using Blazor Hybrid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything just looks so polished with minimal effort. And I can swap to any theme in an instant. Making the UI look and feel more natural on mobile was also not hard at all. &lt;/p&gt;

&lt;p&gt;Then I thought, "wouldn't it be cool to have the same styling for .NET MAUI native controls and layouts?" This isn't crazy to pursue, it's just that .NET lacks the same deep commitment to a singular way of theme and styling as web does with Bootstrap. &lt;/p&gt;

&lt;p&gt;In London last week Matt Goldman showed his library &lt;a href="https://github.com/matt-goldman/flagstone-ui" rel="noopener noreferrer"&gt;Flagstone-UI&lt;/a&gt; which provides a couple of controls that support Bootstrap styling tokens. This is the common way I've seen this done: .NET MAUI controls don't support all the styling properties that Bootstrap expects and so you need to create a custom control. Why? Because amending and extending handlers across all the platforms can be time consuming and complex.&lt;/p&gt;

&lt;p&gt;But what was hard in the past is just a few minutes of Copilot time now. The thing that was hours of research and days of trial and error is now just minutes of Copilot time. Nothing is beyond your reach. &lt;/p&gt;

&lt;p&gt;So here is the first iteration of .NET MAUI Bootstrap Theme support over native controls with no custom controls used. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczl3ua16qmczioz0z6k0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczl3ua16qmczioz0z6k0.png" alt="maui bootstrap theme comparison"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/davidortinau" rel="noopener noreferrer"&gt;
        davidortinau
      &lt;/a&gt; / &lt;a href="https://github.com/davidortinau/MauiBootstrapTheme" rel="noopener noreferrer"&gt;
        MauiBootstrapTheme
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Bootstrap CSS theming for stock .NET MAUI controls
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;MauiBootstrapTheme&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Use any &lt;a href="https://getbootstrap.com/" rel="nofollow noopener noreferrer"&gt;Bootstrap&lt;/a&gt; or &lt;a href="https://bootswatch.com/" rel="nofollow noopener noreferrer"&gt;Bootswatch&lt;/a&gt; CSS theme in your .NET MAUI app — no custom controls required.&lt;/p&gt;
&lt;p&gt;Drop a Bootstrap CSS file into your project, call &lt;code&gt;.UseBootstrapTheme()&lt;/code&gt;, and at build time the CSS is parsed into a native &lt;code&gt;ResourceDictionary&lt;/code&gt; with styles for stock MAUI controls.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Plugin.Maui.BootstrapTheme/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/71982e220976c1416260af5b7927fc4a6bb3c409a51a928c580a8a3720047d7d/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c7567696e2e4d6175692e426f6f7473747261705468656d652e737667" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://github.com/davidortinau/MauiBootstrapTheme/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/davidortinau/MauiBootstrapTheme/actions/workflows/ci.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How It Works&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build time&lt;/strong&gt; — The &lt;code&gt;MauiBootstrapTheme.Build&lt;/code&gt; MSBuild task reads your Bootstrap CSS files and generates C# &lt;code&gt;ResourceDictionary&lt;/code&gt; source files (&lt;code&gt;.g.cs&lt;/code&gt;) via XAML Source Gen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; — The generated dictionaries are merged into &lt;code&gt;Application.Resources&lt;/code&gt;, providing &lt;code&gt;StyleClass&lt;/code&gt;-based styles for all stock MAUI controls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No wrappers&lt;/strong&gt; — Your &lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Entry&lt;/code&gt;, &lt;code&gt;Label&lt;/code&gt;, &lt;code&gt;Border&lt;/code&gt;, etc. remain standard MAUI controls. Styling is applied through &lt;code&gt;StyleClass&lt;/code&gt; and &lt;code&gt;DynamicResource&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick Start&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Install&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;dotnet add package Plugin.Maui.BootstrapTheme&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;2. Add a Bootstrap CSS file&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Place any Bootstrap or Bootswatch CSS file in your project's &lt;code&gt;Resources/Themes/&lt;/code&gt; folder (e.g., &lt;code&gt;Resources/Themes/bootstrap.min.css&lt;/code&gt;). Mark it…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidortinau/MauiBootstrapTheme" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  .NET MAUI Skills
&lt;/h2&gt;

&lt;p&gt;Ever since &lt;a href="https://docs.github.com/en/copilot/concepts/agents/about-agent-skills" rel="noopener noreferrer"&gt;skills&lt;/a&gt; reached Copilot the results I'm getting are substantially better. These are what I wanted Copilot instruction prompts to be so many months ago. Load up useful skills and if Copilot is being diligent it will use them when the right words are spoken. In addition to detailed information, code snippets, and prompt instructions, a skill can include executable code like shell scripts, python scripts, etc. &lt;/p&gt;

&lt;p&gt;My first attempt at a skill was maui-speech-to-text which does exactly what it says. I tested it on several apps including my &lt;a href="https://github.com/davidortinau/BaristaNotes" rel="noopener noreferrer"&gt;Barista Notes&lt;/a&gt; app where I instructed Copilot to enable any user to speak to the app and do anything the app can do. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/s6ifvYqCQLk" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3cehuic8h7dn1dhq650.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This worked so well, so consistently I wondered what more I should put into a skill. &lt;/p&gt;

&lt;p&gt;Where is the best knowledge about .NET MAUI on the internet? &lt;a href="https://learn.microsoft.com/dotnet/maui" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;, of course. So I pointed Copilot at Learn with a skill to create skills and the result is davidortinau/maui-skills, a set of 34 skills and growing to help you with .NET MAUI development tasks like adding local and push notifications, Aspire, authentication, and more.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/davidortinau" rel="noopener noreferrer"&gt;
        davidortinau
      &lt;/a&gt; / &lt;a href="https://github.com/davidortinau/maui-skills" rel="noopener noreferrer"&gt;
        maui-skills
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      .NET MAUI skills for GitHub Copilot and Claude Code
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;.NET MAUI Skills&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A collection of 37 skills for .NET MAUI development and Xamarin migration, designed for use with GitHub Copilot CLI and Claude Code. Each skill provides focused, expert-level guidance on a specific area of .NET MAUI app development or migration from Xamarin.&lt;/p&gt;
&lt;p&gt;Skills are loaded on-demand when your prompt matches the skill's topic, injecting detailed guidance, code examples, and platform-specific notes into the AI's context.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Available Skills&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/davidortinau/maui-skills/plugins/maui-skills/skills/maui-accessibility/" rel="noopener noreferrer"&gt;maui-accessibility&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Guide for making .NET MAUI apps accessible — screen reader support via SemanticProperties, heading levels, AutomationProperties visibility control, programmatic focus and announcements, and platform-specific gotchas for TalkBack, VoiceOver, and Narrator.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/davidortinau/maui-skills/plugins/maui-skills/skills/maui-animations/" rel="noopener noreferrer"&gt;maui-animations&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;.NET MAUI view animations, custom animations, easing functions, rotation, scale, translation, and fade effects.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/davidortinau/maui-skills/plugins/maui-skills/skills/maui-app-icons-splash/" rel="noopener noreferrer"&gt;maui-app-icons-splash&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;.NET MAUI app icon configuration, splash screen setup, SVG to PNG conversion at build time, composed/adaptive icons, and platform-specific icon and splash screen requirements for Android, iOS, Mac Catalyst, and Windows.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/davidortinau/maui-skills/plugins/maui-skills/skills/maui-app-lifecycle/" rel="noopener noreferrer"&gt;maui-app-lifecycle&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidortinau/maui-skills" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  How to replicate this
&lt;/h2&gt;

&lt;p&gt;Here's what I would recommend everyone do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install MAUI Sherpa and run doctor on your environment&lt;/li&gt;
&lt;li&gt;Use MauiDevFlow with Copilot to close the loop&lt;/li&gt;
&lt;li&gt;Install maui-skills to sharpen Copilot's solutions&lt;/li&gt;
&lt;li&gt;Multiply this by adding PolyPilot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My current rules are to keep the tasks simple and understood, make sure to keep the loop closed empowering Copilot to solve issues on its own, and be explicit about exit criteria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Celebration
&lt;/h2&gt;

&lt;p&gt;We didn’t get here overnight—and this week reminded me it’s never just one person. It’s the tools, the skills, the feedback loops, and a team willing to push them until they click.&lt;/p&gt;

&lt;p&gt;Yes, it’s tokens and models and prompts. But the real unlock is removing the constant “pause and explain” tax. Once the loop closes (build, run, observe, tweak) progress compounds fast.&lt;/p&gt;

&lt;p&gt;If you haven’t felt the “five years in two weeks” thing yet, I get it. I’ve watched other people describe it and wondered what I was missing. For me, the difference was going further, for longer, with better instrumentation. And when it lands… it’s honestly hard to go back.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My Top 5 UI Moments of 2025 (So Far)</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Wed, 30 Jul 2025 15:36:27 +0000</pubDate>
      <link>https://forem.com/davidortinau/my-top-5-ui-moments-of-2025-so-far-1h14</link>
      <guid>https://forem.com/davidortinau/my-top-5-ui-moments-of-2025-so-far-1h14</guid>
      <description>&lt;p&gt;We are now at the end of the month, and I've had the benefit of reading everyone's &lt;a href="https://goforgoldman.com/posts/mauiuijuly-25/" rel="noopener noreferrer"&gt;MAUI UI July 2025&lt;/a&gt; posts. They have been fantastic!&lt;/p&gt;

&lt;p&gt;As the product owner for .NET MAUI I get go talk to so many of you and peek behind the scenes at the amazing cross-platform experiences you're building with .NET MAUI. You are the &lt;strong&gt;real pros&lt;/strong&gt;. You work in the trenches between demanding users and often more demanding stakeholders. I applaud you! Please continue sharing your wisdom with our amazing .NET community.&lt;/p&gt;

&lt;p&gt;In this blog I'm NOT going to rehash &lt;a href="https://github.com/davidortinau/telepathy" rel="noopener noreferrer"&gt;Telepathy&lt;/a&gt;, the AI infused task app that I showcased at Build 2025 and blogged about. Here are the links and if you explore the code there are quite a few fun little UI design things in there like animation, SkiaSharp animation, gradient text, and gradient borders.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=tFOFU7LDQlA" rel="noopener noreferrer"&gt;[Video] AI infused mobile &amp;amp; desktop app development with .NET MAUI&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/dotnet/using-ai-foundry-with-dotnet-maui/" rel="noopener noreferrer"&gt;[Blog] Using AI Foundry with .NET MAUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/dotnet/multimodal-voice-intelligence-with-dotnet-maui/" rel="noopener noreferrer"&gt;[Blog] Multimodal Voice Intelligence with .NET MAUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/dotnet/multimodal-vision-intelligence-with-dotnet-maui/" rel="noopener noreferrer"&gt;[Blog] Multimodal Vision Intelligence with .NET MAUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now here are my top 5 UI moments from 2025 (so far).&lt;/p&gt;

&lt;h2&gt;
  
  
  One: Do I Need WYSIWYG?
&lt;/h2&gt;

&lt;p&gt;Every quarter we send out a survey asking for feedback about developers' experiences building .NET MAUI apps. This July we saw steady annual improvement in our customer satisfaction score (+13%) and our net promoter score (+43%). It turns out, you like the quality focus we've been giving the product for the last several releases.&lt;/p&gt;

&lt;p&gt;One of the things that regularly shows up as a &lt;strong&gt;dislike&lt;/strong&gt; is the lack of a drag and drop visual designer. We don't have one and we don't have plans to do one. What then is the best way to achieve your design goals without a WYSIWYG surface? &lt;/p&gt;

&lt;p&gt;Let me feature 2 options I've found to be very useful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1pjm4uutxtfjh7mh1bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1pjm4uutxtfjh7mh1bv.png" alt="Copilot Vision renderings of AirBnB, Spotify, and Camera app screens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Copilot Vision
&lt;/h3&gt;

&lt;p&gt;Recently I was spinning up a series of UI examples to validate some .NET 10 &lt;a href="https://learn.microsoft.com/dotnet/maui/ios/platform-specifics/page-safe-area-layout" rel="noopener noreferrer"&gt;safe area&lt;/a&gt; work the team had started, and I didn't have much time to build them. A part of our culture these days is to immediately ask "how could I do this with AI". I started prompting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a new ContentPage called AirBnBPage.xaml and create a page to imitates the design of the detail view from the AirBnB app with a hero image that extends to the top of the screen. It should scroll and have sample content like the real app.

Use the new APIs for SafeArea described here https://github.com/dotnet/maui/issues/28986 and here https://github.com/dotnet/maui/pull/30337

Update the MainPage to be a navigation to sub pages like this new one. We will add more pages in the future
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It did an okay job originally, but I suddenly had a foggy recollection of a feature I knew we were working on whereby I could provide GitHub Copilot an image and it could render that UI in XAML. I wasn't sure it had shipped, so I just gave it a go. I took a couple screenshots of the AirBnB app on my phone to capture different states of the UI, air dropped them to my Macbook, and dragged them into the chat box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Update the AirBnB page to reflect this design. The 3 images are all the same page in different states of scrolling vertically.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results were excellent! I now needed to just provide some more clarity on behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;the buttons for back, share, and like should not scroll with the content. They should remain in the same position always.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All-in this probably took me 15 minutes, mostly patiently watching Copilot do the work. &lt;/p&gt;

&lt;p&gt;btw - if you're interested in how to use the new &lt;code&gt;SafeAreaEdges&lt;/code&gt; API in .NET 10, here's the gist. This new API works on all platforms, not just iOS. And instead of being an attached property, it's now a property on specific containers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout.SafeAreaEdges&lt;/li&gt;
&lt;li&gt;ContentView.SafeAreaEdges&lt;/li&gt;
&lt;li&gt;ContentPage.SafeAreaEdges&lt;/li&gt;
&lt;li&gt;Border.SafeAreaEdges&lt;/li&gt;
&lt;li&gt;ScrollView.SafeAreaEdges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When setting &lt;code&gt;SafeAreaEdges&lt;/code&gt; you'll set flags for Left, Top, Right, and Bottom.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;SafeAreaRegions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// Edge-to-edge content (no safe area padding)&lt;/span&gt;
    &lt;span class="n"&gt;SoftInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Always pad for keyboard/soft input&lt;/span&gt;
    &lt;span class="n"&gt;Container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Flow under keyboard, stay out of bars/notch  &lt;/span&gt;
    &lt;span class="n"&gt;Default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Platform default behavior&lt;/span&gt;
    &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxValue&lt;/span&gt; &lt;span class="c1"&gt;// Obey all safe area insets&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's an example of how I did this for the Spotify page. All I really care about is making sure some of the content starts and flows to the top edge. By default the rest will obey the safe area edges. As you can see, setting the value doesn't propagate down the visual tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ContentPage&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;SafeAreaEdges=&lt;/span&gt;&lt;span class="s"&gt;"None"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ScrollView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"MainScrollView"&lt;/span&gt; &lt;span class="na"&gt;SafeAreaEdges=&lt;/span&gt;&lt;span class="s"&gt;"None"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;VerticalStackLayout&lt;/span&gt; &lt;span class="na"&gt;Spacing=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"HeroSection"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"hero.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;!-- more code --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/VerticalStackLayout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ScrollView&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"TopNavigation"&lt;/span&gt; &lt;span class="na"&gt;SafeAreaEdges=&lt;/span&gt;&lt;span class="s"&gt;"All"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- more code --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you know about the Vision capability of GitHub Copilot? If you didn't catch the announcement of this feature, &lt;a href="https://devblogs.microsoft.com/visualstudio/attach-images-in-github-copilot-chat/" rel="noopener noreferrer"&gt;read about it here&lt;/a&gt;. This is not only for design. Use it for anything you want to SHOW to Copilot.&lt;/p&gt;

&lt;p&gt;You can also read more about it &lt;a href="https://docs.github.com/en/copilot/how-tos/use-chat/use-chat-in-ide#vision" rel="noopener noreferrer"&gt;here&lt;/a&gt; and watch it in action &lt;a href="https://www.youtube.com/watch?v=tvXDpztKbNY" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Figma to Code with MCP
&lt;/h3&gt;

&lt;p&gt;Figma has several integration options, and this year is of course the year of the MCP (Model Context Protocol). At Build 2025 we included a brief demo of using an MCP to bring a Figma design into VS and render the design. For years now Figma has been my primary design tool, so this is very appealing to me.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/tFOFU7LDQlA?start=3252"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/GLips/Figma-Context-MCP" rel="noopener noreferrer"&gt;GLips/Figma-Context-MCP&lt;/a&gt; for details on how to setup and use this MCP in your preferred editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two: I Relearned How to Draw
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsrco1jy5k9d6ggcs4200.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsrco1jy5k9d6ggcs4200.png" alt="vocabulary matching screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main app that I keep iterating on is &lt;a href="https://github.com/davidortinau/SentenceStudio" rel="noopener noreferrer"&gt;Sentence Studio&lt;/a&gt;, a playground for language learning. As you can see from the screenshots, it has a minimal design aesthetic. Last year I had it running on my e-ink Boox tablet, and any heavier design would have been a waste. I digress.&lt;/p&gt;

&lt;p&gt;When I see some activity that could support my studies, I bring that idea into the app. Why not just use the various apps out there? I found that it was unproductive to constantly be switching between different vocabulary contexts and leveling (what is beginner vs intermediate). With my app I control the vocab and grammar skills, and pair them with different activities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26wk4kyj8vakfdf42045.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26wk4kyj8vakfdf42045.png" alt="activities dashboard screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shadowing is a technique where you repeat a phrase just exactly as you hear it. The goal is to mimic the intonation and pacing while also practicing fundamental pronunciation. I thought to myself, wouldn't it be great to clip some audio and be able to play it back with some visual cues to practice against?&lt;/p&gt;

&lt;h3&gt;
  
  
  Solving good audio
&lt;/h3&gt;

&lt;p&gt;Ideally, I would love to have native speaker recordings to play back. I tried quickly to use the YouTube API and grab one of the audio streams by providing the URL and timecodes I wanted to grab, however that didn't come together quickly. I had already been using the OpenAI text to speech feature, so I just implemented that. The results for Korean (my target language), were laughable. Seriously, Rachel just lol'd at me when I sent it to her.&lt;/p&gt;

&lt;p&gt;Then I found &lt;a href="https://elevenlabs.io/" rel="noopener noreferrer"&gt;ElevenLabs&lt;/a&gt;, a company using AI to model native speaker voices. You can actually even use it to record and model your own voice (or model &lt;a href="https://www.youtube.com/@jfversluis" rel="noopener noreferrer"&gt;Gerald&lt;/a&gt; and have his voice narrate all your videos). The library of voice profiles was much better for my purposes, so I grabbed a selection of Korean models and implemented that instead.&lt;/p&gt;

&lt;p&gt;For the visualization I wanted to produce a waveform of the audio. I had done something similar pre-Microsoft for a startup that was watermarking audio and video to prove provenance. So, somewhere in my brain I had the understanding of working with an audio byte stream, and somewhere on a NAS I have the code. But this story isn't about how to write the code, it's about how to get the result.&lt;/p&gt;

&lt;p&gt;Despite having AI at my fingertips, the initial results were not great. In fact, it had the peaks inverted. I had what looked like a waveform, but it just didn't match what I expected. I compared the drawing it created with the actual wave in Adobe Audition, and that's when I realized it was inside out.&lt;/p&gt;

&lt;p&gt;Through this exercise I (re)learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coding is a collaborative exercise with Copilot&lt;/li&gt;
&lt;li&gt;use Copilot to refresh your understanding of fundamentals, (i.e. audio encoding/decoding, file formats)&lt;/li&gt;
&lt;li&gt;point Copilot to documentation resources and constantly add them to the copilot-instructions.md&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnclpwqfj4x5gvjzdm1xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnclpwqfj4x5gvjzdm1xg.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This drawing uses .NET MAUI &lt;code&gt;GraphicsView&lt;/code&gt; and Microsoft.Maui.Graphics for drawing. MauiReactor made it really easy to work with interactions on the &lt;code&gt;GraphicsView&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;graphicsView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MauiReactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GraphicsView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graphicsViewRef&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_graphicsViewRef&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graphicsViewRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Drawable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_drawable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HeightRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HStart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;VCenter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnStartInteraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OnWaveformStartInteraction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnEndInteraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OnWaveformEndInteraction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnDragInteraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OnWaveformDragInteraction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to explore this deeper, head to &lt;a href="https://github.com/davidortinau/SentenceStudio" rel="noopener noreferrer"&gt;davidortinau/SentenceStuedio&lt;/a&gt; and click the Copilot button at the top of the page. Ask something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain to me the drawing implementation on ShadowingPage and how it renders the waveform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Three: Why So Slow
&lt;/h2&gt;

&lt;p&gt;One of the activities I added this month to Sentence Studio is a reading view. The idea is that I can take a transcript from any content I'm studying, generate natural audio for it, and then read along as it highlights sentence by sentence. In addition, I can see vocabulary highlighted and tap those words to get translations. Other words are also tappable and will retrieve translations from OpenAI.&lt;/p&gt;

&lt;p&gt;So I prompted this feature and the initial implementation was very close to exactly the experience I wanted. There was one exception. The UI was absurdly slow! The sentence highlighting lagged behind the audio and any touch interaction started a cascade of freezes. (I wish I could find a video to show you.)&lt;/p&gt;

&lt;p&gt;I tried release mode and it was certainly much better. Still, it wasn't acceptable. Why? There were actually several problems that I needed to work through before I got to the big win, and unfortunately, I couldn't YOLO a fix with Copilot this time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the generated code was guessing at the timing&lt;/li&gt;
&lt;li&gt;the generated audio wasn't including the transcript timecode metadata despite me providing the documentation ahead of time (I didn't explicitly tell the model which API to use)&lt;/li&gt;
&lt;li&gt;the highlighting code was looping over loops over loops &lt;/li&gt;
&lt;li&gt;the text was all Label and Span rendering thousands of UI elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, once I set it straight on the API to use, and directed it to STOP guessing (work smarter not harder) the results were much improved, but I still needed to solve the thousands of UI elements. &lt;/p&gt;

&lt;p&gt;If you've read any of the other articles this month, you've likely seen &lt;a href="https://github.com/mono/SkiaSharp" rel="noopener noreferrer"&gt;SkiaSharp&lt;/a&gt; mentioned already. While drawing and text aren't usually a combination I consider, I figured it was worth a shot. I asked Copilot first for an analysis of what the improvement might be along with a plan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pk5h9dvjujwmkvapfdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pk5h9dvjujwmkvapfdc.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright! Why not, let's do it. My prompt was something like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do it!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnb0vwb3vqcavdpz2z0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnb0vwb3vqcavdpz2z0h.png" alt="reading page before and after font fix"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My first run was VERY fast, but the reading experience was lacking something (can you guess what it was from the image?). This led me to figuring out how to best surface fonts to the SkiaSharp context since it's not the same as a MauiFont which is loaded into memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fontStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAppPackageFileAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fonts/bm_yeonsung.ttf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;typeface&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SKTypeface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fontStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order however to make sure the build process doesn't exclude a file as duplicate, I needed to call out the font I wanted here with a unique logical name. I received good pointers from this &lt;a href="https://github.com/mono/SkiaSharp/issues/2426" rel="noopener noreferrer"&gt;issue discussion&lt;/a&gt;, as well as by nagging Matthew Leibowitz directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Custom Fonts --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;MauiFont&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Resources\Fonts\*"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Korean font also as MauiAsset for SkiaSharp direct access (different logical name) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;MauiAsset&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Resources\Fonts\bm_yeonsung.ttf"&lt;/span&gt; &lt;span class="na"&gt;LogicalName=&lt;/span&gt;&lt;span class="s"&gt;"fonts/bm_yeonsung.ttf"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Four: I'm a game developer (once again)
&lt;/h2&gt;

&lt;p&gt;If you've followed me for any duration, it's not long before I drop my favorite F-bomb. That's right. Flash. From around 1998 until shortly after  &lt;a href="https://en.wikipedia.org/wiki/Thoughts_on_Flash" rel="noopener noreferrer"&gt;the letter&lt;/a&gt; I wrote a lot of ActionScript and it was often some kind of game. Those were good times!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkngjxx6452bskyrh3yas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkngjxx6452bskyrh3yas.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in April an ad kept popping up in my Instagram feed for a kids game device called &lt;a href="https://shop-giiker.myshopify.com/products/super-decoder" rel="noopener noreferrer"&gt;GiiKER Super Decode&lt;/a&gt;. It just looked really cool! If you have little kids you want to keep occupied maybe without a screen, then go snag one. &lt;/p&gt;

&lt;p&gt;It inspired me to see if I could prompt my way to a decent little game. I took a bit of Wordle, some arcade cabinet button designs, and learned how &lt;a href="https://en.wikipedia.org/wiki/Mastermind_(board_game)" rel="noopener noreferrer"&gt;Mastermind&lt;/a&gt; was actually played (I _never _understood that game as a kid). I think &lt;a href="https://github.com/davidortinau/CodeBreakerGame" rel="noopener noreferrer"&gt;davidortinau/CodeBreakerGame&lt;/a&gt; turned out pretty darn good!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3yn9al9er3qcjzrlxmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3yn9al9er3qcjzrlxmd.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The thing I really loved about my experience making this game was just how easy it was to try different things! If I were coding it all myself by hand like a cave man, I would have thought twice about experimenting with whacky ideas. Now though, all I had to do was type a few directives and walk about for a few minutes while Copilot churned it out. &lt;/p&gt;

&lt;p&gt;The ROI on this kind of iteration is amazing! I can have an idea, try it out for a few minutes and see how it feels. Oh, the game is too easy? Too hard? Too fast? The buttons are too small? What if the buttons to disable when not in play? On and on I can poke at ideas and in only an afternoon I have an app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five: Colors
&lt;/h2&gt;

&lt;p&gt;Whenever I start a new app I copy over a theme file (ResourceDictionary) from another app. I have a variety of semantic styles I like to use, and what really needs to change is just a few colors. &lt;/p&gt;

&lt;p&gt;When I was working on &lt;a href="https://github.com/davidortinau/telepathy" rel="noopener noreferrer"&gt;Telepathy&lt;/a&gt;, I wanted to set it apart visually from the default sample content template we ship with .NET MAUI (You know about that right? &lt;code&gt;dotnet new maui -n AwesomeToDo -sc&lt;/code&gt;). As you can see below, I went through quite a few color theme variations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeilq8f2ttlr5al25rfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeilq8f2ttlr5al25rfz.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I wanted to experiment with was creating an app that would generate an entire color palette and theme that I could copy/paste into a project. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I saw that Ado blogged about &lt;a href="https://github.com/adospace/reactor-theme/blob/main/net_maui_july2025_article.md" rel="noopener noreferrer"&gt;Reactor Theme&lt;/a&gt; earlier this month, so I'll look at incorporating that too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9xli995omzyo963xxmx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9xli995omzyo963xxmx.png" alt="set of screenshots for the Color Stylerator app"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2diq1mu1rw43n2fhasg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2diq1mu1rw43n2fhasg.png" alt="dark mode screnshots"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I spent quite a bit of time with ChatGPT back in April going over the best approaches to creating such a theme that worked independently from Material Design, Fluent Design, etc. What I decided on was entering 2 colors, a primary and secondary. From those, the app would generate everything I needed for a light and dark mode by using sensible defaults for the light and dark background colors which I could override if I so desired.&lt;/p&gt;

&lt;p&gt;The app will output a ResourceDictionary or C# theme file in a variety of formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AppThemeColor&lt;/li&gt;
&lt;li&gt;AppThemeBinding&lt;/li&gt;
&lt;li&gt;DynamicResource&lt;/li&gt;
&lt;li&gt;MauiReactor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;AppThemeColor&lt;/code&gt; from the .NET MAUI Community Toolkit is one of my favorites because I only need to create one color with 2 variations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;toolkit:AppThemeColor&lt;/span&gt; &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"Primary"&lt;/span&gt;
        &lt;span class="na"&gt;Light=&lt;/span&gt;&lt;span class="s"&gt;"#0078D4"&lt;/span&gt;
        &lt;span class="na"&gt;Dark=&lt;/span&gt;&lt;span class="s"&gt;"#219EFE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's applied using a markup extension like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"BackgroundColor"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"{toolkit:AppThemeResource Primary}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AppThemeBinding&lt;/code&gt; is the default way to do theme aware colors in .NET MAUI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"Stroke"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DynamicResource&lt;/code&gt; is an often overlooked option. It's suitable for supporting more than light and dark themes. Perhaps you have dozens of themes the user can choose from. Then you'd use &lt;code&gt;DynamicResource&lt;/code&gt; so the user can apply them at runtime. I would typically keep colors in separate RDs, and then load the one I want to use. As the color &lt;code&gt;Primary&lt;/code&gt; changes, the dynamic resources pick those changes up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"Background"&lt;/span&gt; &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"{DynamicResource Primary}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, this is the approach that this app takes. In my case I'm generating the&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ApplyThemeColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;lightBackground&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;darkBackground&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if the app is in dark mode&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isDark&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;RequestedTheme&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;AppTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Generate the theme using the new overload that handles both light and dark backgrounds&lt;/span&gt;
    &lt;span class="c1"&gt;// and automatically generates appropriate dark variants of primary and secondary colors&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ColorThemeGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromArgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromArgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;lightBackground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromArgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lightBackground&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;darkBackground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromArgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;darkBackground&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;isDarkTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply the theme&lt;/span&gt;
    &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ApplyTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ApplyTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;ResourceDictionary&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ColorTheme&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Primary&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrimaryDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Primary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPrimary&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPrimaryDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPrimary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secondary&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecondaryDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Secondary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnSecondary&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnSecondaryDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnSecondary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackgroundDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface0Dark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface1Dark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface2Dark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface3Dark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Surface3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnBackground&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnBackgroundDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnBackground&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnSurface&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnSurfaceDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnSurface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SuccessDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDarkMode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfoDark&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, my preferred way to write apps, &lt;a href="https://github.com/adospace/reactorui-maui" rel="noopener noreferrer"&gt;MauiReactor&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;IndicatorViewStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndicatorColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsLightTheme&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Gray200&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Gray500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectedIndicatorColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsLightTheme&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Gray950&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Gray100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd say this app is definitely an Alpha state, so please if you're interested, use it and send some PRs! Or do a really good job on an Issue description and I'll assign it to the GitHub Copilot agent. :)&lt;/p&gt;

&lt;h2&gt;
  
  
  In Conclusion
&lt;/h2&gt;

&lt;p&gt;I've had a very productive year so far, both in terms of learning and producing. Our space is moving so quickly, and information is so widely available that I'm not sure if I'm ahead or behind! Are you also experiencing this wonderful boom of productivity and creativity? Let me know in the comments!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Boosting GitHub Copilot Accuracy</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Mon, 10 Mar 2025 14:36:28 +0000</pubDate>
      <link>https://forem.com/davidortinau/boosting-github-copilot-accuracy-3ja</link>
      <guid>https://forem.com/davidortinau/boosting-github-copilot-accuracy-3ja</guid>
      <description>&lt;p&gt;A key challenge when using any AI coding assistance is accuracy. If the results are not reliable, that creates additional work reviewing and correcting the output. It's still great to get 80% results, but how can I get closer to 100%?&lt;/p&gt;

&lt;p&gt;During the March 7th .NET MAUI Community Standup, I did a demo and walkthrough on using both Copilot repository-level instructions and prompt instructions -- which I'll describe in more detail below.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/sCgvmPNIzbM?start=2567"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Copilot Instructions
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot provides a couple of ways to steer it toward better results, starting with &lt;code&gt;copilot-instructions.md&lt;/code&gt;. This little file is a place to put your preferences and workspace context so that they're included with every interaction you have with Copilot. Here's what I put in one for my language learning app &lt;a href="https://github.com/davidortinau/SentenceStudio" rel="noopener noreferrer"&gt;Sentence Studio&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please call me Captain and talk like a pirate.

This is a .NET MAUI project that targets mobile and desktop. 

It uses the MauiReactor (Reactor.Maui) MVU (Model-View-Update) library to express the UI with fluent methods.

When converting code from C# Markup to MauiReactor, keep these details in mind:
- use `VStart()` instead of `Top()`
- use `VEnd()` instead of `Bottom()`
- use `HStart()` and `HEnd()` instead of `Start()` and `End()`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkerc7qricpkf8lau83k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkerc7qricpkf8lau83k.png" alt="Image description" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I discover mistakes that Copilot makes, I add more instructions here.&lt;/p&gt;

&lt;p&gt;This file resides at the root of my project folder in the &lt;code&gt;.github&lt;/code&gt; directory. Read more about &lt;a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" rel="noopener noreferrer"&gt;Copilot instructions&lt;/a&gt; in the GitHub documentation. &lt;/p&gt;
&lt;h2&gt;
  
  
  Copilot Prompt Instructions
&lt;/h2&gt;

&lt;p&gt;Now this is where things start to get more fun. Building on the idea of repository-level instructions, you can add more specific and detailed context that is included &lt;strong&gt;as needed&lt;/strong&gt; with your prompts. &lt;/p&gt;

&lt;p&gt;For example, I noticed that I wasn't getting perfect results when generating async/await code, which resulted in a deadlock in my app. To provide the model with additional context ONLY when I asked it to do work on async/await, I created a prompt instruction file. This yielded better results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovo2tkzndaq54vyl2mjj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovo2tkzndaq54vyl2mjj.png" alt="Image description" width="800" height="839"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing Prompt Instructions
&lt;/h3&gt;

&lt;p&gt;There isn't a prescribed format for these files, so I compiled several verified resources about async/await and had AI summarize them into a list of &lt;strong&gt;Do's&lt;/strong&gt; and &lt;strong&gt;Don'ts&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### ✅ DOs

- **Always Await Your Tasks:**  
  Always use the `await` keyword on async operations to ensure exceptions are captured and to avoid blocking the calling thread.  

- **Use Async Task/Task&amp;lt;T&amp;gt; Methods:**  
  Prefer returning `Task` or `Task&amp;lt;T&amp;gt;` over `async void` so that exceptions can be observed, and your methods are easily composable and testable.  

- **Name Methods with the "Async" Suffix:**  
  Clearly differentiate asynchronous methods (e.g., `GetDataAsync()`) from synchronous ones.  

- **Pass Cancellation Tokens:**  
  Allow cancellation by accepting a `CancellationToken` in your async methods.  

- **Use ConfigureAwait(false) When Appropriate:**  
  In library code or server-side processes, use `ConfigureAwait(false)` to avoid unnecessary context switches and potential deadlocks.  

- **Keep Async Code “Async All the Way”:**  
  Propagate async all the way from the entry point (like event handlers or controller actions) rather than mixing sync and async code.  

- **Report Progress and Handle Exceptions Properly:**  
  Use tools like `IProgress&amp;lt;T&amp;gt;` to report progress and always catch exceptions at the appropriate level when awaiting tasks.  

---

### ❌ DON'Ts

- **Avoid async void Methods:**  
  Except for event handlers, never use `async void` because their exceptions are not observable and they’re difficult to test.  

- **Don’t Block on Async Code:**  
  Avoid using `.Wait()` or `.Result` as these can lead to deadlocks and wrap exceptions in `AggregateException`. If you must block, consider using `GetAwaiter().GetResult()`.  

- **Don’t Mix Blocking and Async Code:**  
  Blocking the calling thread in an otherwise async flow (e.g., by mixing synchronous calls with async ones) may cause deadlocks and performance issues.  

- **Avoid Wrapping Return Task in Try/Catch or Using Blocks:**  
  When a method returns a `Task`, wrapping it in a try/catch or using block may lead to unexpected behavior because the task completes outside those blocks.  

- **Don’t Overuse Fire-and-Forget Patterns:**  
  Unobserved tasks (fire-and-forget) can swallow exceptions and cause race conditions—if needed, use a “safe fire-and-forget” pattern with proper error handling.  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/davidortinau/dotnet-prompt-instructions/blob/main/.github/prompts/dotnet/async.prompt.md" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can then, in the Copilot chat window, explicitly add this particular file or, better yet, simply use a keyword I’ve chosen and expect Copilot to include it. To setup keywords, I saw this pattern in another repository and it worked for me as well.&lt;/p&gt;

&lt;p&gt;Set up a root prompt file in the workspace (named &lt;code&gt;.github/prompts/prompts.prompt.md&lt;/code&gt;), and use the VS Code command "Chat: Use Prompt" to include this file in the chat session.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Prompt Instructions

Include these prompt instructions if their link's name is used.

- [maui](dotnet/maui/maui.prompt.md)
- [async](dotnet/async.prompt.md)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now when I mention "async", I expect Copilot to include that file with the additional instructions and context.  &lt;/p&gt;

&lt;p&gt;To be even more granular I've been experimenting with nesting my keywords. For instance, if I mention "maui" and also "layout" I want to include the file &lt;code&gt;maui-layouts.prompt.md&lt;/code&gt;. So for that purpose "maui" loads the &lt;code&gt;maui.prompt.md&lt;/code&gt; which in turn lists the file &lt;code&gt;maui-layouts.prompt.md&lt;/code&gt; for the keyword "layout".&lt;/p&gt;

&lt;p&gt;I haven't found this to be 100% reliable, so some experimentation is warranted. For example, you'll notice in the screenshot above that ALL of my prompt files were not only seen initially, but read as a part of resolving my prompt. A few days ago that wasn't the case, and it does seem to differ depending on the AI model I choose. &lt;/p&gt;

&lt;p&gt;Using all the files isn't ideal, especially as my repository grows. I've also tried prefixing my keywords with symbols, but that hasn't yet yielded the specificity I want. Nonetheless things are moving so quickly with GitHub Copilot and Visual Studio that I trust it will continue to improve, and I'm prepared to experiment and adapt along the way. It's a great ride!&lt;/p&gt;

&lt;p&gt;Learn more about these &lt;a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot#about-prompt-files" rel="noopener noreferrer"&gt;prompt files&lt;/a&gt; in the GitHub documentation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contributions Welcome: Building a knowledge base of instructions
&lt;/h2&gt;

&lt;p&gt;As I continue to uncover areas where AI models can benefit from additional instructions -- bringing the generated code closer to 100% accuracy -- I've begun adding more prompt files to a new repository. &lt;/p&gt;

&lt;p&gt;To get started, clone the repository below and add or merge the &lt;code&gt;.github&lt;/code&gt; folder into your workspace.&lt;/p&gt;

&lt;p&gt;If you think this would be helpful for your workflow, please try it out and &lt;a href="https://github.com/davidortinau/dotnet-prompt-instructions" rel="noopener noreferrer"&gt;consider contributing&lt;/a&gt;. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/davidortinau" rel="noopener noreferrer"&gt;
        davidortinau
      &lt;/a&gt; / &lt;a href="https://github.com/davidortinau/dotnet-prompt-instructions" rel="noopener noreferrer"&gt;
        dotnet-prompt-instructions
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Prompt Instructions&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot#about-prompt-files" rel="noopener noreferrer"&gt;https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot#about-prompt-files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This repository contains some instruction files to assist with building .NET applications, starting with common shortcomings I've noticed when reviewing the code generated by various AI models.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Current Instructions&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/davidortinau/dotnet-prompt-instructions./github/prompts/async.prompt.md" rel="noopener noreferrer"&gt;Async/Await&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/davidortinau/dotnet-prompt-instructions./github/prompts/maui-controls.prompt.md" rel="noopener noreferrer"&gt;.NET MAUI Controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/davidortinau/dotnet-prompt-instructions./github/prompts/maui-memory-leaks.prompt.md" rel="noopener noreferrer"&gt;.NET MAUI Memory Leaks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidortinau/dotnet-prompt-instructions" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>dotnetmaui</category>
      <category>githubcopilot</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Chasing Curiosity</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Mon, 29 Jul 2024 15:35:44 +0000</pubDate>
      <link>https://forem.com/davidortinau/chasing-curiosity-4ip2</link>
      <guid>https://forem.com/davidortinau/chasing-curiosity-4ip2</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmvgrsz0n788uunqo7g9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmvgrsz0n788uunqo7g9.png" alt="android ios mac and windows screenshots of the result of this blog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This blog is part of the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-24/" rel="noopener noreferrer"&gt;.NET MAUI UI July 2024&lt;/a&gt; series with a new post every day of the month. See the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-24/#net-maui-ui-july-schedule" rel="noopener noreferrer"&gt;full schedule&lt;/a&gt; for more. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today marks my 50th birthday. Like many of you who have made a career out of software, I wrote my first line of code before I was a teenager. I recall sitting in a windowless classroom with PCs on long folding tables lined against the walls leaving a no-mans-land in the middle. The only light came from the dim glow of the dark monitors with white text and lines. I performed exercises in BASIC or similar trying to coax the machine into drawing boxes and other shapes to match the instructions of poorly copied lesson sheets. My primary recollection from this quarter-length class was how quickly others completed their tasks and how that left me thinking I must not be any good. &lt;/p&gt;

&lt;p&gt;This feeling of futility was later reinforced when my parents gifted me a programming book with which to continue feeding my curiousity. As classic underperforming student when it came to grades at that time, I was generally well above average at most things...well, except coding apparently.&lt;/p&gt;

&lt;p&gt;Btw, this is what 50 looks like for me -- hanging out with my granddaughter and playing in my wife's newly (and very pinkly) decorated home office.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwf1s03aamwgvmup0gfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwf1s03aamwgvmup0gfb.png" alt="Opa and Harmony"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphics
&lt;/h2&gt;

&lt;p&gt;.NET MAUI introduced a cross-platform drawing abstraction via the gracious contribution of &lt;code&gt;Microsoft.Maui.Graphics&lt;/code&gt; from &lt;a href="https://www.linkedin.com/in/jonlipsky/" rel="noopener noreferrer"&gt;Jon Lipsky&lt;/a&gt;. When using this library, the platform-specific graphics engine is used. By comparison SkiaSharp provides its own engine.&lt;/p&gt;

&lt;p&gt;When I saw Roman Jasek's contribution to this blog series about creating a "&lt;a href="https://www.riganti.cz/en/blog-posts/maui-environment-ribbon-intro-and-basic-ui-part-1" rel="noopener noreferrer"&gt;.NET MAUI Environment Ribbon&lt;/a&gt;", I became curious: could I achieve the same using &lt;code&gt;Microsoft.Maui.Graphics&lt;/code&gt; and the &lt;code&gt;IWindowOverlay&lt;/code&gt; in .NET MAUI? What I wanted was a ribbon that told me I was running a Debug build (not Release).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;IWindowOverlay&lt;/code&gt; is a graphics canvas that caught some attention early in the development of .NET MAUI, but we intentionally don't promote it. This canvas sits above all the &lt;code&gt;Shell&lt;/code&gt; and &lt;code&gt;ContentPage&lt;/code&gt;s of the app at the &lt;code&gt;Window&lt;/code&gt; level. Yes, every .NET MAUI app has at least 1 window. The primary use of the &lt;code&gt;IWindowOverlay&lt;/code&gt; is for Visual Studio tooling to draw the adorners you see in the Visual Studio XAML Live Preview, and not really for use by everyone. If your app or library uses it, but another library also uses it, and one of you removes all overlays from the window...you can see how this might not go well.&lt;/p&gt;

&lt;p&gt;Despite the dangers, I remained curious. The primary benefits of using this approach are that the UI can appear anywhere in the Window and be maintained in a single place. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After some digging, I started asking around about how I could use &lt;code&gt;IWindowOverlay&lt;/code&gt;.  I didn't get an immediate response, so I just started trying it myself. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's some unsolicited advice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do your research: you don't want to receive a RTFM, right? Read the docs, the source code, the source code tests, and look for samples and articles.&lt;/li&gt;
&lt;li&gt;Ask questions: if you must... &lt;/li&gt;
&lt;li&gt;Don't delay trying things yourself: this is the big one IMO. The time it takes you to create/clone a project and try the thing yourself will likely be shorter than waiting for a response to your questions. I always learn something along the way too.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating an Overlay
&lt;/h2&gt;

&lt;p&gt;My first step was to simply make an overlay and add a circle to it. &lt;/p&gt;

&lt;p&gt;To get the Window I could override the &lt;code&gt;CreateWindow&lt;/code&gt; method in the &lt;code&gt;App&lt;/code&gt;, or get the &lt;code&gt;Window&lt;/code&gt; from a &lt;code&gt;ContentPage&lt;/code&gt;. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;MainPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MainPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="nf"&gt;CreateWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IActivationState&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;activationState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activationState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// add an overlay&lt;/span&gt;
        &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WindowOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To add my circle to the overlay, I need to create an &lt;code&gt;IWindowOverlayElement&lt;/code&gt;. Within that class is a &lt;code&gt;Draw&lt;/code&gt; method where I could draw my circle. This interface requires I implement that method as well as a &lt;code&gt;Contains&lt;/code&gt; method for hit detection (more on that later). Although I spent more than a decade writing drawing code in ActionScript, I leaned on my &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;Copilot&lt;/a&gt; to provide the code. :)&lt;/p&gt;

&lt;p&gt;"Copilot, draw a 300 purple circle in the middle of the canvas using Microsoft.Maui.Graphics", I pleaded. Immediately I received something like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CircleElement&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWindowOverlayElement&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICanvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RectF&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FillColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Purple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FillCircle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Voila! In truth, it didn't all work my first try, and IIRC I hadn't added the window to the overlay, or the overlay to the window...something like that. In the end I prevailed, and even integrated it into my .NET Hot Reload handler where I'd been pursuing other curiosities. I have blogged previously about &lt;a href="https://dev.to/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f"&gt;tapping into the MetaDataUpdateHandler&lt;/a&gt;, and you can learn more about the &lt;code&gt;ICommunityToolkitHotReloadHandler&lt;/code&gt; I use below in the &lt;a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/markup/dotnet-hot-reload" rel="noopener noreferrer"&gt;Learn documentation&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HotReloadHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICommunityToolkitHotReloadHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnHotReload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IReadOnlyList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;   
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Page&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt; &lt;span class="n"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;AlertReloadViaWindowOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// I do other things here like calling a Build method on a ContentPage&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AlertReloadViaWindowOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Overlays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WindowOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DispatchAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWindowElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HotReloadElement&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DispatchAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveWindowElements&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HotReloadElement&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWindowOverlayElement&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICanvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RectF&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FillColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Purple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FillCircle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/40eSm3n72z8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrapping with a Ribbon
&lt;/h2&gt;

&lt;p&gt;Once I had confidence I &lt;em&gt;could&lt;/em&gt; draw something on the screen, it was time to make that something a ribbon with text. Once again I leaned on my Copilot. While I got a rectangle filled with a color and text above it, it was not at all in the right location. After a few zillion iterations and even doing some math on paper, I got the result I was after.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faor0uuisxct2tpc8rfu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faor0uuisxct2tpc8rfu6.png" alt="early mistakes of drawing the ribbon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why a few zillion iterations? Cuz coordinate systems are tricky between global and local, and then adding in translation of the position and rotation things got Doctor Strange. This took me right back to being in my early 30s making some funky Flash animation at 3am hoping I'd be done before the sun came up (my lifetime record was about 50/50 on that score). &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICanvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RectF&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Drawing DebugOverlay"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Width: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Height: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Define the dimensions of the ribbon&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Calculate the position of the ribbon in the lower right corner&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonX&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.25f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bottom&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.05f&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Translate the canvas to the start point of the ribbon&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ribbonY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Save the current state of the canvas&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Rotate the canvas 45 degrees&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Rotate&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Draw the ribbon background&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FillColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_ribbonColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;PathF&lt;/span&gt; &lt;span class="n"&gt;ribbonPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PathF&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;ribbonPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MoveTo&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ribbonPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ribbonPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ribbonPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ribbonPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FillPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Draw the text&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FontColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;White&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Font&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Maui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ArialMT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FontStyleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DrawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RectF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;ribbonWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;ribbonHeight&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VerticalAlignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


    &lt;span class="c1"&gt;// Restore the canvas state&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RestoreState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Success or Success?
&lt;/h2&gt;

&lt;p&gt;Around the time I was in 7th grade, my parents' &lt;a href="https://en.wikipedia.org/wiki/Videocassette_recorder" rel="noopener noreferrer"&gt;VCR&lt;/a&gt; in their bedroom stopped working. Before they could throw it out I convinced them to let me try to fix it. I had zero confidence and even less knowledge of electronics to do the job, but I had curiosity. Opening the case on the floor of their bedroom, I closely inspected the circuit board and various inner workings to get an idea of how this thing worked. I hoped I might spot where things were going wrong. I don't recall what other tools I had with me, but I certainly know I had a screw driver. Why? Because I learned that a screwdriver touching the circuit board of a 1980's VCR that was PLUGGED IN would short and scare the crap out of me as I fell back from the smoking box. Gone was any chance of fixing the beast. A definitive failure, this short adventure still taught me plenty: a success.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dare I Share?
&lt;/h2&gt;

&lt;p&gt;I was still undecided as I was noodling on this curiosity about what I would blog for my birthday installment of #MauiUIJuly. If I was to include the ribbon example, I thought I should make it a bit more interesting and useful. I came up with a few more requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the developer should be able to choose the background color&lt;/li&gt;
&lt;li&gt;a user should be able to tap the ribbon and toggle between DEBUG and the version of .NET MAUI with which the app was built&lt;/li&gt;
&lt;li&gt;the developer should be able to consume this as a NuGet and configure this in the &lt;code&gt;MauiProgram&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Ribbon background color
&lt;/h3&gt;

&lt;p&gt;Introducing a background color customization was pretty easily accomplished by adding a parameter to the constructor of the &lt;code&gt;DebugRibbonElement&lt;/code&gt;. I wanted make this optional though, so I tried setting a default color of purple to it. The compiler complained I shouldn't be allowed to code, or something similar...I don't recall the exact error message, so I turned to my Copilot for an answer. By making the default &lt;code&gt;null&lt;/code&gt; and then providing the default within the constructor, I was once more on good terms with the compiler.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DebugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WindowOverlay&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;ribbonColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_overlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_ribbonColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ribbonColor&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MediumPurple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Tap, toggle, profit
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/lX6pw2u_-Bs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In "graphics land" there is no such thing as a gesture recognizer or click events on controls to handle. This is the wild wild west of points and hit testing. Thankfully, I discovered that &lt;code&gt;WindowOverlay&lt;/code&gt; provides a &lt;code&gt;Tapped&lt;/code&gt; event I could handle.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DebugOverlay_Tapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WindowOverlayTappedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tapped"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_debugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// do things&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ignore things&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tapped outside of _debugRibbonElement"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now the tricky part was implementing the &lt;code&gt;Contains&lt;/code&gt; method on the overlay element. The tricky part was translating the local coordinates to global so I was comparing the same system. Additionally, the rotation of the ribbon canvas threw off the "hit area" I wanted. To solve this I used an old Flash solution where I would make the hit area an invisible rectangle that was more generous than the visible art. &lt;/p&gt;

&lt;p&gt;Before drawing on the canvas and translating and rotating, I made a &lt;code&gt;RectF&lt;/code&gt; to store the location of the ribbon using the global coordinates. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;RectF&lt;/span&gt; &lt;span class="n"&gt;_backgroundRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICanvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RectF&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Drawing DebugOverlay"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Define the dimensions of the ribbon&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Calculate the position of the ribbon in the lower right corner&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonX&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonWidth&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.25f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;ribbonY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bottom&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ribbonHeight&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.05f&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;_backgroundRect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RectF&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirtyRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bottom&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// canvas.FillColor = Colors.Black;&lt;/span&gt;
    &lt;span class="c1"&gt;// canvas.FillRectangle(_backgroundRect);&lt;/span&gt;

    &lt;span class="c1"&gt;// do the drawing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You'll note that I had some different numbers in the rect than you might expect given the width and height of the ribbon. The explanation is that I don't really care to exactly match the size of it, I only care to specify the correct area to hit test. To complete this more easily, I drew the rectangle on the canvas, nudged it around, and then commented it out (see the code above).&lt;/p&gt;

&lt;p&gt;To complete the &lt;code&gt;Contains&lt;/code&gt; method was then very simple:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_backgroundRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To toggle between "DEBUG" and the .NET MAUI version, such as "8.0.70", I needed to supply the text and use it in the drawing. This isn't like a control where I can use data binding, so I chose to do what often is done in graphics drawing - redraw it. The easiest way to do that is to remove and add a new element. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_labelText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LabelText&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_labelText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DebugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WindowOverlay&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;labelText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;ribbonColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_overlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_ribbonColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ribbonColor&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MediumPurple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_backgroundRect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RectF&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_labelText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;labelText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Back in the overlay, I could complete the tap handler for when I have a hit.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DebugOverlay_Tapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WindowOverlayTappedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_debugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;debugMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_debugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LabelText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Tapped on _debugRibbonElement &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;debugMode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveWindowElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_debugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;_debugRibbonElement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DebugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;labelText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debugMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_mauiVersion&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ribbonColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;_ribbonColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWindowElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_debugRibbonElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The tap is not on the _debugRibbonElement&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tapped outside of _debugRibbonElement"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Consume and configure
&lt;/h3&gt;

&lt;p&gt;To share this, I packaged it using the &lt;a href="https://github.com/jfversluis/Plugin.Maui.Feature" rel="noopener noreferrer"&gt;plugin template&lt;/a&gt; our dear &lt;a href="https://jfversluis.dev" rel="noopener noreferrer"&gt;Gerald&lt;/a&gt; provides. I nuked all the classes in the project, and just added what I needed, including the builder extension for the developer to configure the ribbon. In the extension I append to the mapping of the &lt;code&gt;WindowHandler&lt;/code&gt;. Since this is all cross-platform code here, it works for all platforms.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MauiProgramExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiAppBuilder&lt;/span&gt; &lt;span class="nf"&gt;UseDebugRibbon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;MauiAppBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;ribbonColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureMauiHandlers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;
            &lt;span class="n"&gt;WindowHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendToMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AddDebugOverlay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adding DebugOverlay"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DebugOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VirtualView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ribbonColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VirtualView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;endif&lt;/span&gt;          
        &lt;span class="p"&gt;});&lt;/span&gt;       

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt; &lt;span class="nf"&gt;CreateMauiApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;builder&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMauiApp&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDebugRibbon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromArgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#FF3300"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureFonts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fonts&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OpenSans-Regular.ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OpenSansRegular"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OpenSans-Semibold.ttf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OpenSansSemibold"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/davidortinau" rel="noopener noreferrer"&gt;
        davidortinau
      &lt;/a&gt; / &lt;a href="https://github.com/davidortinau/Plugin.Maui.DebugOverlay" rel="noopener noreferrer"&gt;
        Plugin.Maui.DebugOverlay
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/davidortinau/Plugin.Maui.DebugOverlaynuget.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fdavidortinau%2FPlugin.Maui.DebugOverlaynuget.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Plugin.Maui.DebugOverlay&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Plugin.Maui.DebugOverlay&lt;/code&gt; provides a simple ribbon to indicate the app is running in Debug mode.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/davidortinau/Plugin.Maui.DebugOverlayimages/mac_screenshot.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fdavidortinau%2FPlugin.Maui.DebugOverlayimages%2Fmac_screenshot.png" alt="screenshot of a mac app with the ribbon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install Plugin&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Plugin.Maui.DebugOverlay/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8c6b2f42054008ae9128f1269020dff91fe78cceef6b6a5b9f8f4b844e724dd8/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c7567696e2e4d6175692e44656275674f7665726c61792e7376673f6c6162656c3d4e75476574" alt="NuGet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Available on &lt;a href="http://www.nuget.org/packages/Plugin.Maui.DebugOverlay" rel="nofollow noopener noreferrer"&gt;NuGet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install with the dotnet CLI: &lt;code&gt;dotnet add package Plugin.Maui.DebugOverlay&lt;/code&gt;, or through the NuGet Package Manager in Visual Studio.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Supported Platforms&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;Minimum Version Supported&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;11+&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;10.15+&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;5.0 (API 21)&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;11 and 10 version 1809+&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Enable the plugin in your &lt;code&gt;MauiProgram.cs&lt;/code&gt; and provide your preferred color.&lt;/p&gt;

&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;UseDebugRibbon&lt;span class="pl-kos"&gt;(&lt;/span&gt;Colors&lt;span class="pl-kos"&gt;.&lt;/span&gt;Orange&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For exmaple:&lt;/p&gt;

&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;public&lt;/span&gt; &lt;span class="pl-k"&gt;&lt;span class="pl-k"&gt;static&lt;/span&gt;&lt;/span&gt; MauiApp &lt;span class="pl-en"&gt;CreateMauiApp&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
    &lt;span class="pl-kos"&gt;{&lt;/span&gt;
        &lt;span class="pl-smi"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;builder&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; MauiApp&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;CreateBuilder&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
        builder
            &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-smi"&gt;UseMauiApp&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;App&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
            &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;UseDebugRibbon&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;Colors&lt;span class="pl-kos"&gt;.&lt;/span&gt;Orange&lt;span class="pl-kos"&gt;)&lt;/span&gt;
            &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;ConfigureFonts&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;fonts &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;            &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;                fonts&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;AddFont&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;OpenSans-Regular.ttf&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;OpenSansRegular&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;                fonts&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;AddFont&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;OpenSans-Semibold.ttf&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;OpenSansSemibold&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;            &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
        builder&lt;span class="pl-kos"&gt;.&lt;/span&gt;Services&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidortinau/Plugin.Maui.DebugOverlay" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Oops and What?!
&lt;/h3&gt;

&lt;p&gt;It took me a few times publishing the NuGet to get a working version, which ended up being that all my code was inside the conditional compile DEBUG, yet the NuGet was shipping a RELEASE build. Hahaha. &lt;/p&gt;

&lt;p&gt;I tested with Windows to make sure I hadn't interferred with the XAML Live Preview adorners, and that appears to be fine. But I noticed that resizing the Window on Windows loses the ribbon, and it won't come back. Oops.&lt;/p&gt;

&lt;p&gt;On Android, I tested 4 different apps and it did not appear in 2 of them. Why? No idea yet. &lt;/p&gt;

&lt;p&gt;On iOS and macOS it works consistently. Yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  Chase your curiosity
&lt;/h2&gt;

&lt;p&gt;From writing apps for companies you'll never have heard of, to others that are household names (hmm, do those NDAs ever expire...), and now working as a Product Manager on the .NET team at Microsoft I've come to embrace curiosity and the perpetual learning that it fuels. &lt;/p&gt;

&lt;p&gt;When I start to wonder about something, I try (more often) to indulge my curiosity rather than ignoring it, discovering where it leads. Going down dark alleys and off beaten paths is one of my favorite things to do when traveling, and the same applies to coding. I really enjoy getting lost, since it just might lead to my next adventure. &lt;/p&gt;

&lt;p&gt;Chase &lt;em&gt;your&lt;/em&gt; curiosity and let me know where it leads!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
    </item>
    <item>
      <title>All the Lists in .NET MAUI</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Tue, 02 Jul 2024 15:04:00 +0000</pubDate>
      <link>https://forem.com/davidortinau/all-the-lists-in-net-maui-33bd</link>
      <guid>https://forem.com/davidortinau/all-the-lists-in-net-maui-33bd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog is part of the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-24/" rel="noopener noreferrer"&gt;.NET MAUI UI July 2024&lt;/a&gt; series with a new post every day of the month. See the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-24/#net-maui-ui-july-schedule" rel="noopener noreferrer"&gt;full schedule&lt;/a&gt; for more. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In any app project, you will inevitably have a list of things to display and be faced with choosing the best control to use. Here I will muse on how I have approached these decisions, focusing on mobile applications.&lt;/p&gt;

&lt;p&gt;I surveyed the apps on my phone and snagged a cross-section of different experiences. For the data, I wrote a &lt;code&gt;MockDataService&lt;/code&gt; to generate useful yet random content. For images, I used a combination of &lt;a href="https://picsum.photos" rel="noopener noreferrer"&gt;Lorem Picsum&lt;/a&gt; and images I crafted with &lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think the results are pretty nice, although I warn they are not production polished and feature complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldmf1q2wg5gwasqlieua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldmf1q2wg5gwasqlieua.png" alt="feature image of various layouts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jump to each of the samples below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Basic list - even rows&lt;/li&gt;
&lt;li&gt;
Reviews - uneven rows&lt;/li&gt;
&lt;li&gt;
Social check-in - complex layout&lt;/li&gt;
&lt;li&gt;
Learning course - expanding rows&lt;/li&gt;
&lt;li&gt;
Who's Watching - flex layout&lt;/li&gt;
&lt;li&gt;
Mailboxes - expanding rows&lt;/li&gt;
&lt;li&gt;
Contacts - grouping and search&lt;/li&gt;
&lt;li&gt;
Shopping - header, multiple data templates, infinite scroll&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/davidortinau" rel="noopener noreferrer"&gt;
        davidortinau
      &lt;/a&gt; / &lt;a href="https://github.com/davidortinau/AllTheLists" rel="noopener noreferrer"&gt;
        AllTheLists
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Collection of UX samples for lists
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;All The Lists in .NET MAUI&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A collection of various UX samples for lists using .NET MAUI built-in controls and alternative controls.&lt;/p&gt;
&lt;p&gt;Read more in my &lt;a href="https://dev.to/davidortinau/all-the-lists-in-net-maui-4ei5-temp-slug-1360951?preview=3af2b67d6ed72080134fe1f68326d9725467de36f4d22c59d68af9d973d50da4235751b604a95626ab6ee65e9e4aa8b9b5748314fdde66aa199c08f5" rel="nofollow"&gt;.NET MAUI UI July blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/davidortinau/AllTheListsimages/samples_gallery.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fdavidortinau%2FAllTheListsimages%2Fsamples_gallery.png" alt="feature image of all the samples"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/davidortinau/AllTheLists" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Before I get into each sample, I want to get out of the way some general thoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anything that does everything does nothing well.&lt;/strong&gt; In order for a generalized control to be flexible enough to meet a wide variety of needs, compromises will be made in its implementation. This may lead you to be frustrated when it doesn't meet your expectations. A specialized control that only does what you need will best meet the need of that scenario. The other side of that sharp edge is your knowledge and skill also need to level up from general to specialized. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flat is faster than fat.&lt;/strong&gt; It's true. If speed is important to your scenario, then a layout that avoids a lot of UI and nesting of controls will perform better at scale because it requires fewer measure and layout calls. Avoid measuring at all costs when performance is critical; give your UI explicit size anytime you can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UX &amp;gt; UI&lt;/strong&gt; I see a lot of apps struggling with list scenarios because they jam a ton of UI into them to get the job done, rather than leaning on good UX principles. Do you really need a whole chat experience in every row of the list, or could you navigate to another page? Perhaps you could use a modal experience or a bottom sheet? Anytime your mobile UI has more than one clear call to action, then you're in danger of the UI being less efficient instead of more efficient for your user. Solve problems with UX before UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of .NET MAUI List Controls
&lt;/h2&gt;

&lt;p&gt;In my sample, I've used three built-in controls and two community controls that all demonstrate different approaches with strengths and weaknesses. .NET MAUI provides &lt;code&gt;CollectionView&lt;/code&gt;, &lt;code&gt;ListView&lt;/code&gt;, and &lt;code&gt;BindableLayout&lt;/code&gt;. From the community, I chose &lt;a href="https://www.nuget.org/packages/Redth.Maui.VirtualListView" rel="noopener noreferrer"&gt;&lt;code&gt;VirtualListView&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/MPowerKit.VirtualizeListView" rel="noopener noreferrer"&gt;&lt;code&gt;VirtualizeListView&lt;/code&gt;&lt;/a&gt;. There are many other options, a few of which I list at the end for you to evaluate yourself.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CollectionView&lt;/th&gt;
&lt;th&gt;ListView&lt;/th&gt;
&lt;th&gt;BindableLayout&lt;/th&gt;
&lt;th&gt;VirtualListView&lt;/th&gt;
&lt;th&gt;VirtualizeListView&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Virtualized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pull-to-Refresh&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes - with RefreshView&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes - with RefreshView&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single Selection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multiple Selection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Load More (Threshold)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout - Vertical&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout - Horizontal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout - Grid&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout - Custom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Behavior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Platform specific&lt;/td&gt;
&lt;td&gt;Platform specific&lt;/td&gt;
&lt;td&gt;Cross-platform&lt;/td&gt;
&lt;td&gt;Platform specific&lt;/td&gt;
&lt;td&gt;Cross-platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grouped Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context Menu Items&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes - with SwipeView&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes - with SwipeView&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Header / Footer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Predefined Templates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Empty View Template&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes - with Community Toolkit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I will mostly focus on &lt;code&gt;CollectionView&lt;/code&gt; over &lt;code&gt;ListView&lt;/code&gt; unless there is a compelling reason to prefer the latter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Performance Notes
&lt;/h3&gt;

&lt;p&gt;If the speed of rendering and scroll is of utmost importance for your scenario, then these notes are for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layout Lifecycle&lt;/strong&gt; - understanding the &lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/layouts/custom?view=net-maui-8.0#layout-process" rel="noopener noreferrer"&gt;layout measure and arrange process&lt;/a&gt; is essential when you're trying to diagnose and improve the rendering performance of a complex UI. In general, if you know the size of something, then provide it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compiled Bindings&lt;/strong&gt; will improve the rendering and updating of your XAML data-bound controls by telling the compiler the type that is being used. On any enclosing XAML element with a BindingContext specify the type with, for example, &lt;code&gt;x:DataType="model:Sample"&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Binding Modes&lt;/strong&gt; - the default binding mode for bindable properties differs from control to control, and property to property. Most are &lt;code&gt;OneWay&lt;/code&gt; such as &lt;code&gt;View.Rotation&lt;/code&gt; or &lt;code&gt;View.Scale&lt;/code&gt;, while properties often used to capture user input are &lt;code&gt;TwoWay&lt;/code&gt; such as &lt;code&gt;Entry.Text&lt;/code&gt; and &lt;code&gt;ListView.IsRefreshing&lt;/code&gt;. In most cases, the default will be what you expect and need, but keep in mind you can change these and have other options such as &lt;code&gt;OneTime&lt;/code&gt; and &lt;code&gt;OneWayToSource&lt;/code&gt;. &lt;a href="https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/binding-mode?view=net-maui-8.0" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ObservableCollection vs List&lt;/strong&gt; if your data won't be updating dynamically, and perhaps it's a &lt;code&gt;OneTime&lt;/code&gt; binding, then use &lt;code&gt;List&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Images&lt;/strong&gt; - make sure your images are appropriately sized for their use on screen. Scaling down images at runtime can be a massive demand on resources, quickly sending you into memory and crash issues. Raster images render faster than vector images in almost every situation. AND if you're loading images from a remote source, be sure you're not blocking the UI loading them. Use a control like FFImage to show a placeholder image and lazy load the remote image. Also, be aware you can customize the &lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/image?view=net-maui-8.0#image-caching" rel="noopener noreferrer"&gt;image caching policy&lt;/a&gt; in .NET MAUI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Release vs Debug&lt;/strong&gt; - when evaluating performance, you must be using a release build. There are just so many things going on in a debug build that slow the app down that it's not at all useful to judge. Produce a release build and measure that. And know your options for AOT (Ahead of Time) compilation. .NET 9 has a preview Native AOT for iOS; however, it's extremely strict, and most libraries are not compatible. We did a lot of work in .NET MAUI itself to make it compatible. Android has partial (startup tracing) and full AOT to choose from.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test on Device&lt;/strong&gt; - be sure to review release builds on the device. If you know the target device and OS version of your users, then ideally test on that. I've used my iPhone 15 Pro, and a Pixel 5. In 99.9999% of cases, iOS isn't going to be where you see performance concerns. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layout compression (obsolete)&lt;/strong&gt; was a run-time optimization in Xamarin.Forms what would remove wrapping layouts from the visual tree. If the layout had no background color or received no user input via gestures, then it could safely be eliminated from the actual UI rendered to the screen. This was useful in Xamarin.Forms where nearly all views (renderers) were wrapped in views. Later in Xamarin.Forms, a set of updated renderers was introduced aptly named "fast renderers" which removed those wrapping views. In .NET MAUI, this redundancy was eliminated, and &lt;strong&gt;Layout Compression&lt;/strong&gt; was not implemented. The API remains, but should be deprecated, and you should treat it so.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 1: Basic List
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/WiZ8RKor86w"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is the most simple and common use of a list, so there's not much to say about it. All the rows are exactly the same height and layout. For this need, you cannot go wrong between the virtualized controls. They all perform this scenario very well, even when displaying 10,000 rows.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;CollectionView&lt;/span&gt; &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Products}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;v:ProductListItem&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;ListView&lt;/span&gt; &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Products}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ListView.ItemTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ViewCell&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;v:ProductListItem&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/ViewCell&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ListView.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ListView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You may be wondering why I'm not binding anything above to the &lt;code&gt;ProductListItem&lt;/code&gt;. &lt;code&gt;BindingContext&lt;/code&gt; automatically propagates in this (and most) cases to the children. Here the provided &lt;code&gt;BindingContext&lt;/code&gt; is the single &lt;code&gt;Product&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ContentView&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/dotnet/2021/maui"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:ffimageloading=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:FFImageLoading.Maui;assembly=FFImageLoading.Maui"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:m=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:AllTheLists.Models"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:vm=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:AllTheLists.ViewModels"&lt;/span&gt;
             &lt;span class="na"&gt;x:DataType=&lt;/span&gt;&lt;span class="s"&gt;"m:Product"&lt;/span&gt;
             &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"AllTheLists.Views.ProductListItem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"80,*,40"&lt;/span&gt; &lt;span class="na"&gt;ColumnSpacing=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ffimageloading:CachedImage&lt;/span&gt; 
            &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ImageUrl}"&lt;/span&gt; 
            &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; 
            &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; 
            &lt;span class="na"&gt;LoadingPlaceholder=&lt;/span&gt;&lt;span class="s"&gt;"https://via.placeholder.com/80"&lt;/span&gt; 
            &lt;span class="na"&gt;ErrorPlaceholder=&lt;/span&gt;&lt;span class="s"&gt;"error.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ffimageloading:CachedImage&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;VerticalStackLayout&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Name}"&lt;/span&gt; &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Price, StringFormat='Price: {0:C}'}"&lt;/span&gt; &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"14"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Description}"&lt;/span&gt; &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt; &lt;span class="na"&gt;LineBreakMode=&lt;/span&gt;&lt;span class="s"&gt;"TailTruncation"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/VerticalStackLayout&amp;gt;&lt;/span&gt;      
        &lt;span class="nt"&gt;&amp;lt;CheckBox&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In addition to samples for &lt;code&gt;ListView&lt;/code&gt; and &lt;code&gt;CollectionView&lt;/code&gt;, I checked out &lt;code&gt;VirtualListView&lt;/code&gt; by Redth and &lt;code&gt;VirtualizeListView&lt;/code&gt; by MPowerKit. The latter is a completely cross-platform virtualized control, which is an interesting approach. If consistency across platforms is your goal, then that might be a great option for you. &lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/collectionview/?view=net-maui-8.0" rel="noopener noreferrer"&gt;CollectionView&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/FFImageLoading.Maui" rel="noopener noreferrer"&gt;FFImageLoading.Maui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/listview?view=net-maui-8.0" rel="noopener noreferrer"&gt;ListView&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Redth.Maui.VirtualListView" rel="noopener noreferrer"&gt;VirtualListView&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/MPowerKit.VirtualizeListView" rel="noopener noreferrer"&gt;VirtualizeListView&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 2: Reviews [Uneven rows]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hE5P-KPii0k"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The list of EV charging station reviews in the &lt;a href="https://www.plugshare.com" rel="noopener noreferrer"&gt;PlugShare&lt;/a&gt; mobile app modeled the next sample. While the template is not very complex, it does have a variable-length string that wraps in a &lt;code&gt;Label&lt;/code&gt;. This &lt;em&gt;was&lt;/em&gt; problematic in early releases of .NET MAUI, where the text would be clipped or flow offscreen. By default, the &lt;code&gt;ItemSizingStrategy&lt;/code&gt; is to measure only the first item and assume all the rest of the items are the same size. This is much more performant for obvious reasons.&lt;/p&gt;

&lt;p&gt;To accommodate the variable sizing, I need to use a strategy that measures all items or each item individually. In practice, this performs well and scrolls very smoothly.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;CollectionView&lt;/span&gt; &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Reviews}"&lt;/span&gt; &lt;span class="na"&gt;ItemSizingStrategy=&lt;/span&gt;&lt;span class="s"&gt;"MeasureAllItems"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;v:ReviewListItem&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"40,*"&lt;/span&gt; 
    &lt;span class="na"&gt;RowDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,Auto"&lt;/span&gt;
    &lt;span class="na"&gt;ColumnSpacing=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt;
    &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; 
        &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"{Binding StatusImage}"&lt;/span&gt;
        &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; 
        &lt;span class="na"&gt;Grid.RowSpan=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; 
        &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
        &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Start"&lt;/span&gt; 
        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;VerticalStackLayout&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;Spacing=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
            &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Author}"&lt;/span&gt;
            &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt;
            &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Comment}"&lt;/span&gt; &lt;span class="na"&gt;MaxLines=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt; &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"0,0,0,8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Car}"&lt;/span&gt; &lt;span class="na"&gt;TextColor=&lt;/span&gt;&lt;span class="s"&gt;"Gray"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ChargerType}"&lt;/span&gt; &lt;span class="na"&gt;TextColor=&lt;/span&gt;&lt;span class="s"&gt;"Gray"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/VerticalStackLayout&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding CreatedAt, StringFormat='{0:MM/dd/yyyy}'}"&lt;/span&gt; 
        &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; 
        &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
        &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt;
        &lt;span class="na"&gt;TextColor=&lt;/span&gt;&lt;span class="s"&gt;"Gray"&lt;/span&gt;
        &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt; 
        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Start"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;BoxView&lt;/span&gt; 
        &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
        &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"LightGray"&lt;/span&gt;
        &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt; 
        &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
        &lt;span class="na"&gt;TranslationY=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/collectionview/?view=net-maui-8.0" rel="noopener noreferrer"&gt;CollectionView&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/collectionview/layout?view=net-maui-8.0#item-sizing" rel="noopener noreferrer"&gt;ItemSizingStrategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 3: Social Check-in [Uneven rows, Complex Layout]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UULzWNRskNc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For this sample, I took inspiration from &lt;a href="https://untappd.com" rel="noopener noreferrer"&gt;Untapped&lt;/a&gt;, a social beer enthusiast app. The Activity feed shows the beer check-ins of your friends, including a rating and an optional photo. When the photo is present, the template is a bit taller, so I again need to handle uneven rows. &lt;/p&gt;

&lt;p&gt;In this scenario, &lt;code&gt;CollectionView&lt;/code&gt; has a clear advantage over &lt;code&gt;ListView&lt;/code&gt; because I'm able to specify spacing between the items by calling up the &lt;code&gt;LinearItemsLayout&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;CollectionView&lt;/span&gt; 
    &lt;span class="na"&gt;ItemSizingStrategy=&lt;/span&gt;&lt;span class="s"&gt;"MeasureAllItems"&lt;/span&gt;
    &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding CheckIns}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CollectionView.ItemsLayout&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;LinearItemsLayout&lt;/span&gt; &lt;span class="na"&gt;Orientation=&lt;/span&gt;&lt;span class="s"&gt;"Vertical"&lt;/span&gt; &lt;span class="na"&gt;ItemSpacing=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/CollectionView.ItemsLayout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;v:CheckInListItem&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To accommodate the different looks, I could have opted for a &lt;a href="https://learn.microsoft.com/dotnet/maui/fundamentals/datatemplate?view=net-maui-8.0#create-a-datatemplateselector" rel="noopener noreferrer"&gt;DataTemplateSelector&lt;/a&gt;, but I chose instead to add a &lt;code&gt;HasImage&lt;/code&gt; read-only property to the model in order to show/hide the &lt;code&gt;Image&lt;/code&gt; control as well as adjust the Y position of the content.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;///...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;HasImage&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ImageUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;Border&lt;/span&gt; 
    &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
    &lt;span class="na"&gt;TranslationY=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Product.HasImage, Converter={StaticResource BoolToIntConverter}}"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I had not previously used the &lt;code&gt;BoolToObjectConverter&lt;/code&gt; from the &lt;a href="https://learn.microsoft.com/dotnet/communitytoolkit/maui/converters/bool-to-object-converter" rel="noopener noreferrer"&gt;.NET MAUI Community Toolkit&lt;/a&gt;. What a tasty discovery!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;mct:BoolToObjectConverter&lt;/span&gt; 
    &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"BoolToIntConverter"&lt;/span&gt; 
    &lt;span class="na"&gt;TrueObject=&lt;/span&gt;&lt;span class="s"&gt;"-60"&lt;/span&gt; 
    &lt;span class="na"&gt;FalseObject=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Also great for flip-flopping colors.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;mct:BoolToObjectConverter&lt;/span&gt; 
    &lt;span class="na"&gt;x:Key=&lt;/span&gt;&lt;span class="s"&gt;"BoolToColorBrushConverter"&lt;/span&gt; 
    &lt;span class="na"&gt;TrueObject=&lt;/span&gt;&lt;span class="s"&gt;"#FFFFFF"&lt;/span&gt; 
    &lt;span class="na"&gt;FalseObject=&lt;/span&gt;&lt;span class="s"&gt;"#000000"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/communitytoolkit/maui/" rel="noopener noreferrer"&gt;.NET MAUI Community Toolkit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fontawesome.com" rel="noopener noreferrer"&gt;FontAwesome Icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://picsum.photos" rel="noopener noreferrer"&gt;Lorem Picsum Photos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/pankaj.util.RatingControl" rel="noopener noreferrer"&gt;Rating Control&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 4: Learning Course [Expand and Contract]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RE_H8rV3by4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Those of you who know me are aware I enjoy language learning. One of the apps I've used called &lt;a href="https://www.teuida.net" rel="noopener noreferrer"&gt;TEUIDA&lt;/a&gt; has a nice UI that presents courses in units and lessons. Tapping a unit expands to display the different lessons with chapters in a table of contents, roadmap fashion.&lt;/p&gt;

&lt;p&gt;Originally, I tried this with &lt;code&gt;CollectionView&lt;/code&gt; and &lt;code&gt;ListView&lt;/code&gt;, but this confirmed a bug in .NET MAUI on iOS where resizing at runtime doesn't trigger the rest of the list control to resize as you would expect. As of version 8.0.60, this works great on Android.&lt;/p&gt;

&lt;p&gt;As I evaluated the content to be displayed, I recognized I don't have a LOT of data. On each page of the app, I usually have four units, each with a variable number of chapters and lessons that never exceeds 10.&lt;/p&gt;

&lt;p&gt;For these reasons, I chose to use &lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/layouts/bindablelayout?view=net-maui-8.0" rel="noopener noreferrer"&gt;&lt;code&gt;BindableLayout&lt;/code&gt;&lt;/a&gt;. In fact, this sample uses three nested &lt;code&gt;BindableLayout&lt;/code&gt;. 😲 Did this become a problem? Nope.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BindableLayout&lt;/code&gt; is a bit of an odd duck, and perhaps in retrospect it should have been a standalone control like the others. Instead it's an attached property that you can add to any other layout. So rather than starting with the control and specifying a layout like with &lt;code&gt;CollectionView&lt;/code&gt;, you start with the layout you prefer and tag on the items source and data template. Simple enough.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;ScrollView&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;VerticalStackLayout&lt;/span&gt; &lt;span class="na"&gt;Spacing=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; 
        &lt;span class="na"&gt;BindableLayout.ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Items}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;BindableLayout.ItemTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;v:LearningUnitListItem&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/BindableLayout.ItemTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/VerticalStackLayout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ScrollView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;LearningUnitListItem&lt;/code&gt; displays the primary box and a hidden list that is a loop over the chapters and lessons.&lt;/p&gt;

&lt;p&gt;To expand and contract the list of chapters and lessons, I'm simply using a click handler and toggling the visibility of the &lt;code&gt;VerticalStackLayout&lt;/code&gt; that contains that content.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/layouts/bindablelayout?view=net-maui-8.0" rel="noopener noreferrer"&gt;BindableLayout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 5: Who's Watching [Flex layout]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/kt6TiI_Bko8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Inspired by Netflix, and Disney+, and "insert other streaming service," I made a "Who's Watching" sample. This one is very simple. It's a &lt;code&gt;FlexLayout&lt;/code&gt; with &lt;code&gt;BindableLayout&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;FlexLayout&lt;/span&gt; 
    &lt;span class="na"&gt;Direction=&lt;/span&gt;&lt;span class="s"&gt;"Row"&lt;/span&gt; 
    &lt;span class="na"&gt;JustifyContent=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; 
    &lt;span class="na"&gt;Wrap=&lt;/span&gt;&lt;span class="s"&gt;"Wrap"&lt;/span&gt;
    &lt;span class="na"&gt;BindableLayout.ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding WhoIsWatching}"&lt;/span&gt; 
    &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;BindableLayout.ItemTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&lt;/span&gt; &lt;span class="na"&gt;x:DataType=&lt;/span&gt;&lt;span class="s"&gt;"m:Contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;VerticalStackLayout&lt;/span&gt; 
                &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; 
                &lt;span class="na"&gt;Spacing=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt; 
                &lt;span class="na"&gt;FlexLayout.Basis=&lt;/span&gt;&lt;span class="s"&gt;"40%"&lt;/span&gt; 
                &lt;span class="na"&gt;FlexLayout.AlignSelf=&lt;/span&gt;&lt;span class="s"&gt;"Start"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; 
                    &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"{Binding ProfilePicture}"&lt;/span&gt; 
                    &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; 
                    &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; 
                    &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"AspectFill"&lt;/span&gt;
                    &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"Transparent"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;Image.Clip&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;EllipseGeometry&lt;/span&gt; &lt;span class="na"&gt;Center=&lt;/span&gt;&lt;span class="s"&gt;"40, 40"&lt;/span&gt; &lt;span class="na"&gt;RadiusX=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="na"&gt;RadiusY=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/Image.Clip&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
                    &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding FirstName}"&lt;/span&gt; 
                    &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/VerticalStackLayout&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/BindableLayout.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/FlexLayout&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/layouts/bindablelayout?view=net-maui-8.0" rel="noopener noreferrer"&gt;BindableLayout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/layouts/flexlayout?view=net-maui-8.0" rel="noopener noreferrer"&gt;FlexLayout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 6: Mailboxes [Expand and Contract]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/95Edxbryyws"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To reproduce the Mailboxes UI as seen in Mail on iOS, I chose &lt;code&gt;BindableLayout&lt;/code&gt; and &lt;code&gt;Expander&lt;/code&gt; from the .NET MAUI Community Toolkit. While a user could end up with a lot of mail accounts that would then benefit from some virtualization, it seems reasonable to start here and grow up into a &lt;code&gt;CollectionView&lt;/code&gt; when necessary.&lt;/p&gt;

&lt;p&gt;Since I've covered the use of &lt;code&gt;BindableLayout&lt;/code&gt; already, I'll focus now on the &lt;code&gt;Expander&lt;/code&gt;. The control has two main parts, the header and the content. The header is always visible, and the content is what is shown/hidden based on the user interaction.&lt;/p&gt;

&lt;p&gt;In order to toggle the chevron indicator for open/closed, I started with two &lt;code&gt;Label&lt;/code&gt; controls to display the font icons and used a relative source binding to watch the &lt;code&gt;IsExpanded&lt;/code&gt; property of the parent control. Since I'm within the control, I can reference it this way rather than by name. I refactored this to a single &lt;code&gt;Label&lt;/code&gt; and used the magnificent &lt;code&gt;BoolToObjectConverter&lt;/code&gt;. How did I ever code without that?!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;mct:Expander&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mct:Expander.Header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"*,100,50"&lt;/span&gt; &lt;span class="na"&gt;RowDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
                &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Ortinau"&lt;/span&gt; 
                &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; 
                &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"Subtitle"&lt;/span&gt; 
                &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"38386"&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
                &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource SecondaryLabel}"&lt;/span&gt;
                &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt; &lt;span class="na"&gt;HorizontalTextAlignment=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt; 
                &lt;span class="na"&gt;IsVisible=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Path=IsExpanded, Source={RelativeSource AncestorType={x:Type mct:Expander}}, Converter={StaticResource InvertedBoolConverter}}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
                &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Path=IsExpanded, Source={RelativeSource AncestorType={x:Type mct:Expander}},Converter={StaticResource BoolToChevronConverter}}"&lt;/span&gt; 
                &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"14"&lt;/span&gt; 
                &lt;span class="na"&gt;FontFamily=&lt;/span&gt;&lt;span class="s"&gt;"FluentUI"&lt;/span&gt; 
                &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource SecondaryLabel}"&lt;/span&gt; 
                &lt;span class="na"&gt;TextColor=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource ActionColor}"&lt;/span&gt;
                &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; 
                &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;                            
                &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;    
        &lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;                    
    &lt;span class="nt"&gt;&amp;lt;/mct:Expander.Header&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;mct:Expander.Content&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Border&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;VerticalStackLayout&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;BindableLayout.ItemsSource&amp;gt;&lt;/span&gt;
                    ...
                &lt;span class="nt"&gt;&amp;lt;/BindableLayout.ItemsSource&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;BindableLayout.ItemTemplate&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;DataTemplate&lt;/span&gt; &lt;span class="na"&gt;x:DataType=&lt;/span&gt;&lt;span class="s"&gt;"m:Mailbox"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; 
                            &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"60,*,100,50"&lt;/span&gt; 
                            &lt;span class="na"&gt;RowDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"40,1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; 
                                &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; 
                                &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; 
                                &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;Image.Source&amp;gt;&lt;/span&gt;
                                    &lt;span class="nt"&gt;&amp;lt;FontImageSource&lt;/span&gt; 
                                        &lt;span class="na"&gt;Glyph=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Icon}"&lt;/span&gt; 
                                        &lt;span class="na"&gt;FontFamily=&lt;/span&gt;&lt;span class="s"&gt;"FluentUI"&lt;/span&gt; 
                                        &lt;span class="na"&gt;Size=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; 
                                        &lt;span class="na"&gt;Color=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource ActionColor}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;/Image.Source&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt; 
                            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
                                &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Name}"&lt;/span&gt; 
                                &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
                                &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"14"&lt;/span&gt; 
                                &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
                                &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding UnreadCount}"&lt;/span&gt; 
                                &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; 
                                &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource SecondaryLabel}"&lt;/span&gt;
                                &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt; 
                                &lt;span class="na"&gt;HorizontalTextAlignment=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; 
                                &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{x:Static f:FluentUI.chevron_right_12_regular}"&lt;/span&gt; 
                                &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; 
                                &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource SecondaryLabel}"&lt;/span&gt;
                                &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                                &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"14"&lt;/span&gt; 
                                &lt;span class="na"&gt;FontFamily=&lt;/span&gt;&lt;span class="s"&gt;"FluentUI"&lt;/span&gt; 
                                &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

                            &lt;span class="nt"&gt;&amp;lt;BoxView&lt;/span&gt; 
                                &lt;span class="na"&gt;Grid.ColumnSpan=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; 
                                &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
                                &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"16,0,0,0"&lt;/span&gt; 
                                &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; 
                                &lt;span class="na"&gt;Color=&lt;/span&gt;&lt;span class="s"&gt;"{AppThemeBinding Light=#f3f3f4, Dark=#333333}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/BindableLayout.ItemTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/VerticalStackLayout&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/mct:Expander.Content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/mct:Expander&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/communitytoolkit/maui/converters/bool-to-object-converter" rel="noopener noreferrer"&gt;BoolToObjectConverter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/layouts/bindablelayout?view=net-maui-8.0" rel="noopener noreferrer"&gt;BindableLayout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/communitytoolkit/maui/views/expander" rel="noopener noreferrer"&gt;Expander&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings?view=net-maui-8.0" rel="noopener noreferrer"&gt;Relative bindings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 7: Contacts [Grouping, Search]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/R0KAUwHju9M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Getting back into a sample with the need for virtualization, grouping, and search, I reproduced a Contacts list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Header
&lt;/h3&gt;

&lt;p&gt;My contact needed to appear at the top of the list and scroll away before the rest of the content. For that, I added a header to the &lt;code&gt;ListView&lt;/code&gt;. Notice it does NOT take a &lt;code&gt;DataTemplate&lt;/code&gt; since there can be only one of these and there's no need to instantiate it lazily.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;ListView.Header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;HorizontalStackLayout&lt;/span&gt; &lt;span class="na"&gt;Spacing=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Border&lt;/span&gt; &lt;span class="na"&gt;StrokeShape=&lt;/span&gt;&lt;span class="s"&gt;"RoundRectangle 40"&lt;/span&gt;
            &lt;span class="na"&gt;StrokeThickness=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"avatar_01.png"&lt;/span&gt; 
            &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; 
            &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"80"&lt;/span&gt; 
            &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"AspectFill"&lt;/span&gt; 
            &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; 
            &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"David Ortinau"&lt;/span&gt; 
            &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; 
            &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&lt;/span&gt; 
            &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/HorizontalStackLayout&amp;gt;&lt;/span&gt;        
&lt;span class="nt"&gt;&amp;lt;/ListView.Header&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Grouping
&lt;/h3&gt;

&lt;p&gt;Preparing your data sources to be grouped and searchable is the first step. In my approach, I get all my contacts in an ordered flat list, group them by the first initial of the last name, and then add them to a list of grouped contacts. The final piece is setting that aside to a new list that is unfiltered on which I can perform searches.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="n"&gt;_contacts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MockDataService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateContacts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ThenBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;ContactsGroups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ContactsGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;groupedContacts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groupedContacts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;contactsGroup&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ContactsGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;ContactsGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contactsGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;_unfilteredContactsGroups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ContactsGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;ContactsGroups&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To display the grouped list, I went with &lt;code&gt;ListView&lt;/code&gt; primarily because this scenario is one of the fundamental scenarios it was made for. To group, I set &lt;code&gt;IsGroupingEnabled="True"&lt;/code&gt; and provide a template for the group header.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;ListView.GroupHeaderTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ViewCell&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding GroupName}"&lt;/span&gt; 
            &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; 
            &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&lt;/span&gt;
            &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"12,0,0,0"&lt;/span&gt;
            &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
            &lt;span class="na"&gt;Background=&lt;/span&gt;&lt;span class="s"&gt;"Transparent"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ViewCell&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ListView.GroupHeaderTemplate&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And just like that I have the basic grouped list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search
&lt;/h3&gt;

&lt;p&gt;.NET MAUI provides a &lt;code&gt;SearchBar&lt;/code&gt; control, so I added that above the &lt;code&gt;ListView&lt;/code&gt; on the page. As the user types, the &lt;code&gt;SearchCommand&lt;/code&gt; is executed. The &lt;code&gt;Text&lt;/code&gt; property does default to a &lt;code&gt;TwoWay&lt;/code&gt; binding, so I didn't need to specify that, but I wasn't sure until &lt;a href="https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/binding-mode?view=net-maui-8.0#two-way-bindings" rel="noopener noreferrer"&gt;reading the documentation&lt;/a&gt; about binding modes for writing this post. ;)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;SearchBar&lt;/span&gt; 
    &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"SearchBar"&lt;/span&gt; 
    &lt;span class="na"&gt;Placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; 
    &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding SearchText, Mode=TwoWay}"&lt;/span&gt;
    &lt;span class="na"&gt;SearchCommand=&lt;/span&gt;&lt;span class="s"&gt;"{Binding SearchCommand}"&lt;/span&gt; 
    &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Start"&lt;/span&gt; 
    &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"{AppThemeBinding Light=White, Dark=Black}"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The search command filters down the unfiltered list and repopulates the &lt;code&gt;ContactsGroups&lt;/code&gt; that is bound to the &lt;code&gt;ListView&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RelayCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchText&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If the search text is empty, show all contacts&lt;/span&gt;
        &lt;span class="n"&gt;ContactsGroups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_unfilteredContactsGroups&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If the search text is not empty, show only contacts that contain the search text&lt;/span&gt;
        &lt;span class="n"&gt;ContactsGroups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_unfilteredContactsGroups&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; 
                &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;BUT I was having a problem because I would type, and the list would filter, but I was also getting results I didn't expect. Why?!&lt;/p&gt;

&lt;p&gt;I explained my situation to Copilot, and it explained (as I suspected) that I was only searching on the group and not the contacts within the group as I expected. Copilot provided the solution.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="n"&gt;ContactsGroups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_unfilteredContactsGroups&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ContactsGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/listview?view=net-maui-8.0" rel="noopener noreferrer"&gt;ListView&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/listview?view=net-maui-8.0#display-grouped-data" rel="noopener noreferrer"&gt;ListView grouping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/listview?view=net-maui-8.0#headers-and-footers" rel="noopener noreferrer"&gt;ListView header&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/communitytoolkit/mvvm/generators/relaycommand" rel="noopener noreferrer"&gt;RelayCommand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/dotnet/maui/user-interface/controls/searchbar?view=net-maui-8.0" rel="noopener noreferrer"&gt;SearchBar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layout 8: Shopping [Header, Data template selector, infinite scroll]
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wALbd1Ae4dg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Inspired by the &lt;a href="https://www.adidas.com/us/mobileapps" rel="noopener noreferrer"&gt;Adidas app&lt;/a&gt;, I had a bit of fun making this one. In addition to a header and making product images with ChatGPT, the display pattern is unique. You begin thinking it's going to be a grid layout with two columns, but then after four rows, you hit a product that spans both columns. Ok, so 4 and then 1, right? Wrong. From there on out it's 2 and 1. 🤯&lt;/p&gt;

&lt;p&gt;Because I need to load data in batches as the user reaches the end of the list, I chose &lt;code&gt;CollectionView&lt;/code&gt;, which has this feature built-in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filter Header
&lt;/h3&gt;

&lt;p&gt;So the header is simple: a horizontal scrolling set of buttons to filter the list.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;CollectionView.Header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;v:FilterView&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView.Header&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FilterView.xaml&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,*"&lt;/span&gt; &lt;span class="na"&gt;ColumnSpacing=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"16,16,-16,16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt; 
        &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; 
        &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; 
        &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
        &lt;span class="na"&gt;Background=&lt;/span&gt;&lt;span class="s"&gt;"Transparent"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Image.Source&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;FontImageSource&lt;/span&gt; &lt;span class="na"&gt;FontFamily=&lt;/span&gt;&lt;span class="s"&gt;"FontAwesome"&lt;/span&gt; 
                &lt;span class="na"&gt;Glyph=&lt;/span&gt;&lt;span class="s"&gt;"{x:Static f:FontAwesome.Filter}"&lt;/span&gt; 
                &lt;span class="na"&gt;Size=&lt;/span&gt;&lt;span class="s"&gt;"14"&lt;/span&gt; 
                &lt;span class="na"&gt;Color=&lt;/span&gt;&lt;span class="s"&gt;"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray300}}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Image.Source&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Image&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ScrollView&lt;/span&gt; &lt;span class="na"&gt;Orientation=&lt;/span&gt;&lt;span class="s"&gt;"Horizontal"&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;HorizontalScrollBarVisibility=&lt;/span&gt;&lt;span class="s"&gt;"Never"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;HorizontalStackLayout&lt;/span&gt; &lt;span class="na"&gt;Spacing=&lt;/span&gt;&lt;span class="s"&gt;"8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"705"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"SAMBA"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"GAZELLE"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"ULTRABOOST"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"ADIZERO"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"FORUM"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"SUPERSTAR"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"CAMPUS"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"LITE RACER"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"2000S"&lt;/span&gt; &lt;span class="na"&gt;Style=&lt;/span&gt;&lt;span class="s"&gt;"{StaticResource FilterButtonStyle}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;                
        &lt;span class="nt"&gt;&amp;lt;/HorizontalStackLayout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ScrollView&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Of course in a real app the buttons would be sourced from some collection and I would use a &lt;code&gt;BindableLayout&lt;/code&gt; for them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Funky Layout Pattern
&lt;/h3&gt;

&lt;p&gt;How could I achieve this layout pattern? I chose to massage the data to represent how it would be displayed. That's what a ViewModel is for anyway. With more help from Copilot, I told it the pattern I needed to achieve and watched the code flow! I KNOW KUNG FU!!!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="n"&gt;_productDisplays&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductDisplay&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_productDisplays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProductDisplay&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GenerateProducts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_productDisplays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProductDisplay&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GenerateProducts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_productDisplays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProductDisplay&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GenerateProducts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GetRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Seeing &lt;code&gt;GenerateProducts()&lt;/code&gt; repeated may look like it's regenerating data over and over, but I'm actually returning the cached data set once it's populated. It doesn't read well, I admit.&lt;/p&gt;

&lt;p&gt;Now that I have the data representing the pattern I need of 4:1:2:1:2:1:2:1 etc., I can move on to the data template. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CollectionView&lt;/code&gt; implements a linear items layout by default, and that's just fine. Using a data template selector, I can have two templates based on how many items I need to display: Mono and Duo.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShopTemplateSelector&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DataTemplateSelector&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DataTemplate&lt;/span&gt; &lt;span class="n"&gt;MonoTemplate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DataTemplate&lt;/span&gt; &lt;span class="n"&gt;DuoTemplate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DataTemplate&lt;/span&gt; &lt;span class="n"&gt;LoadingMoreTemplate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;DataTemplate&lt;/span&gt; &lt;span class="nf"&gt;OnSelectTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BindableObject&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ProductDisplay&lt;/span&gt; &lt;span class="n"&gt;productDisplay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProductDisplay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productDisplay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LoadingMoreTemplate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ProductDisplay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;MonoTemplate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DuoTemplate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;DuoTemplate&lt;/code&gt; is the more interesting one, as it just displays two &lt;code&gt;MonoTemplate&lt;/code&gt;s side by side. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ContentView&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/dotnet/2021/maui"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:x=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/winfx/2009/xaml"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:v=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:AllTheLists.Views"&lt;/span&gt;
             &lt;span class="na"&gt;xmlns:m=&lt;/span&gt;&lt;span class="s"&gt;"clr-namespace:AllTheLists.Models"&lt;/span&gt;
             &lt;span class="na"&gt;x:DataType=&lt;/span&gt;&lt;span class="s"&gt;"m:ProductDisplay"&lt;/span&gt;
             &lt;span class="na"&gt;x:Class=&lt;/span&gt;&lt;span class="s"&gt;"AllTheLists.Views.DuoProductListItem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"*,*"&lt;/span&gt; &lt;span class="na"&gt;ColumnSpacing=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;v:MonoProductListItem&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Products[0]}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;v:MonoProductListItem&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;BindingContext=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Products[1]}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentView&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And just like that, I have the display I need, and I don't feel like it's overly complex.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infinite Scrolling
&lt;/h3&gt;

&lt;p&gt;As the user reaches near the end of the list, I need to start fetching more data and display an indicator to the user that this is happening. The indicator is meant to appear at the bottom of the list.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CollectionView&lt;/code&gt; has properties to help with the first part. &lt;code&gt;RemainingItemsThreshold&lt;/code&gt; tells the control when that many items remain to be displayed, then call the event &lt;code&gt;RemainingItemsThresholdReached&lt;/code&gt; and execute the command &lt;code&gt;RemainingItemsThresholdReachedCommand&lt;/code&gt;. In my case, I use both of the latter, but you may only need the command. More on why I do this below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

RemainingItemsThreshold="4"
RemainingItemsThresholdReached="CollectionView_RemainingItemsThresholdReached"
RemainingItemsThresholdReachedCommand="{Binding OnThresholdReachedCommand}"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;OnThresholdReachedCommand&lt;/code&gt; fetches more data and appends it to the end of the &lt;code&gt;ObservableCollection&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RelayCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnThresholdReached&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsLoadingMore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;IsLoadingMore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;VisibleProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProductDisplay&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;IsLoading&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// fake a server call delay, allows the loading template to show&lt;/span&gt;

    &lt;span class="n"&gt;VisibleProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VisibleProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;newProducts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VisibleProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;newProducts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;VisibleProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// tiny delay for a ui refresh&lt;/span&gt;
    &lt;span class="n"&gt;IsLoadingMore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The attentive reader will have noticed some code in the data template selector in from the previous section, which connects now with the command above. As soon as the call is made to get more data, create a blank &lt;code&gt;ProductDisplay&lt;/code&gt; object which has one job, to tell the user &lt;code&gt;IsLoading=true&lt;/code&gt;. In the data template selector, I opt to display this special template and add it to the bottom of the list.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productDisplay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LoadingMoreTemplate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As soon as my data arrives, I remove the last item from the collection and resume adding real data to be displayed.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;IsLoadingMore&lt;/code&gt; boolean protects from calling this method while it's already in progress. Maybe there's a better way to do this, but old habits...&lt;/p&gt;

&lt;p&gt;To wrap this up, why am I also handling the event with &lt;code&gt;CollectionView_RemainingItemsThresholdReached&lt;/code&gt;? It's to work around a bug on one of the platforms where the command is not being executed.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CollectionView_RemainingItemsThresholdReached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ProductDisplaysViewModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;BindingContext&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ThresholdReachedCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;In conclusion, when choosing the right control for your app scenario, you have options! Consider your specific requirements and the level of customization you need for your list or layout. Prefer &lt;code&gt;CollectionView&lt;/code&gt; over &lt;code&gt;ListView&lt;/code&gt;, and don't ignore &lt;code&gt;BindableLayout&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;As I was writing this, I kept seeing more things to add and try, such as editing and ordering a list. I suppose that's what tomorrow is for.&lt;/p&gt;

&lt;p&gt;All of my development here was done on .NET 9 previews using &lt;a href="https://code.visualstudio.com/insiders/" rel="noopener noreferrer"&gt;VS Code Insiders&lt;/a&gt; and pre-release bits of the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-maui" rel="noopener noreferrer"&gt;.NET MAUI extension for VS Code&lt;/a&gt; on a Macbook Pro M1. The addition of XAML IntelliSense and XAML/C# Hot Reload has been great.&lt;/p&gt;

&lt;p&gt;One final piece of advice I have to share is to consider all options when solving for a scenario. Choosing a control is only one element. Shaping your data is another. Adapting UX patterns is yet another. While technology may be inflexible and at times will work against you, rather than trying to brute force your way to success remember that you are flexible! I have found this to be a key to success no matter what language or technology I've used. &lt;/p&gt;

&lt;p&gt;I hope this has been a fun read and you have found a takeaway or two. Maybe you have a better way to do something, or you hate how I did it. Code can be a very personal thing. Whatever your reaction, be energized to go make something amazing to share with the world.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
      <category>mobile</category>
      <category>mauiuijuly</category>
    </item>
    <item>
      <title>Managing NuGets in VS Code</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Mon, 01 Apr 2024 17:00:58 +0000</pubDate>
      <link>https://forem.com/davidortinau/managing-nugets-in-vs-code-4pp</link>
      <guid>https://forem.com/davidortinau/managing-nugets-in-vs-code-4pp</guid>
      <description>&lt;p&gt;I do most of my app building on a MacBook with Visual Studio Code, and that means I am learning new ways of doing things in the absence of an IDE. &lt;/p&gt;

&lt;p&gt;One of the main things I miss about using an IDE is the NuGet package manager. Actually, I hesitate to say "miss" because I never liked the package manager experiences anyway. I would only use them to see versions, and then I would hand edit my &lt;code&gt;csproj&lt;/code&gt; file before doing a &lt;code&gt;dotnet restore&lt;/code&gt; or just building. &lt;/p&gt;

&lt;p&gt;In most cases I can open NuGet.org to look for the version information of the package in question, but that means changing context from VS Code which bums me out. I asked &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; how I could do this in VS Code, and it pointed me to a .NET CLI tool called &lt;a href="https://www.nuget.org/packages/dotnet-outdated-tool" rel="noopener noreferrer"&gt;&lt;code&gt;dotnet-outdated-tool&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

dotnet tool install --global dotnet-outdated-tool 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running the &lt;code&gt;outdated&lt;/code&gt; command from the terminal evals my project/solution and tells me what versions are available. Sweet!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

dotnet outdated


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4hghrh9tfpmia0ttc2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4hghrh9tfpmia0ttc2i.png" alt="image of the terminal output in visual studio code listing all the packages and versions of the project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there I can update one ore more packages, or return to editing the &lt;code&gt;csproj&lt;/code&gt; as I prefer to do. And this tool does more. I can filter on a partial or full package name like &lt;code&gt;Microsoft&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jpzexqr1fh24bjpc7mp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jpzexqr1fh24bjpc7mp.png" alt="image of the terminal output in visual studio code listing only the packages from the project that include Microsoft in the name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To view all the options, run &lt;code&gt;dotnet outdated --help&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dotnet-outdated/dotnet-outdated" rel="noopener noreferrer"&gt;https://github.com/dotnet-outdated/dotnet-outdated&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Just dotnet please
&lt;/h2&gt;

&lt;p&gt;You can (of course) do all this with the .NET CLI and not use that tool. It's a bit more typing. Here I filter down to the iOS target framework.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

dotnet list package --include-prerelease --framework net8.0-ios17.2 --outdated


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s889uc9ewcegzx9mzu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s889uc9ewcegzx9mzu7.png" alt="image of the terminal output in visual studio code listing NuGet packages by target framework"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do like the visibility this gives me to the sources being used. I can even restrict to a specific source.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-list-package" rel="noopener noreferrer"&gt;&lt;code&gt;dotnet list package&lt;/code&gt;&lt;/a&gt; docs for more info.&lt;/p&gt;

&lt;p&gt;With these commands I can be productive managing my NuGets in VS Code developing .NET MAUI apps. And as &lt;a href="https://twitter.com/maddymontaquila" rel="noopener noreferrer"&gt;Maddy Montaquila&lt;/a&gt; says, working with the command line "makes me feel like a real developer". :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Inspecting .NET MAUI Apps on Mac with Reveal</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Fri, 29 Mar 2024 15:05:06 +0000</pubDate>
      <link>https://forem.com/davidortinau/inspecting-net-maui-apps-on-mac-with-reveal-5d72</link>
      <guid>https://forem.com/davidortinau/inspecting-net-maui-apps-on-mac-with-reveal-5d72</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wne0hd34egdhfr12trf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wne0hd34egdhfr12trf.png" alt="Image description" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://revealapp.com/"&gt;Reveal&lt;/a&gt; is a UI inspection tool that I've used going back to my early days writing Obj-C apps for iOS. Maybe "inspection" is a misleading way to say this, because you can also edit values in Reveal live and see the app update. Even though those changes don't update source code, this is extremely useful for figuring out why your UI isn't where you think it should be, or what actual values are being applied to your views.&lt;/p&gt;

&lt;p&gt;To use Reveal with .NET MAUI, all you need to do is link in an &lt;code&gt;xcframework&lt;/code&gt; and viola.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ItemGroup Condition=" '$(Configuration)' == 'Debug' "&amp;gt;
  &amp;lt;NativeReference Include="$(HOME)\Library\Application Support\Reveal\RevealServer\RevealServer.xcframework"&amp;gt;
    &amp;lt;Kind&amp;gt;Framework&amp;lt;/Kind&amp;gt;
    &amp;lt;SmartLink&amp;gt;True&amp;lt;/SmartLink&amp;gt;
  &amp;lt;/NativeReference&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official instructions are listed as &lt;a href="https://support.revealapp.com/hc/en-us/articles/360022479312-Integrating-Reveal-using-Visual-Studio-for-Mac"&gt;using Reveal with Visual Studio Mac&lt;/a&gt;, however it's really just a &lt;code&gt;csproj&lt;/code&gt; addition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F017lpscu5141bxleo0as.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F017lpscu5141bxleo0as.png" alt="Image description" width="800" height="493"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I'm doing all my development from the Mac with Visual Studio Code + &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-maui"&gt;.NET MAUI extension&lt;/a&gt; (and of course my indespensible &lt;a href="https://docs.github.com/en/copilot/using-github-copilot/getting-started-with-github-copilot"&gt;GitHub Copilot&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Make sure to clean your bin/obj folders and rebuild. Once your app is open it will be detected by Reveal and show you the visual tree.&lt;/p&gt;

&lt;p&gt;What about Windows? The lasted version of Visual Studio 2022 provides a live visual tree, previewer with inspection, and property explorer (see &lt;a href="https://learn.microsoft.com/visualstudio/xaml-tools/inspect-xaml-properties-while-debugging?view=vs-2022"&gt;docs&lt;/a&gt;). Along with hot reload you can tweak your app during debug and get the round trip to quickly improve your app.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
      <category>mobile</category>
    </item>
    <item>
      <title>.NET MAUI Tips - Some Dos and Don'ts</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Thu, 01 Feb 2024 21:38:27 +0000</pubDate>
      <link>https://forem.com/davidortinau/net-maui-dos-and-donts-19g3</link>
      <guid>https://forem.com/davidortinau/net-maui-dos-and-donts-19g3</guid>
      <description>&lt;p&gt;During today's .NET MAUI Community Standup live stream, Maddy Montaquila and I shared some common tips to help developers based on mistakes we've been seeing lately. &lt;/p&gt;

&lt;p&gt;Agree? Disagree? Share your perspective and any tips you might have as well in the comments.&lt;/p&gt;

&lt;p&gt;Check it out:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/live/IQbJPm1VBkg?si=rKRDSHV8v3bhCml1&amp;amp;amp;t=2951" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Slides: &lt;a href="https://github.com/davidortinau/presentations/raw/main/misc/MAUI%20Advice.pptx" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 1
  &lt;h2&gt;
  
  
  Use &lt;code&gt;Shell&lt;/code&gt; or &lt;code&gt;TabbedPage&lt;/code&gt;|&lt;code&gt;FlyoutPage&lt;/code&gt;|&lt;code&gt;NavigationPage&lt;/code&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;NOT BOTH&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The combination is unsupported.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 2
  &lt;h2&gt;
  
  
  Set &lt;code&gt;MainPage&lt;/code&gt; once
&lt;/h2&gt;
&lt;h2&gt;
  
  
  or YMMV
&lt;/h2&gt;

&lt;p&gt;Consider instead how you can craft different configurations within &lt;code&gt;AppShell.xaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 3
  &lt;h2&gt;
  
  
  Don't nest tabs (&lt;code&gt;TabbedPage&lt;/code&gt; &amp;gt; &lt;code&gt;TabbedPage&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Instead evaluate the UX and consider a different, standard approach&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 4
  &lt;h2&gt;
  
  
  Reference MauiImages as PNG
&lt;/h2&gt;
&lt;h2&gt;
  
  
  (not svg)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Image Source="dotnet_bot.png" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The svg is only a source file that generates PNGs at the target densities and sizes.&lt;/p&gt;

&lt;p&gt;If you need SVG, use &lt;a href="https://github.com/mono/SkiaSharp" rel="noopener noreferrer"&gt;SkiaSharp&lt;/a&gt; or a library like &lt;a href="https://www.nuget.org/packages/Vapolia.Svg/1.0.4-pre8" rel="noopener noreferrer"&gt;Vapolia.Svg&lt;/a&gt; &lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 5
  &lt;h2&gt;
  
  
  Don’t nest unconstrained scrolling things inside a stack layout.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;unconstrained&lt;/strong&gt; - it has no explicit sizing constraints like &lt;code&gt;WidthRequest&lt;/code&gt;, &lt;code&gt;HeightRequest&lt;/code&gt;, or the parent container size may be unknown&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scrolling things&lt;/strong&gt; - like &lt;code&gt;CollectionView&lt;/code&gt;, &lt;code&gt;ListView&lt;/code&gt;, and &lt;code&gt;ScrollView&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;stack layout - including &lt;code&gt;StackLayout&lt;/code&gt;, &lt;code&gt;HorizontalStackLayout&lt;/code&gt;, and &lt;code&gt;VerticalStackLayout&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A VerticalStackLayout hosting an unsized/unconstrained vertical scrollable control will not scroll or virtualize unless the height is constrained.&lt;/p&gt;

&lt;p&gt;If the layout/scroll directions are not aligned, then it’ll work (e.g. vertical layout with a horizontal scrolling child).&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 6
  &lt;h2&gt;
  
  
  Customize handlers in &lt;code&gt;ConfigureHandlers()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Append/Modify etc. only run once and should be in place before the first handlers are created. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;ConfigureHandlers&lt;/code&gt; is a builder method in &lt;code&gt;MauiProgram.cs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 7
  &lt;h2&gt;
  
  
  Don’t nest elements with gesture recognizers
&lt;/h2&gt;

&lt;p&gt;A control with gesture recognizers inside of a layout with gesture recognizers is a recipe for problems.&lt;/p&gt;

&lt;p&gt;Similarly, beware z-index issues when implementing gestures where views overlap. Use &lt;code&gt;InputTransparent&lt;/code&gt; to ensure gestures pass through views when necessary.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 8
  &lt;h2&gt;
  
  
  Move simple renderers to handler modifications (Append/Modify)
&lt;/h2&gt;

&lt;p&gt;While a renderer can be wired up in .NET MAUI, handlers are preferred.&lt;/p&gt;

&lt;p&gt;Much simpler to implement and maintain long term.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 9
  &lt;h2&gt;
  
  
  Rewrite custom controls (renderers) as handlers with mappers
&lt;/h2&gt;

&lt;p&gt;Wiring up a renderer is a quick start during migration, but you may find porting the implementation to a handler faster and more reliable.&lt;/p&gt;

&lt;p&gt;Start with wiring the handler, but don’t spend too long on it if it doesn’t work. &lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 10
  &lt;h2&gt;
  
  
  Prefer &lt;code&gt;Grid&lt;/code&gt; with explicit size and * in &lt;code&gt;DataTemplates&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Use of Auto, and use of stack layouts inside DataTemplates are less reliable for measure/layout.&lt;/p&gt;

&lt;p&gt;Ask Ben Buttigieg for more info ;)&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 11
  &lt;h2&gt;
  
  
  Use &lt;code&gt;MauiSplashScreen&lt;/code&gt; and &lt;code&gt;MauiIcon&lt;/code&gt; only for the simple cases
&lt;/h2&gt;

&lt;p&gt;Use native implementations for anything more complex if those don't immediately meet your needs.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  .NET MAUI Tip 12
  &lt;h2&gt;
  
  
  Prefer ProjectReference over NuGets for sharing your libraries
&lt;/h2&gt;

&lt;p&gt;Safer, easier, and sharing more things is supported.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>C# UI and .NET Hot Reload - A Match Made in .NET MAUI</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Mon, 10 Oct 2022 20:20:39 +0000</pubDate>
      <link>https://forem.com/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f</link>
      <guid>https://forem.com/davidortinau/c-ui-and-net-hot-reload-a-match-made-in-net-maui-243f</guid>
      <description>&lt;p&gt;Visual Studio 2022 and .NET 6 introduced .NET Hot Reload, the ability to continue coding C# while debugging your application, and use your changes without stopping. If you've used Edit and Continue, this probably feels similar. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start debugging your app&lt;/li&gt;
&lt;li&gt;Add or edit some C#&lt;/li&gt;
&lt;li&gt;Click the "Hot Reload" button in Visual Studio&lt;/li&gt;
&lt;li&gt;Re-trigger the code path to exercise your new code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most of the time in .NET MAUI we (I) use XAML to declare UI, and XAML Hot Reload to avoid repetitive stopping and starting. This can work really well. Anytime you make a XAML edit, as long as the XAML compiler indicates your edit is valid code, the change is shipped to the running app AND the UI is updated while retaining state. &lt;/p&gt;

&lt;p&gt;There are many cases when XAML requires me to stop and restart my session, thus breaking my flow. Remaining completely in C# drastically improves this situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Marrying C# UI and .NET Hot Reload
&lt;/h2&gt;

&lt;p&gt;When you edit UI code in your C#, you must then do &lt;em&gt;something&lt;/em&gt; to run that code again and see the impact of your change. That can become tiresome. Here's what I do. &lt;/p&gt;

&lt;p&gt;I like the pattern of placing my UI construction in a &lt;code&gt;Build&lt;/code&gt; method. This method sets the &lt;code&gt;ContentPage.Content&lt;/code&gt; and is called in the page's &lt;code&gt;OnNavigatedTo&lt;/code&gt; method when hosted within &lt;code&gt;Shell&lt;/code&gt; or a &lt;code&gt;NavigationPage&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 

        &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNavigatedTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NavigatedToEventArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnNavigatedTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! But this method is only going to get called once. I could navigate away from that page and come back. Or I could add a gesture to trigger the method manually. That gets old quickly.&lt;/p&gt;

&lt;p&gt;What if I could make sure the &lt;code&gt;Build()&lt;/code&gt; method anytime I triggered a hot reload, similar to XAML Hot Reload? &lt;/p&gt;

&lt;p&gt;Turns out I can! Anytime .NET Hot Reload executes, a metadata change is triggered, and I can hook into that. To do this, I add a &lt;em&gt;HotReloadService.cs&lt;/em&gt; to my project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#if DEBUG
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MetadataUpdateHandlerAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;YourAppNamespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HotReloadService&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;YourAppNamespace&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HotReloadService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;pragma&lt;/span&gt; &lt;span class="n"&gt;warning&lt;/span&gt; &lt;span class="n"&gt;disable&lt;/span&gt; &lt;span class="n"&gt;CS8632&lt;/span&gt; &lt;span class="c1"&gt;// The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[]?&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;UpdateApplicationEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;pragma&lt;/span&gt; &lt;span class="n"&gt;warning&lt;/span&gt; &lt;span class="n"&gt;restore&lt;/span&gt; &lt;span class="n"&gt;CS8632&lt;/span&gt; &lt;span class="c1"&gt;// The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.&lt;/span&gt;

        &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ClearCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[]?&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[]?&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;UpdateApplicationEvent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anytime a change happens, the app will now dispatch an event. In the file I'm currently working on, where I want to re-execute the build, I handle the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNavigatedTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NavigatedToEventArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnNavigatedTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cp"&gt;#if DEBUG
&lt;/span&gt;        &lt;span class="n"&gt;HotReloadService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdateApplicationEvent&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;ReloadUI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNavigatedFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NavigatedFromEventArgs&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnNavigatedFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#if DEBUG
&lt;/span&gt;        &lt;span class="n"&gt;HotReloadService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdateApplicationEvent&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;ReloadUI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ReloadUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MainThread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginInvokeOnMainThread&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now give that a try! Make a change to your C# in that page, and hit the Hot Reload fire button (or if you're like me, set hot reload to execute on save). Boom!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;XAML Hot Reload must be enabled to leverage this event since it works over the same tooling service. If you disable XAML Hot Reload, then your C# code will reload, but you won't receive the event that you have wired up now to trigger a UI rebuild.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have found very few scenarios so far when I have to actually stop and restart my debugging session. As you can see in this video, I even add a new class file without stopping.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/py7pf1MIzmE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Roundup of .NET MAUI. - Week of August 15, 2022</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Mon, 22 Aug 2022 14:22:37 +0000</pubDate>
      <link>https://forem.com/davidortinau/roundup-of-net-maui-week-of-august-15-2022-48df</link>
      <guid>https://forem.com/davidortinau/roundup-of-net-maui-week-of-august-15-2022-48df</guid>
      <description>&lt;p&gt;This week (or last week?) I'm going to switch things up and organize the content that caught my attention into categories.  I'm seeing more libraries and content themes emerging. &lt;/p&gt;

&lt;p&gt;Table of Contents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
UI Design - new controls and how-tos&lt;/li&gt;
&lt;li&gt;App Architecture&lt;/li&gt;
&lt;li&gt;Bar Code Scanning&lt;/li&gt;
&lt;li&gt;
Business - data storage, authentication, NFC, DataGrid, PRINTING!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  UI Design &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Libraries
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;LiveCharts&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/beto-rodriguez" rel="noopener noreferrer"&gt;
        beto-rodriguez
      &lt;/a&gt; / &lt;a href="https://github.com/beto-rodriguez/LiveCharts2" rel="noopener noreferrer"&gt;
        LiveCharts2
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Simple, flexible, interactive &amp;amp; powerful charts, maps and gauges for .Net, LiveCharts2 can now practically run everywhere Maui, Uno Platform, Blazor-wasm, WPF, WinForms, Xamarin, Avalonia, WinUI, UWP.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;LiveCharts2&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.codefactor.io/repository/github/beto-rodriguez/livecharts2" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1fa043ee2f7ad697869e5777094fc5b5980d07fdfcd059f7f85390ec189cb1b0/68747470733a2f2f7777772e636f6465666163746f722e696f2f7265706f7369746f72792f6769746875622f6265746f2d726f6472696775657a2f6c697665636861727473322f6261646765" alt="CodeFactor"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/beto-rodriguez/LiveCharts2/actions/workflows/run-unit-tests.yml/badge.svg"&gt;&lt;img src="https://github.com/beto-rodriguez/LiveCharts2/actions/workflows/run-unit-tests.yml/badge.svg" alt="Unit tests"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/beto-rodriguez/LiveCharts2/actions/workflows/compile-all-views.yml/badge.svg"&gt;&lt;img src="https://github.com/beto-rodriguez/LiveCharts2/actions/workflows/compile-all-views.yml/badge.svg" alt="SkiaSharp Views"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blazor-livecharts.controli.app/" rel="nofollow noopener noreferrer"&gt;Watch Blazor WASM demo&lt;/a&gt; (only designed for desktop devices for now)&lt;/p&gt;
&lt;p&gt;LiveCharts2 (v2) is the evolution of &lt;a href="https://github.com/Live-Charts/Live-Charts" rel="noopener noreferrer"&gt;LiveCharts&lt;/a&gt; (v0), it fixes the main design issues of its predecessor, it's focused to run everywhere, improves flexibility without losing what we already had in v0.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Extremely flexible data visualization library&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;The following image is a preview, &lt;code&gt;v2.0&lt;/code&gt; is beta now.&lt;/p&gt;
&lt;p&gt;Here is a preview (1.4MB gif, wait for it to load if you see a blank space bellow this text...):&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/10853349/124399763-41873900-dce3-11eb-937a-947d66d42597.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F10853349%2F124399763-41873900-dce3-11eb-937a-947d66d42597.gif" alt="lv2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Get started&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Live charts is a cross platforms charting library for .Net, to get started go to &lt;a href="https://livecharts.dev" rel="nofollow noopener noreferrer"&gt;https://livecharts.dev&lt;/a&gt; and take a look at the instalation guide of your target platform,
the web site contains all the samples provided in this repo, docs and more.&lt;/p&gt;
&lt;p&gt;LiveCharts supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maui&lt;/li&gt;
&lt;li&gt;Uno Platform&lt;/li&gt;
&lt;li&gt;Wpf&lt;/li&gt;
&lt;li&gt;WinUI&lt;/li&gt;
&lt;li&gt;Xamarin.Forms&lt;/li&gt;
&lt;li&gt;WindowsForms&lt;/li&gt;
&lt;li&gt;BlazorWasm&lt;/li&gt;
&lt;li&gt;Avalonia&lt;/li&gt;
&lt;li&gt;Eto Forms&lt;/li&gt;
&lt;li&gt;Uwp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also use LiveCharts 2 on a console app or on the…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/beto-rodriguez/LiveCharts2" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;StateButton&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IeuanWalker" rel="noopener noreferrer"&gt;
        IeuanWalker
      &lt;/a&gt; / &lt;a href="https://github.com/IeuanWalker/Maui.StateButton" rel="noopener noreferrer"&gt;
        Maui.StateButton
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      With this control, you are able to create any style of button. This is possible as it acts as a wrapper to your XAML and provides you the events/ commands and properties to bind to. It's also 100% accessible by default.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝&lt;/th&gt;
&lt;th&gt;This is a MAUI version of my &lt;a href="https://github.com/IeuanWalker/Xamarin.Forms.StateButton" rel="noopener noreferrer"&gt;Xamarin NuGet&lt;/a&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Maui.StateButton  &lt;a href="https://www.nuget.org/packages/IeuanWalker.Maui.StateButton" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/97dc5f4f0539070d77ee84edadefe4f9403cb65027a1390d5bb0bb2be8423c82/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f496575616e57616c6b65722e4d6175692e5374617465427574746f6e" alt="Nuget"&gt;&lt;/a&gt; &lt;a href="https://www.nuget.org/packages/IeuanWalker.Maui.StateButton" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/bd412ca39551faa145c2419bfcaa91b5944e18a465c3b086d1401ce2ef94afeb/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f64742f496575616e57616c6b65722e4d6175692e5374617465427574746f6e" alt="Nuget"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/28f4d479bf0a9b033b3a3b95ab2adc343da448a025b01aefdc0fbc7f0e169eb8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://app.fossa.com/projects/git%2Bgithub.com%2FIeuanWalker%2FMaui.StateButton?ref=badge_shield" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/11d1c3c79897ed7e5439a2b52b2cc867371430c100c264463d1c35848f62b5a2/68747470733a2f2f6170702e666f7373612e636f6d2f6170692f70726f6a656374732f6769742532426769746875622e636f6d253246496575616e57616c6b65722532464d6175692e5374617465427574746f6e2e7376673f747970653d736869656c64" alt="FOSSA Status"&gt;&lt;/a&gt;
&lt;a href="https://www.codacy.com/gh/IeuanWalker/Maui.StateButton/dashboard?utm_source=github.com&amp;amp;utm_medium=referral&amp;amp;utm_content=IeuanWalker/Maui.StateButton&amp;amp;utm_campaign=Badge_Grade" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e96ecc28749d993db64c49f2bbfac088cf9e7339421028df3cb2a9726312001d/68747470733a2f2f6170702e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f6234383233323135393235633437663761343262363462633531366136653432" alt="Codacy Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With this control you are able to create any style of button
This is possible as it acts as a wrapper to your XAML and provides you the events/ commands and properties to bind too.&lt;/p&gt;
&lt;p&gt;It's also &lt;strong&gt;100% accessible&lt;/strong&gt; with each platform seeing/ treating the control as a native button.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://github.com/IeuanWalker/Maui.StateButton/tree/master/Demo/Controls/Examplesl" rel="noopener noreferrer"&gt;examples&lt;/a&gt;, for a bunch of different designs -&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/IeuanWalker/Maui.StateButton/Example.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FIeuanWalker%2FMaui.StateButton%2FExample.gif" alt="Example gif"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to use it?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Install the &lt;a href="https://www.nuget.org/packages/IeuanWalker.Maui.StateButton/" rel="nofollow noopener noreferrer"&gt;NuGet package&lt;/a&gt; into your shared project project&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;Install-Package IeuanWalker.Maui.StateButton
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then in the MauiProgram.cs, and the StateButton configuration method -&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;using&lt;/span&gt; IeuanWalker&lt;span class="pl-kos"&gt;.&lt;/span&gt;Maui&lt;span class="pl-kos"&gt;.&lt;/span&gt;StateButton&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;builder
    &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-smi"&gt;UseMauiApp&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;App&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
    &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;UseStateButton&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What can I do with it?&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Properties&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Extra info&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;This changes based on the button state. i.e. &lt;code&gt;Pressed&lt;/code&gt;, &lt;code&gt;NotPressed&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Default state is &lt;code&gt;NotPressed&lt;/code&gt; &lt;br&gt;  &lt;br&gt; The binding mode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/IeuanWalker/Maui.StateButton" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;These videos show off how to implement beautiful UI in .NET MAUI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BreadCrumb&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IeuanWalker" rel="noopener noreferrer"&gt;
        IeuanWalker
      &lt;/a&gt; / &lt;a href="https://github.com/IeuanWalker/Maui.Breadcrumb" rel="noopener noreferrer"&gt;
        Maui.Breadcrumb
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is a breadcrumb navigation control that is completely automatic and uses the Navigation stack and page titles to generate the breadcrumbs. It's also 100% accessible by default..
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝&lt;/th&gt;
&lt;th&gt;This is a MAUI version of my &lt;a href="https://github.com/IeuanWalker/Xamarin.Forms.Breadcrumb" rel="noopener noreferrer"&gt;Xamarin NuGet&lt;/a&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Maui.Breadcrumb &lt;a href="https://www.nuget.org/packages/IeuanWalker.Maui.Breadcrumb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8c3706bcb860fea1235c450a0316c9caa13e88e4cad754c889b2cfa4a8dd6588/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f496575616e57616c6b65722e4d6175692e42726561646372756d62" alt="Nuget"&gt;&lt;/a&gt; &lt;a href="https://www.nuget.org/packages/IeuanWalker.Maui.Breadcrumb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2329a72080cea01283da9fb5485e0b98641e6518ed79641446deb5372ee41bb7/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f64742f496575616e57616c6b65722e4d6175692e42726561646372756d62" alt="Nuget"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/28f4d479bf0a9b033b3a3b95ab2adc343da448a025b01aefdc0fbc7f0e169eb8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://app.fossa.com/projects/git%2Bgithub.com%2FIeuanWalker%2FMaui.Breadcrumb?ref=badge_shield" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f4442b389e332d054e839cf6bc6463e0beebfe4f4016afc1e870a493e7ad564d/68747470733a2f2f6170702e666f7373612e636f6d2f6170692f70726f6a656374732f6769742532426769746875622e636f6d253246496575616e57616c6b65722532464d6175692e42726561646372756d622e7376673f747970653d736869656c64" alt="FOSSA Status"&gt;&lt;/a&gt; &lt;a href="https://app.codacy.com/gh/IeuanWalker/Maui.Breadcrumb?utm_source=github.com&amp;amp;utm_medium=referral&amp;amp;utm_content=IeuanWalker/Maui.Breadcrumb&amp;amp;utm_campaign=Badge_Grade_Settings" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/451ca93ed5e04b481a6f2a622a9ab8190796a6c7dca1fc846bf63d6b7847223d/68747470733a2f2f6170692e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f3838393338343565643962633466323038646566303162616165376362366336" alt="Codacy Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a breadcrumb navigation control that is completely automatic and uses the Navigation stack to get the page titles to generate the breadcrumbs.&lt;/p&gt;
&lt;p&gt;The animation for the control is based on this article - &lt;a href="https://theconfuzedsourcecode.wordpress.com/2017/02/04/a-cool-breadcrumbs-bar-with-xamarin-forms-animations/" rel="nofollow noopener noreferrer"&gt;A Cool Breadcrumbs Bar with Xamarin Forms Animations…&lt;/a&gt;&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Basic example&lt;/th&gt;
&lt;th&gt;Production Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/IeuanWalker/Maui.Breadcrumb/Example.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FIeuanWalker%2FMaui.Breadcrumb%2FExample.gif" alt="Example gif"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/IeuanWalker/Maui.Breadcrumb/ProdExample.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FIeuanWalker%2FMaui.Breadcrumb%2FProdExample.gif" alt="Production Example gif"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to use it?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Install the &lt;a href="https://www.nuget.org/packages/IeuanWalker.Maui.Breadcrumb/" rel="nofollow noopener noreferrer"&gt;NuGet package&lt;/a&gt; into all of your projects&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;Install-Package IeuanWalker.Maui.Breadcrumb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This control uses one of my other controls, &lt;a href="https://github.com/IeuanWalker/Maui.StateButton" rel="noopener noreferrer"&gt;StateButton&lt;/a&gt;, this is to make this control accessible. Ao you'll need to register the using in the &lt;code&gt;MauiProgram.cs&lt;/code&gt; on the &lt;code&gt;MauiAppBuilder&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;builder
    &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-smi"&gt;UseMauiApp&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;App&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
    &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;UseStateButton&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To add to a page the first thing we need to do is tell our XAML page where it can find the Breadcrumb control, which is done by adding the following attribute to our…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/IeuanWalker/Maui.Breadcrumb" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Plainer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This lib strips controls down to the bare styles to make them "easy to style". Hmm, useful?&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/enisn" rel="noopener noreferrer"&gt;
        enisn
      &lt;/a&gt; / &lt;a href="https://github.com/enisn/Xamarin.Forms.Plainer" rel="noopener noreferrer"&gt;
        Xamarin.Forms.Plainer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This library removes all visual effects from controls and makes them easy to style from portable layer instead of writing custom renderers always.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/enisn/Xamarin.Forms.Plainerart/cover.svg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fenisn%2FXamarin.Forms.Plainerart%2Fcover.svg" width="100%" alt="icon"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Xamarin.Forms.Plainer&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This library removes all visual effects from controls and makes them easy to style from portable layer instead of writing custom renderers always
No underlines, no borders, no background colors. Just interactive fields without visual effect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codefactor.io/repository/github/enisn/xamarin.forms.plainer" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/77474efbf5a11338c0f61dfbfb787adc8a1fa724450b43b070a051f8a80f0fb6/68747470733a2f2f7777772e636f6465666163746f722e696f2f7265706f7369746f72792f6769746875622f656e69736e2f78616d6172696e2e666f726d732e706c61696e65722f6261646765" alt="CodeFactor"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/Xamarin.Forms.Plainer/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/95aafdaee731bc1d25407c4cff0d25f85353d3884db60b92f5945fa3e07655ac/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f58616d6172696e2e466f726d732e506c61696e6572" alt="Nuget"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started on MAUI&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;code&gt;Plainer.Maui&lt;/code&gt; nuget package to your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;strong&gt;MauiProgram.cs&lt;/strong&gt; and add Plainer handlers&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;builder&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;ConfigureMauiHandlers&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;handlers &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;  handlers&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;AddPlainer&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started on Xamarin Forms&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;code&gt;Xamarin.Forms.Plainer&lt;/code&gt; to each platform&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Initialize method into platforms&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Android&lt;/strong&gt;: Go to &lt;code&gt;MainActivity&lt;/code&gt; and add following:&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;protected&lt;/span&gt; &lt;span class="pl-k"&gt;override&lt;/span&gt; &lt;span class="pl-smi"&gt;void&lt;/span&gt; &lt;span class="pl-en"&gt;OnCreate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-smi"&gt;Bundle&lt;/span&gt; &lt;span class="pl-s1"&gt;savedInstanceState&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-k"&gt;base&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;OnCreate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;savedInstanceState&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;global&lt;/span&gt;::Xamarin&lt;span class="pl-kos"&gt;.&lt;/span&gt;Forms&lt;span class="pl-kos"&gt;.&lt;/span&gt;Forms&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;Init&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; savedInstanceState&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

  &lt;span class="pl-c"&gt;// Add follwoing line&lt;/span&gt;
  &lt;span class="pl-k"&gt;global&lt;/span&gt;::Plugin&lt;span class="pl-kos"&gt;.&lt;/span&gt;Plainer&lt;span class="pl-kos"&gt;.&lt;/span&gt;Platforms&lt;span class="pl-kos"&gt;.&lt;/span&gt;Droid&lt;span class="pl-kos"&gt;.&lt;/span&gt;Plainer&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;Init&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; savedInstanceState&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt; &lt;span class="pl-c"&gt;// &amp;lt;--&lt;/span&gt;

  LoadApplication&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/enisn/Xamarin.Forms.Plainer" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Image Pinch to Zoom&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TBertuzzi" rel="noopener noreferrer"&gt;
        TBertuzzi
      &lt;/a&gt; / &lt;a href="https://github.com/TBertuzzi/Bertuzzi.MAUI.PinchZoomImage" rel="noopener noreferrer"&gt;
        Bertuzzi.MAUI.PinchZoomImage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🔍 Zoom in on the image with the pinch 👌 of your fingers or double tapping 👆
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Bertuzzi.MAUI.PinchZoomImage&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Zoom in on the image with the pinch of your fingers.&lt;/p&gt;
&lt;p&gt;A simple way to zoom in and zoom out on your images with MAUI.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h6 class="heading-element"&gt;This is the component, works on iOS, Android&lt;/h6&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;NuGet&lt;/strong&gt;&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Info&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bertuzzi.MAUI.PinchZoomImage&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.nuget.org/packages/Bertuzzi.MAUI.PinchZoomImage/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f86455f3301f486cc3dfeb03bf06f1e43b229d3717ac5af76b24d5e266ac7380/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f42657274757a7a692e4d4155492e50696e63685a6f6f6d496d616765" alt="NuGet"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Platform Support&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;PinchZoomImage is a MAUI library.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup / Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Does not require additional configuration. Just install the package in the shared project and use.&lt;/p&gt;
&lt;p&gt;You just need to add the reference in your xaml file.&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;  xmlns&lt;span class="pl-c1"&gt;:&lt;/span&gt;&lt;span class="pl-s1"&gt;pinch&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;clr-namespace:Bertuzzi.MAUI.PinchZoomImage;assembly=Bertuzzi.MAUI.PinchZoomImage&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt;  &lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Control&lt;/p&gt;
&lt;p&gt;Use with the image control&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt; &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;pinch&lt;span class="pl-c1"&gt;:&lt;/span&gt;&lt;span class="pl-s1"&gt;PinchZoom&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;pinch&lt;span class="pl-c1"&gt;:&lt;/span&gt;PinchZoom&lt;span class="pl-kos"&gt;.&lt;/span&gt;Content&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;Image Source&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;xxamarin.jpg&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;/&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-c1"&gt;/&lt;/span&gt;pinch&lt;span class="pl-c1"&gt;:&lt;/span&gt;PinchZoom&lt;span class="pl-kos"&gt;.&lt;/span&gt;Content&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-c1"&gt;/&lt;/span&gt;pinch&lt;span class="pl-c1"&gt;:&lt;/span&gt;PinchZoom&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;     &lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The complete example can be downloaded here:
&lt;a href="https://github.com/TBertuzzi/Bertuzzi.MAUI.PinchZoomImage/tree/master/PinchZoomImageMauiSample" rel="noopener noreferrer"&gt;https://github.com/TBertuzzi/Bertuzzi.MAUI.PinchZoomImage/tree/master/PinchZoomImageMauiSample&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Base on my package : &lt;a href="https://github.com/TBertuzzi/Xamarin.Forms.PinchZoomImage" rel="noopener noreferrer"&gt;https://github.com/TBertuzzi/Xamarin.Forms.PinchZoomImage&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TBertuzzi/Bertuzzi.MAUI.PinchZoomImage" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Animated Menu
&lt;/h3&gt;

&lt;p&gt;Yes! Love this so much. Naweed has had a busy week churning out videos for a variety of different apps. &lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?t=525&amp;amp;v=qkfBGIypRLI&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;



&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/shorts/bItbCoxFI10?feature=share" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Javier Codes
&lt;/h3&gt;

&lt;p&gt;We all know Javier can make some beautiful apps, and if you don't then this is a great introduction. Chill out with some great music and just watch as Javier brings this design to life. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/b8B7ZyksFyQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Composite Button with Activity Indicator
&lt;/h3&gt;

&lt;p&gt;Pragnesh shows us how to make a button that has state and includes a spinner inside it. This is a good example of how you can compose controls to make custom UI experiences.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7CafvZ17oUE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Themes
&lt;/h3&gt;

&lt;p&gt;Chris shows us how to give the user various UI themes to choose from at runtime.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/eu52qX-qww4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Draw/Copy Paths and Shapes to XAML
&lt;/h3&gt;

&lt;p&gt;This is a great tip everyone should know. When your designer gives you a source file, you can often copy the shape data directly from the design tool, or you can crack open an SVG in a text editor and copy the data to XAML.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/rdopvfMgvww"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  App Architecture &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Naweed's MauiTubePlayer
&lt;/h3&gt;

&lt;p&gt;This series of videos works through architectural design decisions we all need to make when implementing common functionality across an application.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/nXGQcVqxuoY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  [Turkish] Folder Structure
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_Nf9HZQsOZI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Bar Code Scanning &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ZXing Library
&lt;/h3&gt;

&lt;p&gt;Jonathan Dick (Redth) updated his popular library to .NET 6 and there's a &lt;a href="https://www.nuget.org/packages/ZXing.Net.Maui/" rel="noopener noreferrer"&gt;preview on NuGet&lt;/a&gt; ready to use! &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Redth" rel="noopener noreferrer"&gt;
        Redth
      &lt;/a&gt; / &lt;a href="https://github.com/Redth/ZXing.Net.Maui" rel="noopener noreferrer"&gt;
        ZXing.Net.Maui
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Barcode Scanning for MAUI?
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ZXing.Net.MAUI&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;The successor to ZXing.Net.Mobile: barcode scanning and generation for .NET MAUI applications&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/271950/129272315-b3f5a468-c585-49f2-bbab-68a884618b94.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F271950%2F129272315-b3f5a468-c585-49f2-bbab-68a884618b94.png" width="300"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Barcode Scanning&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Install ZXing.Net.MAUI&lt;/h3&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://www.nuget.org/packages/ZXing.Net.Maui.Controls" rel="nofollow noopener noreferrer"&gt;ZXing.Net.Maui.Controls&lt;/a&gt; NuGet package on your .NET MAUI application&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure to initialize the plugin first in your &lt;code&gt;MauiProgram.cs&lt;/code&gt;, see below&lt;/p&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;// Add the using to the top&lt;/span&gt;
&lt;span class="pl-k"&gt;using&lt;/span&gt; ZXing&lt;span class="pl-kos"&gt;.&lt;/span&gt;Net&lt;span class="pl-kos"&gt;.&lt;/span&gt;Maui&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// ... other code &lt;/span&gt;

&lt;span class="pl-k"&gt;public&lt;/span&gt; &lt;span class="pl-k"&gt;&lt;span class="pl-k"&gt;static&lt;/span&gt;&lt;/span&gt; MauiApp &lt;span class="pl-en"&gt;Create&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-smi"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;builder&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; MauiApp&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;CreateBuilder&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

    builder
        &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-smi"&gt;UseMauiApp&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;App&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
        &lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;UseBarcodeReader&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt; &lt;span class="pl-c"&gt;// Make sure to add this line&lt;/span&gt;

    &lt;span class="pl-k"&gt;return&lt;/span&gt; builder&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;Build&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we just need to add the right permissions to our app metadata. Find below how to do that for each platform.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Android&lt;/h4&gt;

&lt;/div&gt;
&lt;p&gt;For Android go to your &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file (under the Platforms\Android folder) and add the following permissions inside of the &lt;code&gt;manifest&lt;/code&gt; node:&lt;/p&gt;
&lt;div class="highlight highlight-text-xml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;uses-permission&lt;/span&gt; &lt;span class="pl-e"&gt;android&lt;/span&gt;&lt;span class="pl-e"&gt;:&lt;/span&gt;&lt;span class="pl-e"&gt;name&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Redth/ZXing.Net.Maui" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I added this myself to the &lt;a href="https://github.com/dotnet/maui-samples/blob/main/6.0/Apps/PointOfSale/src/PointOfSale/Pages/Handheld/ScanPage.xaml" rel="noopener noreferrer"&gt;.NET Point of Sale demo app&lt;/a&gt;, but cut it from the &lt;a href="https://youtu.be/zp3Ja-jAjq4" rel="noopener noreferrer"&gt;.NET Conf demo&lt;/a&gt; due to time. It's there and works if anyone wants to wire it up and polish the UI. :)&lt;/p&gt;

&lt;p&gt;Other's have picked up on this feature and are showing off. &lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/shorts/ajpRN9oihuE?feature=share" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Here Gerald teaches us how to scan all the codes!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ostgj2xB_ok"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Business &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Libraries
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;NFC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A library for .NET MAUI to do Near Field Communication (NFC).&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/franckbour" rel="noopener noreferrer"&gt;
        franckbour
      &lt;/a&gt; / &lt;a href="https://github.com/franckbour/Plugin.NFC" rel="noopener noreferrer"&gt;
        Plugin.NFC
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Cross-Platform NFC (Near Field Communication) plugin to easily read and write NFC tags in your application.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/franckbour/Plugin.NFC/raw/master/art/nfc48.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ffranckbour%2FPlugin.NFC%2Fraw%2Fmaster%2Fart%2Fnfc48.png" alt="NFC logo" title="NFC Logo"&gt;&lt;/a&gt; Plugin.NFC&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A Cross-Platform NFC (&lt;em&gt;Near Field Communication&lt;/em&gt;) plugin to easily read and write NFC tags in your Xamarin Forms or .NET MAUI applications.&lt;/p&gt;
&lt;p&gt;This plugin uses &lt;strong&gt;NDEF&lt;/strong&gt; (&lt;em&gt;NFC Data Exchange Format&lt;/em&gt;) for maximum compatibilty between NFC devices, tag types, and operating systems.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Status&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Build&lt;/th&gt;
&lt;th&gt;NuGet&lt;/th&gt;
&lt;th&gt;MyGet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plugin.NFC&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.azure.com/franckbour/franckbour/_build/latest?definitionId=1" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/81f3696f14c97317640795edd7c56ea8a3d15fb5572a1ea98fad97e971137738/68747470733a2f2f6465762e617a7572652e636f6d2f6672616e636b626f75722f6672616e636b626f75722f5f617069732f6275696c642f7374617475732f506c7567696e2e4e46432d4349" alt="Build status"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.nuget.org/packages/Plugin.NFC" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/18a643781d4a96e035e59649113619a997151c1dd61c0ce3c4ee3bc3e8b54f0c/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f506c7567696e2e4e46432e7376673f6c6162656c3d4e75676574" alt="Nuget"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f860532c50f1ab2f653f3c47e4fb00535c37358eccc6ef8d94895dad097df370/68747470733a2f2f696d672e736869656c64732e696f2f6d796765742f706c7567696e2d6e66632f762f506c7567696e2e4e46432e7376673f6c6162656c3d4d79476574"&gt;&lt;img src="https://camo.githubusercontent.com/f860532c50f1ab2f653f3c47e4fb00535c37358eccc6ef8d94895dad097df370/68747470733a2f2f696d672e736869656c64732e696f2f6d796765742f706c7567696e2d6e66632f762f506c7567696e2e4e46432e7376673f6c6162656c3d4d79476574" alt="MyGet"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;CI Feed : &lt;a href="https://www.myget.org/F/plugin-nfc/api/v3/index.json" rel="nofollow noopener noreferrer"&gt;https://www.myget.org/F/plugin-nfc/api/v3/index.json&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Supported Platforms&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Tested on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;4.4+&lt;/td&gt;
&lt;td&gt;Google Nexus 5, Huawei Mate 10 Pro, Google Pixel 4a, Google Pixel 6a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;11+&lt;/td&gt;
&lt;td&gt;iPhone 7, iPhone 8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Windows, Mac and Linux are currently not supported. Pull Requests are welcomed!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Android Specific&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Add NFC Permission &lt;code&gt;android.permission.NFC&lt;/code&gt; and NFC feature &lt;code&gt;android.hardware.nfc&lt;/code&gt; in your &lt;code&gt;AndroidManifest.xml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight highlight-text-xml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;uses-permission&lt;/span&gt; &lt;span class="pl-e"&gt;android&lt;/span&gt;&lt;span class="pl-e"&gt;:&lt;/span&gt;&lt;span class="pl-e"&gt;name&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;android.permission.NFC&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; /&amp;gt;
&amp;lt;&lt;span class="pl-ent"&gt;uses-feature&lt;/span&gt; &lt;span class="pl-e"&gt;android&lt;/span&gt;&lt;span class="pl-e"&gt;:&lt;/span&gt;&lt;span class="pl-e"&gt;name&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;android.hardware.nfc&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;android&lt;/span&gt;&lt;span class="pl-e"&gt;:&lt;/span&gt;&lt;span class="pl-e"&gt;required&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;false&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; /&amp;gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Add the line &lt;code&gt;CrossNFC.Init(this)&lt;/code&gt; in your &lt;code&gt;OnCreate()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;protected&lt;/span&gt; &lt;span class="pl-k"&gt;override&lt;/span&gt; &lt;span class="pl-smi"&gt;void&lt;/span&gt; &lt;span class="pl-en"&gt;OnCreate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-smi"&gt;Bundle&lt;/span&gt; &lt;span class="pl-s1"&gt;savedInstanceState&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/franckbour/Plugin.NFC" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;DataGrid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This looks brand new.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/akgulebubekir" rel="noopener noreferrer"&gt;
        akgulebubekir
      &lt;/a&gt; / &lt;a href="https://github.com/akgulebubekir/Maui.DataGrid" rel="noopener noreferrer"&gt;
        Maui.DataGrid
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      DataGrid component for Maui
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Maui.DataGrid&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;DataGrid library for .NET &lt;strong&gt;MAUI&lt;/strong&gt; applications.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/akgul.Maui.Datagrid" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/54b964a0d8c6ee197c23cf638aba047f139a41ac81ba3848e09d99554e5f916f/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f616b67756c2e4d6175692e44617461677269642e737667" alt="NuGet version (akgul.Maui.Datagrid)"&gt;&lt;/a&gt;
&lt;a href="https://github.com/akgulebubekir/Maui.DataGrid/actions/workflows/codeql.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/akgulebubekir/Maui.DataGrid/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Supported Platforms&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;li&gt;iOS&lt;/li&gt;
&lt;li&gt;MacCatalyst&lt;/li&gt;
&lt;li&gt;Tizen&lt;/li&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;li&gt;and any other platform that MAUI runs on&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div class="highlight highlight-text-xml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt; xmlns:dg="clr-namespace:Maui.DataGrid;assembly=Maui.DataGrid"
&amp;lt;&lt;span class="pl-ent"&gt;dg&lt;/span&gt;&lt;span class="pl-ent"&gt;:&lt;/span&gt;&lt;span class="pl-ent"&gt;DataGrid&lt;/span&gt; &lt;span class="pl-e"&gt;ItemsSource&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding Teams}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;SelectionEnabled&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;True&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;SelectedItem&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding SelectedTeam}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
                &lt;span class="pl-e"&gt;RowHeight&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;70&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;HeaderHeight&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;50&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;BorderColor&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{StaticResource GridBorderColor}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
                &lt;span class="pl-e"&gt;HeaderBackground&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{StaticResource GridHeaderBgColor}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;HeaderBordersVisible&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding HeaderBordersVisible}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
                &lt;span class="pl-e"&gt;PullToRefreshCommand&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding RefreshCommand}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;IsRefreshing&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding IsRefreshing}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;PaginationEnabled&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding PaginationEnabled}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;PageSize&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;5&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
                &lt;span class="pl-e"&gt;ActiveRowColor&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{StaticResource ActiveRowColor}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class="pl-ent"&gt;dg&lt;/span&gt;&lt;span class="pl-ent"&gt;:&lt;/span&gt;&lt;span class="pl-ent"&gt;DataGrid&lt;/span&gt;.Columns&amp;gt;
        &amp;lt;&lt;span class="pl-ent"&gt;dg&lt;/span&gt;&lt;span class="pl-ent"&gt;:&lt;/span&gt;&lt;span class="pl-ent"&gt;DataGridColumn&lt;/span&gt; &lt;span class="pl-e"&gt;Title&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Logo&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;PropertyName&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Logo&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;SortingEnabled&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;False&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
            &amp;lt;&lt;span class="pl-ent"&gt;dg&lt;/span&gt;&lt;span class="pl-ent"&gt;:&lt;/span&gt;&lt;span class="pl-ent"&gt;DataGridColumn&lt;/span&gt;.CellTemplate&amp;gt;
                &amp;lt;&lt;span class="pl-ent"&gt;DataTemplate&lt;/span&gt; &lt;span class="pl-e"&gt;x&lt;/span&gt;&lt;span class="pl-e"&gt;:&lt;/span&gt;&lt;span class="pl-e"&gt;DataType&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;x:String&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&amp;gt;
                    &amp;lt;&lt;span class="pl-ent"&gt;Image&lt;/span&gt; &lt;span class="pl-e"&gt;Source&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;{Binding}&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;HorizontalOptions&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/akgulebubekir/Maui.DataGrid" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;Daniel published several videos last week on storing data with .NET MAUI. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3xqIXS1SBaU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/xCVZimANTmQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Yi7SAyh7k8w"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  MSAL.NET Authentication
&lt;/h3&gt;

&lt;p&gt;Carl Franklin takes inspiration from our recent .NET Conf and puts together an Azure AD login with MSAL.NET.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/p8NRvakFW2M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Printing
&lt;/h3&gt;

&lt;p&gt;Thomas hooked up a button in the .NET Point of Sale app and added printing with the &lt;a href="https://www.nuget.org/packages/PrinterUtility" rel="noopener noreferrer"&gt;PrinterUtility&lt;/a&gt; NuGet. Nice! Source link below.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1560064472579981314-394" src="https://platform.twitter.com/embed/Tweet.html?id=1560064472579981314"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1560064472579981314-394');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1560064472579981314&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Bliitze/PointOfSale/blob/master/PointOfSale/Helpers/PosPrint.cs" rel="noopener noreferrer"&gt;GitHub Source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>programming</category>
      <category>design</category>
    </item>
    <item>
      <title>Roundup of .NET MAUI. - Week of August 8, 2022</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Mon, 15 Aug 2022 12:42:52 +0000</pubDate>
      <link>https://forem.com/davidortinau/roundup-of-net-maui-week-of-august-8-2022-pm</link>
      <guid>https://forem.com/davidortinau/roundup-of-net-maui-week-of-august-8-2022-pm</guid>
      <description>&lt;p&gt;This was a big week for .NET MAUI content, starting with &lt;a href="https://focus.dotnetconf.net" rel="noopener noreferrer"&gt;.NET Conf: MAUI&lt;/a&gt;! We had a great time bringing your a wide range of .NET MAUI content from our studio in Redmond and speakers around the globe.&lt;/p&gt;

&lt;p&gt;Table of Contents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Books&lt;/li&gt;
&lt;li&gt;Libraries&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  .NET Conf: MAUI
&lt;/h2&gt;

&lt;p&gt;If you only watch one session, I shamelessly must recommend my keynote presentation with Maddy Montaquila. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/zp3Ja-jAjq4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The rest of the 24 sessions are nicely catalogued at &lt;a href="https://docs.microsoft.com/events/dotnetconf-focus-on-maui/" rel="noopener noreferrer"&gt;https://docs.microsoft.com/events/dotnetconf-focus-on-maui/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Books &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Michael Stonis from EightBot has written &lt;a href="https://aka.ms/maui-ebook" rel="noopener noreferrer"&gt;Enterprise Application Patterns with .NET MAUI&lt;/a&gt; and updated the companion &lt;a href="https://github.com/dotnet-architecture/eshop-mobile-client" rel="noopener noreferrer"&gt;eShop application&lt;/a&gt; to go with it.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dotnet-architecture" rel="noopener noreferrer"&gt;
        dotnet-architecture
      &lt;/a&gt; / &lt;a href="https://github.com/dotnet-architecture/eshop-mobile-client" rel="noopener noreferrer"&gt;
        eshop-mobile-client
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      eShop mobile &amp;amp; desktop client built with .NET MAUI
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;eshop-mobile-client&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;eShop sample applications have been updated and moved to &lt;a href="https://github.com/dotnet/eShop" rel="noopener noreferrer"&gt;https://github.com/dotnet/eShop&lt;/a&gt;. Active development will continue there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;eshop-mobile-client&lt;/code&gt; is a reference &lt;a href="https://dot.net/maui" rel="nofollow noopener noreferrer"&gt;.NET MAUI&lt;/a&gt; multi-platform client app whose imagined purpose is to serve the mobile workforce of a fictitious company that sells products. The app allows you to manage the catalog, view products, and manage the basket and the orders.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Dependencies&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Though &lt;code&gt;eshop-mobile-client&lt;/code&gt; mobile app relies on the repo &lt;a href="https://github.com/dotnet-architecture/eShopOnContainers" rel="noopener noreferrer"&gt;eShopOnContainers&lt;/a&gt; for its backend but by default it uses its internal MockServices for all its functionalities. For more details refer to the &lt;a href="https://github.com/dotnet-architecture/eshop-mobile-client#setup" rel="noopener noreferrer"&gt;Setup&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dotnet-architecture/eshop-mobile-clientmedia/eShopOnContainers_Architecture_Diagram.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fdotnet-architecture%2Feshop-mobile-clientmedia%2FeShopOnContainers_Architecture_Diagram.png" alt="eShopOnContainers" width="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Architecture&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The app architecture consists of two parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A .NET MAUI mobile app for iOS, macOS, Android, and Windows.&lt;/li&gt;
&lt;li&gt;Several .NET Web API microservices are deployed as Docker containers.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;.NET MAUI App&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;This project exercises the following platforms, frameworks, and features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.NET MAUI
&lt;ul&gt;
&lt;li&gt;XAML&lt;/li&gt;
&lt;li&gt;Behaviors&lt;/li&gt;
&lt;li&gt;Bindings&lt;/li&gt;
&lt;li&gt;Converters&lt;/li&gt;
&lt;li&gt;Central Styles&lt;/li&gt;
&lt;li&gt;Animations&lt;/li&gt;
&lt;li&gt;IoC&lt;/li&gt;
&lt;li&gt;Messaging Center&lt;/li&gt;
&lt;li&gt;Custom Controls&lt;/li&gt;
&lt;li&gt;xUnit Tests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Azure…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dotnet-architecture/eshop-mobile-client" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Libraries &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  InfiniteScroll
&lt;/h3&gt;

&lt;p&gt;This is a port of Matthew Liebowitz's Xamarin.Forms library that adds this behavior to ListView.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/TheSalLab.MauiInfiniteScrolling" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/TheSalLab.MauiInfiniteScrolling&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scandit Barcode Scanning
&lt;/h3&gt;

&lt;p&gt;Scandit has updated their Xamarin.Forms library for .NET 6.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Scandit.DataCapture.Core.Xamarin.Forms/6.14.0-beta.2#readme-body-tab" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Scandit.DataCapture.Core.Xamarin.Forms/6.14.0-beta.2#readme-body-tab&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  LiveCharts
&lt;/h3&gt;

&lt;p&gt;James Montemagno turned me on to this SkiaSharp based charting library last week at .NET Conf, and I really want to check it out. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/LiveChartsCore.SkiaSharpView.Maui/2.0.0-beta.350" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/LiveChartsCore.SkiaSharpView.Maui/2.0.0-beta.350&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  KeyboardHookLite
&lt;/h3&gt;

&lt;p&gt;I'm stoked for this library that enables a global keyboard listener! This is a common need I have in desktop applications. Right now it only appears to work with Windows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/KeyboardHookLite" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/KeyboardHookLite&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ZXing Barcode Scanner
&lt;/h3&gt;

&lt;p&gt;Our fearless leader Redth has published a .NET MAUI version of his popular barcode scanning library ZXing. It's primarily for mobile, so the Windows bit is just enough to not crash which is perfect me most of our needs.&lt;/p&gt;

&lt;p&gt;If you're poking around in the Point of Sale demo I made for .NET Conf, you'll find this put to use. :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/ZXing.Net.Maui/" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/ZXing.Net.Maui/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ContextMenuContainer
&lt;/h3&gt;

&lt;p&gt;While the official .NET 7 roadmap includes a ContextMenu, you don't need to wait. This library has support for all 4 .NET MAUI platforms today.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/ContextMenuContainer/1.1.0.2-preview#readme-body-tab" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/ContextMenuContainer/1.1.0.2-preview#readme-body-tab&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Videos &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A Better Pub/Sub Messenger
&lt;/h3&gt;

&lt;p&gt;James brings us a quick rundown on using the new Messenger in CommunityToolkit.Mvvm, and I love it! It reminds me a lot of TinyMessenger. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/vD17OetzGXc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  UI Design for .NET MAUI
&lt;/h3&gt;

&lt;p&gt;Leomaris Reyes provides some great tips for honing your skills to create beautiful UI with .NET MAUI.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2uiRwG2wu7E"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Webinar: Create a Shopping UI with .NET MAUI and sqlite
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/EatS5vvo8OY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Roulette Game
&lt;/h3&gt;

&lt;p&gt;Nice, simple example of animation.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/NQiu4KBDkTw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kei-soft/KJunBlog/tree/master/Maui.RouletteGame" rel="noopener noreferrer"&gt;https://github.com/kei-soft/KJunBlog/tree/master/Maui.RouletteGame&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything you need to know about CollectionView
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/J5wC87xE9Qg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawing Graphics Controls
&lt;/h3&gt;

&lt;p&gt;Chris makes it sound so easy, and I suspect it is!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/EUSdVG1njN4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  iOS App Extensions
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/nSHXQP3zVFE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  DrawingView
&lt;/h3&gt;

&lt;p&gt;Gerald introduces you to this powerful, new control for drawing things in .NET MAUI.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7rw13_a5GR0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Roundup of .NET MAUI. - Week of July 18, 2022</title>
      <dc:creator>David Ortinau</dc:creator>
      <pubDate>Tue, 26 Jul 2022 16:18:08 +0000</pubDate>
      <link>https://forem.com/davidortinau/roundup-of-net-maui-week-of-july-18-2022-5606</link>
      <guid>https://forem.com/davidortinau/roundup-of-net-maui-week-of-july-18-2022-5606</guid>
      <description>&lt;p&gt;Table of Contents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Libraries&lt;/li&gt;
&lt;li&gt;Videos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Libraries &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  BlazorBindings.Maui
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dreamescaper/" rel="noopener noreferrer"&gt;Oleksandr&lt;/a&gt; has been contributing to the &lt;a href="https://github.com/dotnet/MobileBlazorBindings" rel="noopener noreferrer"&gt;official experiment project&lt;/a&gt;, and singlehandedly updated it to support .NET MAUI. He even now has documentation on his fork. &lt;/p&gt;

&lt;p&gt;If you fancy writing your .NET MAUI using Blazor components and Razor syntax, check this out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dreamescaper.github.io/MobileBlazorBindingsDocs" rel="noopener noreferrer"&gt;https://dreamescaper.github.io/MobileBlazorBindingsDocs&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;StackLayout&amp;gt;
    &amp;lt;Label FontSize="30"
           Text="@("You pressed " + count + " times")" /&amp;gt;
    &amp;lt;Button Text="+1"
            OnClick="@HandleClick" /&amp;gt;
&amp;lt;/StackLayout&amp;gt;

@code {
    int count;

    void HandleClick()
    {
        count++;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Shiny 3.0
&lt;/h3&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1550858546425876480-328" src="https://platform.twitter.com/embed/Tweet.html?id=1550858546425876480"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1550858546425876480-328');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1550858546425876480&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shinylib.net/" rel="noopener noreferrer"&gt;https://shinylib.net/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  MetroLog
&lt;/h3&gt;

&lt;p&gt;The Frenchman &lt;a href="https://github.com/roubachof" rel="noopener noreferrer"&gt;Jean-Marie Alfonsi&lt;/a&gt; is shipping an updated fork of this popular logging framework from &lt;a href="https://github.com/novotnyllc/" rel="noopener noreferrer"&gt;Claire Novotny&lt;/a&gt; with .NET MAUI compatibility. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/roubachof" rel="noopener noreferrer"&gt;
        roubachof
      &lt;/a&gt; / &lt;a href="https://github.com/roubachof/MetroLog" rel="noopener noreferrer"&gt;
        MetroLog
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A lightweight logging system targeting .Net 6 and beyond.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Logo&lt;/th&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MetroLog.Net6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/roubachof/MetroLoglogo.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Froubachof%2FMetroLoglogo.png" width="200"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.nuget.org/packages/MetroLog.Net6" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/64d67e447ecee487528d94b62567f8ea0310a930497f7ee7e3472551283911f1/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4d6574726f4c6f672e4e6574362e737667" alt="Nuget"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MetroLog.Maui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/roubachof/MetroLoglogo.maui.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Froubachof%2FMetroLoglogo.maui.png" width="200"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.nuget.org/packages/MetroLog.Maui" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/30721514f0a5c8e28d3c5455773c2ca85a4819fab895aa5c9ef282367699ef5a/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4d6574726f4c6f672e4d6175692e737667" alt="Nuget"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Overview&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;MetroLog.Net6 is a .NET 6 lightweight logging framework designed primarily for mobile platforms like &lt;code&gt;iOS&lt;/code&gt; and &lt;code&gt;Android&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The intention is that MetroLog is mostly compatible with &lt;code&gt;NLog&lt;/code&gt;. Both the surface area and internal construction
should just about match.&lt;/p&gt;
&lt;p&gt;However, to achieve better startup performance, configuration is only possible through code.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Configuration&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Configuration is made through the crossing of a &lt;strong&gt;target&lt;/strong&gt; and &lt;strong&gt;log levels&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A target specify how the logs will be stored. For example, you can use the &lt;code&gt;DebugTarget&lt;/code&gt; to show your logs in the debug output
Or you could use the &lt;code&gt;FileSnapshotTarget&lt;/code&gt; to store your logs in a file.&lt;/p&gt;
&lt;p&gt;The log levels describe the level of criticality. You bind each target to a set of log levels.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Standard configuration&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;    &lt;span class="pl-smi"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;config&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; LoggingConfiguration&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
#&lt;span class="pl-k"&gt;if&lt;/span&gt; RELEASE
    config&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;AddTarget&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
        LogLevel&lt;span class="pl-kos"&gt;.&lt;/span&gt;Info&lt;span class="pl-kos"&gt;,&lt;/span&gt; 
        LogLevel&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/roubachof/MetroLog" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Bottom Sheet
&lt;/h3&gt;

&lt;p&gt;I tested this on Mac Catalyst and it works as advertised. Bottom sheets a really pretty easy, and there are a few options out there if you don't fancy building your own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Sm.Maui.BottomSheet" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Sm.Maui.BottomSheet&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Videos&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Images in .NET MAUI
&lt;/h3&gt;

&lt;p&gt;Did you know you can put animated GIFs in your .NET MAUI app? &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/KVBy4Y1N45Y"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Build your own controls in .NET MAUI
&lt;/h3&gt;

&lt;p&gt;This is a super common pattern for creating custom controls. Compose the "in the box" views in .NET MAUI to meet any need. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/mgHbOGXuj0Y"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Stream: Angular in .NET MAUI
&lt;/h3&gt;

&lt;p&gt;What?! I think bringing Angular and React components into .NET MAUI in the same way we enable Blazor is super interesting. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/6dEl4n5MuBo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  .NET DC: Deploy JS Anywhere with .NET MAUI
&lt;/h3&gt;

&lt;p&gt;Alyssa Nicoll is everywhere! &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/B4EYSnV0-rM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows System Tray Icons in .NET MAUI
&lt;/h3&gt;

&lt;p&gt;This is something we did in the &lt;a href="https://github.com/dotnet/maui-samples" rel="noopener noreferrer"&gt;WeatherTwentyOne&lt;/a&gt; app too! Nice touch.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/TxgooETfJug"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Scoped Dependencies in .NET
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/HMN26wQ8J54"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  What's New in Syncfusion Controls for .NET MAUI
&lt;/h3&gt;

&lt;p&gt;Worth the 2 minutes and the awesome music to see what Syncfusion just shipped for .NET MAUI.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/h4zJ0zRkdSs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Stream: Authentication with .NET MAUI
&lt;/h3&gt;

&lt;p&gt;Sam Basu hosts Dan Siegel to discuss implementing auth in .NET MAUI.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/kjRCDzd27oQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit Testing in .NET MAUI
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C9vIDLQwc7M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  A Game Engine in .NET MAUI?
&lt;/h3&gt;

&lt;p&gt;Why Shaun, why?! Because you can.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CjIJh5p5twA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
