Tag Archives: .NET MAUI

Introducing DayFrame: Everything I Check All Day, in One Window

The last thing I wrote here was part 1 of my .NET MAUI Hybrid journey, back in April 2024. Well… that journey got a LOT bigger than I expected. For the last three months I’ve been building a Windows desktop app live on stream almost every day, and today it’s in the Microsoft Store. Let me introduce you to DayFrame.

Get DayFrame from the Microsoft Store

Too Many Tabs

Count the things you check before lunch. For me it’s two email accounts, my calendar, a handful of GitHub repositories, Bluesky and Mastodon, some RSS feeds, the weather, and (I’m not proud of this) the stock market and the scores. That’s a browser full of tabs, three or four apps, and a phone I keep picking up to see if anything changed. Most of the time, nothing had.

I wanted ONE window that tells me what’s new before I click on anything. Something with the density of Outlook on the desktop, not a phone app stretched across a 27-inch monitor.

I also wanted it to be mine. I didn’t want another service holding my mail, another account to create, or another app quietly phoning home. If something is going to sit open on my desktop all day, it should work for me and nobody else.

It Started With a Chat Message

On July 23rd I was live on my Twitch and YouTube channels, talking about AI agents and what they could do for us, when Egil Hansen dropped a comment in chat: think about what we could have had if the Windows Phone aggregation of calendar, text, email and photos was still a thing. Here’s the moment, cued up for you:

Egil said Windows Phone. I heard live tiles. If you had a Windows Phone, you remember that Start screen: tiles that flipped over to tell you what was waiting before you ever opened an app. I made the first commit that evening.

The plan was an Outlook-style email client with a navigation pane down the left. Two days later that pane was a rail of live tiles and the email tile had an unread badge. Then I made it flip to show the newest sender. Then I noticed I was reading the tile instead of opening my email. Ok.. so the rail wasn’t navigation anymore. The rail was the product.

So What Is DayFrame?

DayFrame is a Windows desktop app. Down the left side is a rail of live tiles, one for each module. Each tile tells you what’s new: unread mail, your next meeting, the temperature outside, the market close, the score of the game. Click a tile and that module fills the rest of the window.

Live tiles show what’s new before you click

These modules are on from the first start:

  • Email: Gmail, or any IMAP account (Yahoo, AOL, iCloud, Posteo, Mailbox.org, Zoho or your own
    server), with folders, search and a reading pane
  • Calendar: day, week and month views from Google or any CalDAV server
  • Contacts: your address book from Google or any CardDAV server
  • Notes: a folder of Markdown notes with links between them. Point it at your Obsidian vault and
    it just works
  • Time: clocks for the time zones you work across
  • Weather: forecasts for the places you care about
  • RSS: your news and blog feeds, with the full article in a reading pane

And these are a click away in Settings when you want them:

  • GitHub: issues, pull requests and notifications for the repositories you watch
  • Asana: your tasks. Complete them, comment on them and reschedule them right from DayFrame
  • Bluesky and Mastodon: your timelines and notifications
  • Twitch: whether you’re live, who’s watching, your schedule, and which channels you follow are
    live right now. Yes, I built this one for me, and it’s on my second monitor during every stream
  • Market News: quotes, charts and headlines for your watchlists
  • Sports: scores, schedules and news for your teams

Two more things I love. Boards let you build your own dashboard out of widgets from the other modules: your inbox next to today’s calendar next to the weather next to your open pull requests. Drag them anywhere, resize them, and pin the whole board to the rail as one wide tile. And you can pin any place to the rail: one mail folder, a single feed, a repository, a stock ticker, your favorite team. It gets its own live tile.

Make It Yours!

Here’s where I’ve had the most fun. If you’re going to spend hours a day in an app, you should enjoy looking at it, and that’s been a priority from the very first week.

The basics are all there: light and dark themes (or follow Windows), text size, zoom on each pane, one, two or three columns of tiles, and the tiles in whatever order you drag them into. You can recolor every tile at once, or give each module its own background, text color and flip speed.

But then there are the tile materials. A tile doesn’t have to be a flat square of blue. You can pick a gradient like Ocean, Sunset or Aurora, or a texture: wood grain, marble, brick, cork board, denim, chalkboard, brushed metal, carbon fiber… and then it gets silly. There are six colors of glitter and a Holographic Foil, and a glint catches the light on one of them every couple of seconds. There’s Dragon Scales. And there’s Green Slime, which drips now and then and wobbles when you click it. I regret nothing.

The same tile rail dressed up in materials: Calendar in purple glitter, Email in wood grain, Notes in dripping Green Slime, RSS in holographic foil, Time in red dragon scales, Weather in turquoise water, GitHub in carbon fiber and Sports in gold glitter

Yes, that tile is made of slime

Boards get the same treatment. Pick a ready-made style like Paper, Midnight, Forest, Mermaid, Dragon, Neon Tokyo or (for those of us who remember Windows 3.1) Hot Dog Stand. Or choose your own fonts, colors, borders and a background image of your own. There’s a Check contrast button that finds any text that would be hard to read against your background, because your board should look great AND be readable.

A board called My Day in the Dragon style, red and black scales behind widgets with red headers and ember-orange borders: a London clock, current conditions at 64°F, recent .NET Blog items, and My scores showing Dallas at Philadelphia at 8:15 PM

A board with the Dragon style applied

Your Stuff Stays Yours

This is the part I care about most:

  • There is no DayFrame server. The app runs on your PC and talks directly to the services you
    connect. No account to create, no telemetry.
  • Your data stays on your computer. Your sign-ins and everything DayFrame downloads stay on
    your machine.
  • Email and articles are sandboxed. They render in an isolated frame with scripts turned off,
    and remote images (including those sneaky one-pixel trackers in marketing email) stay blocked
    until you allow them for that one message.

The privacy policy lists every service each module talks to and what it sends.

A Quick Note About Google Sign-In

Google is still reviewing DayFrame. Until that’s done, Google shows an “unverified app” warning on the consent screen (choose Advanced, then Go to DayFrame) and caps the app at 100 users. If you hit that cap, you can still add Gmail as an IMAP account with an app password, and that route has no limit. Calendar and Contacts from Google need the regular sign-in, but any CalDAV or CardDAV server works too.

What’s Next?

1.0 is a starting line, not a finish line. Here’s some of what’s on my list:

  • Search across everything. One search box that finds that thing you know you saw somewhere,
    whether it was an email, a note, a feed article or a GitHub issue.
  • More email. More than one Google account, a combined “All inboxes” view, and opening the
    attachments people send you.
  • Real Windows notifications that take you straight to the message or event when you click them.
  • More from Boards. Pop a board out into its own window on your second monitor, switch boards
    automatically by time of day, and share a board you’ve built as a pack someone else can import.
  • More privacy controls. A list of every request DayFrame has made, and an Offline switch that
    stops all of them.
  • New modules. Photos from the cloud libraries you already use, a task list that ties into your
    mail and calendar, and more chat and social networks.
  • Tablets. Yes, really. The work to get DayFrame running on Android tablets and iPads has started.

And of course, more tile materials. I have ideas. Some of them are probably bad ideas. The slime
needs to slosh around when you move the window, for one.

What gets built first is up to you, though. The feedback board at feedback.getdayframe.app is where you can suggest a module or vote on what comes next.

For My Fellow .NET Developers

DayFrame is a .NET MAUI Blazor Hybrid app on .NET 10. The window, the tile rail and the toolbar are native WinUI through MAUI, and every module’s interface is Blazor running in a BlazorWebView, with both halves sharing one dependency injection container. Did I pick Blazor after a careful bake-off? Nope. I know Blazor and I could move fast with it, and I’d make the same call again.

The one rule that made all of this possible: modules never reference each other. Every module
depends only on a shared contracts library, and an architecture test fails the build if that ever
changes. That’s why the newest module costs about as much to add as the fourth one did.

The live tile itself started as Eduardo Gomez’s MIT-licensed TileX.Maui. I’ve extended it a lot, but the tile that flips was his. Thank you, Eduardo!

I want to write more about how DayFrame is put together: the module architecture, testing a Blazor
Hybrid app with Playwright, and building most of it with an AI coding agent that works under some
very strict rules. Which one should I write first?

Get It!

DayFrame 1.0 runs on Windows 10 (version 1809 or later) and Windows 11, on x64 and ARM64.

Get DayFrame from the Microsoft Store, or search for “DayFrame” in the Store app. The Store keeps it up to date for you.

If something breaks, Application → About → Copy diagnostics puts everything I need on your
clipboard (without any of your account details) so you can paste it into a bug report.

So… what would you put on your rail? Let me know in the comments below!

Come hang out and watch the next feature get built, live on Twitch.

Seamless Navigation in .NET MAUI Hybrid Apps – My .NET MAUI Hybrid Journey (part 1)

I’ve always wanted to build a native application with all the insight and expertise that I’ve accumulated in building web applications, it’s felt like a big jump for me to get into native mobile or desktop applications. A long time ago I used to build windows forms applications for my employer, and I haven’t worked a desktop application since.

With the advent of .NET MAUI and the ability to build Blazor applications for a website and share their pages and components for use in a native application felt like a real possibility for me. I haven’t really stepped into building and working working with this application model yet… until now.

This is the first of what I expect will be a series of blog posts describing things that I’ve discovered, challenges that I’ve overcome, and features as I migrate the TagzApp application to run as a native desktop application.

Menus – Not as Easy as They Look

The first feature that I wanted to bring over was going to be the layout of the application. In TagzApp I have a very simple layout with a header menu bar with a couple of options to allow you to navigate to other areas of the application. It’s not too complex but it felt like an easy piece to migrate over to the hybrid application.

In doing some research and thinking more about how I want to represent a menubar inside of a native application it felt like it made more sense to turn this into a native menubar and not an HTML menubar that lived inside of my Blazor application. I started doing some research about how to create a menubar in .NET MAUI and found a few examples that showed how to use a tab bar to create and use multiple BlazorWebView components to represent different sections of the application. This felt clumsy to me because it meant that I would be spinning up multiple browsers to run inside of my application just to access and work with other parts of the application. I knew that that would mean more resources used by the computer when this application is running, and that felt a little irresponsible for me as a developer.

I wanted to actually have a menu bar with items that you would click and it would navigate inside of my Blazor application. Looking at the documentation for the BlazorWebView, there is no direct access to the NavigationManager or an ability to reset the location of the browser component. I set about to make the NavigationManager inside of Blazor accessible to the .NET MAUI application.

In the demos on this post, I’ll start with the default Blazor Hybrid template application and turn the vertical NavBar element into a native menu. Completed source code for this sample is available on my GitHub. I also have recorded a video where I talk through this demo:

The default experience inside a Blazor Hybrid application with .NET 8

Configuring the Shell and Menu Items

To start with, I configured the App.xaml file to have a Shell embedded directly and contain the BlazorWebView for my application. This would allow me to add a Menubar to the Shell.

<Application.MainPage>
  <Shell>
    <ShellContent>
      <ShellContent.ContentTemplate>
        <DataTemplate>
          <ContentPage>
            <BlazorWebView x:Name="blazorWebView1" HostPage="wwwroot/index.html">
              <BlazorWebView.RootComponents>
                <RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}" />
              </BlazorWebView.RootComponents>
            </BlazorWebView>
          </ContentPage>
        </DataTemplate>
      </ShellContent.ContentTemplate>
    </ShellContent>
  </Shell>
</Application.MainPage>

I also removed the call inside App.xaml.cs to set MainPage = new MainPage(); Since we’re specifying our own MainPage inside the XAML markup, there’s no need to instantiate another page. I could run the application now, and I’d get the same user experience as the previous image.

Ok.. next steps…

Adding a MenuBar component

In .NET MAUI, the MenuBar component is added when you introduce MenubarItems. No problem, I added a MenuBarItem and 3 MenuFlyoutItems for the 3 base pages inside the default application. This code was added just inside the ContentPage element in App.xaml

<ContentPage.MenuBarItems>
  <MenuBarItem Text="Content">
    <MenuFlyoutItem Text="Home" Clicked="MenuItem_Clicked"></MenuFlyoutItem>
    <MenuFlyoutItem Text="Counter" Clicked="MenuItem_Clicked"></MenuFlyoutItem>
    <MenuFlyoutItem Text="Weather" Clicked="MenuItem_Clicked"></MenuFlyoutItem>
  </MenuBarItem>
</ContentPage.MenuBarItems>

Notice that I set each of the menu items to trigger the same event, MenuItem_Clicked All of these menu items do the same thing, but vary in the location they target. We’ll write this method in a little bit, because we need to first make the NavigationManager available

Enabling the NavigationManager in .NET MAUI

The Blazor NavigationManager isn’t directly accessible in .NET MAUI. You can’t inject it or reach into the BlazorWebView and interact with it. Instead, we need to create a service that will allow us to capture the NavigationManager and interact with it. The curious part of this is that both parts of the application model, .NET MAUI and Blazor use the same dependency injection services. So…. we can exploit this to allow our service to be injected into both Blazor AND .NET MAUI.

No problem, I can whip up a little bit of code that allows both application models to work with the Blazor NavigationManager:

public class NavigatorService
{

  internal NavigationManager NavigationManager { get; set; }

}

I can then register this NavigatorService with the service locator in .NET MAUI with this line in the MauiProgram.cs file:

builder.Services.AddSingleton<NavigatorService>();

I want this Navigator service on every page in my Blazor application, so I’ll inject it and configure the NavigationManager we’ll use inside the MainLayout.razor file:

@inherits LayoutComponentBase
@inject MauiApp1.NavigatorService NavigatorService
@inject NavigationManager NavigationManager

<div class="page">
...
</div>

@code {

  protected override void OnInitialized()
  {

    NavigatorService.NavigationManager = NavigationManager;

  }

}

Finally, I’ll add the NavigatorService to my App.xaml.cs code so that it is injected and stored as a property for use later:

public partial class App : Application
{
  public App(NavigatorService navigatorService)
  {
    InitializeComponent();
    NavigatorService = navigatorService;
  }

  internal NavigatorService NavigatorService { get; }

  private void MenuItem_Clicked(object sender, EventArgs e)
  {
  }
}

Connecting and Navigating from the MenuBar

Now, we can use a switch statement to configure the navigation of the BlazorWebView. Let’s add that switch inside the Menuitem_Clicked method:

private void MenuItem_Clicked(object sender, EventArgs e)
{

  var menuItem = (MenuItem)sender;
  var url = menuItem.Text switch
  {
    "Counter" => "/counter",
    "Weather" => "/weather",
    _ => "/"
  };
  NavigatorService.NavigationManager.NavigateTo(url);

}
Application with the new MenuBar

Now, when we click the various items in the native MenuBar, the browser navigates appropriately.

For completeness, I removed the side navigation from the MainLayout.razor file so that the application felt more native and didn’t have 2 MenuBars.

Summary

This is just one creative way to connect our Blazor application to .NET MAUI and reuse the code we’ve already built in Blazor. The complete source code for this sample is available on my GitHub. I’m working through an entire application for TagzApp, and will share more of my findings in the weeks ahead.

Have you tried using Blazor content in .NET MAUI, WPF, or Windows Forms? What was your experience? Let me know in the comments below

KlipTok running on an Android emulator on LInux

I built an Android app on my Linux machine using .NET 7 and MAUI

I’ve been tinkering and preparing to build a mobile app for the KlipTok website for the past few months. KlipTok is a personal project, and a joy to work on when I have an hour here and there to spend on the site. The most requested feature I have, is for a mobile app to complement the site, and this week I started focusing on that effort… and was even able to build an Android app using .NET MAUI, Blazor, and my Linux laptop. In this post, I’ll describe how I went about getting the development environment working on Ubuntu Linux.

NOTE: Your experience might be different

Every Linux system is different, and I can point you in the direction that worked for me.  I cannot guarantee that you will find similar success or answer questions for your configuration.

I started this project on my Twitch stream on Friday December 2nd, and was able to get a simple Blazor MAUI application that played a Twitch clip running on my Windows machine.  I used Visual Studio 2022 and the Android emulator that came with it and was happy to have an initial proof-of-concept application running.

I prefer to use my Linux laptop from KFocus when I am not in my home office. It’s a great machine with plenty of power for development. With a quick web search, I found an article where someone was setting up Android development on Linux.  I read through those steps, updated to .NET 7, and wrote a few scripts to help with my development.  Here’s what I did:

  1. I installed Android Studio from the Ubuntu app repository using the Discover app.
  2. I opened Android Studio and grabbed the SDK folder from the Android SDK Manager tool, depicted below.  In my case, its sitting in /home/csharpfritz/Android/Sdk.  Android Studio SDK Manager screen
  3. Installed the .NET MAUI workload into .NET 7 at the command-line with the command: `sudo dotnet workload install maui-android`
  4. Installed the Android Debug Bridge (ADB) with `sudo apt install adb`
  5. Started and ran the emulator from Android Studio, verifying that it was running with `adb devices`

Android Emulator running on Linux

Now that I have the emulator running, I could build and deploy my application to the emulator with the command:

dotnet build -t:Run -f net7.0-android /p:AndroidSdkDirectory=/home/csharpfritz/Android/Sdk

On first test, it failed miserably and complained about not supporting MacCatalyst and iOS development on Linux.  Not a problem, so I updated the csproj file to have supported frameworks like the following:

<TargetFrameworks>net7.0-android;</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('osx'))">
  $(TargetFrameworks);net7.0-ios;net7.0-maccatalyst
</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">
  $(TargetFrameworks);net7.0-windows10.0.19041.0
</TargetFrameworks>

When I now build on Linux, just the Android configuration runs.  I tried to recompile and got errors about the compiler not finding the Java jar tool.  Easy enough, I installed the latest tool with this command:

sudo apt install openjdk-11-jdk-headless

I compiled and was able to deploy to the emulator in 55 seconds!  Progress!

KlipTok running on an Android emulator on LInux

Last steps, I wanted to get the app building and recompiling with dotnet watch.  This way, I can make changes and the app will reflect those changes in the emulator.  My dotnet watch command is:

dotnet watch build -t:Run -f net7.0-android /p:AndroidSdkDirectory=/home/csharpfritz/Android/Sdk

I found that the app didn’t rebuild and deploy when I made changes to the razor files.  Easy enough, I made another addition to my csproj file to include a definition for watching the razor files:

<ItemGroup>
  <Watch Include="**\*.razor" />
</ItemGroup>

With this patched, I was able to build and run the app in watch mode.  As I changed razor files, the app would redeploy to the emulator in 6 seconds!  That’s such a better development experience.  Last thing that I tried, was to add a switch to my script to shut off the analyzers when I’m in watch mode:

dotnet watch build -t:Run -f net7.0-android /p:AndroidSdkDirectory=/home/csharpfritz/Android/Sdk /p:RunAnalyzers=false

That didn’t give me a big boost in performance, but I feel better without the analyzers running when I’m in watch mode.

Have you tried building an Android app on Linux with MAUI?  Do you have any tips or tricks for developers to use in order to have a better experience?  Share with us in the comments below