12 Simple .NET MAUI Do's and Don'ts
A handy list of .NET MAUI "do's and don'ts" was yesterday provided to viewers of the latest .NET MAUI Community Standup video hosted by Microsoft's Maddy Montaquila and David Ortinau.
Microsoft calls .NET MAUI the evolution of the old mobile-centric Xamarin.Forms in that it adds the ability to create desktop apps in addition to traditional iOS and Android targets.
"There are some things that I have seen folks trying to do with .NET MAUI, and running into troubles," said Ortinau in the video. "And so sure, there are bugs -- file the bugs, please, we're working on the bugs. But there are also some things that I think could help you just follow a happier path, save you a little time."
And here they are as described by Ortinau:
Use Shell or Tabbed/Flyout/Nav Pages, But Don't Use Both
Why not? Because it's incompatible.
"They weren't designed to work together," Ortinau said. "Could they work together in the future? Sure. Do some things work? Uh, a little bit, but you're going to run into tons of edge cases and weirdness if you try to use both Shell and tab page, flyout page navigation page."
In fact, Visual Studio displays: "Warning TabbedPage is incompatible with .NET MAUI Shell apps, and an exception will be thrown if you attempt to use TabbedPage in a Shell app."
Set MainPage Once
Ortinau recommended to set the main page once, "or your mileage may vary." Swapping out the main page caused problems in Xamarin and was commonly seen as something to be avoided, but Ortinau acknowledged developers have demonstrated reasons to do it. So while it's better to have that ability, the practice is "fraught with troubles."
"So I would recommend instead, look for different ways to utilize Shell to customize configurations for the different looks that you're going for. So I know that's a little vague. There are samples out there in particular on my repo that showcase different ways to do things with Shell."
Don't Nest Tabs
"Just don't nest tabs, like you're going to run into problems. Instead, I would recommend evaluating the user experience and considering different approaches. Like, there's probably a better way. If things don't just work, there's probably a decent reason why that wasn't part of the design of that navigation pattern."
That prompted some conversation with Montaquila, who has noticed the practice -- for example, putting top tabs inside a bottom tab -- seemed to work. Ortinau agreed that wasn't "a big no-no" and reiterated his "if it just doesn't just work" rule of thumb.
Reference MauiImages as PNG
"So you put a bunch of SVGs is inside of your images folder and you're trying to use them in your app, you might start thinking, 'well, it's called SVG in the file system, so I'm going to say SVG.' It's not really an SVG if it's using MauiImage, because what happens is we actually render those. So the SVG is really the the source file, and we're generating the densities, all the different variations of the PNG. If you need to use an SVG directly, so you can resize it at runtime, then look at one of those libraries [like Vapolia.Svg] or SkiaSharp itself. And you'll have better success there -- you'll actually have success there, because you're not going to get it when you're doing a MauiImage."
Don't Nest Unconstrained Scrolling Things Inside a Stack Layout
"Alright, so this one may seem a little confusing. I tried to make it as concise as possible, but don't nest unconstrained scrolling things. So what's an unconstrained scrolling thing? A scrolling thing is a collection view, scroll view, list view. It scrolls, right? It can scroll vertically, it can scroll horizontally. In some cases, it can scroll all ways. Unconstrained means it doesn't have a predefined size. You haven't explicitly said, 'this is the size that you should be.' Don't do that inside of a stack layout. Why is that specifically problematic? In MAUI, it's problematic because the stack is going to let children take up as much size as they want. So what happens is, your collection view has a thousand or ten thousand items in it, and you expect that to be virtualized, and you expect it to only take up the vertical space of the screen. But the vertical stack layout, or the stack layout, isn't going to do that because it's inside of a scroll. And it's just going to let that thing just keep going and going and going. And you're going to be like, 'Why did my page take forever to load?'"
Ortinau did list one caveat concerning the direction of scrolling such that if they're different (a child that scrolls horizontally inside a vertical scroll, for example) it might work. "But in all cases, consider explicitly constraining the height or the width depending on which way you're scrolling, so that so that you don't get into trouble."
Customize Handlers in ConfigureHandlers()
"Oh, this is a good one, customizing handlers in the ConfigureHandlers method of the host builder. This is just a really good pattern to follow." Ortinau said this was recommended by a developer "because the append, modify, etc. overrides -- not really overrides, but extension methods, points of extension -- only run once, and they need to be there and in place ready to go before the handlers are first created. So by doing it in the ConfigureHandlers, you are in the safest place, the best place to do that."
Don't Nest Elements with Gesture Handlers
This one brought up extensive discussion too long to detail here, but Ortinau summed it up:
"So why is this problematic? A control with a gesture recognizer inside of a layout that also has a gesture recognizer is a recipe for, 'uh oh, why are my gestures not getting caught or handled at the appropriate level?' They're probably in conflict. The same problem goes for when you're z-indexing if you've got a grid with multiple things that overlap, and then you're like, 'why is my button not clickable?' Or this is a problem currently, and it is a bug, in Shell with the flyout, where you have a header, and then the first item in the list is not clickable. It is touchable clickable if you get just south of the header, then it actually triggers. The problem is is that the header is currently overlapping and laying on top of that first item on some platforms, maybe not on all platforms. So that's actually why that's happening. And you can fix that by doing input transparent. Input transparent allows you to pass control pass gesture events, down through a control or layer."
Move Simple Renderers to Handler Modifications
"So full renders -- a lot of code. And sometimes it's a lot of code just to do one line of mod to a control. Consider just moving that to a handler modification, and by those I mean, the thing we just mentioned previously ... append or modify, put that in the MauiProgram.cs, it's a whole lot easier to maintain a couple lines of code than it is a whole class. Much simpler, yes."
Rewrite Custom Controls as Handlers with Mappers
"So these are mostly do's here. So you're like, 'hey, but I have a custom control renderer? Do I really need to go to a handler mapper?' No, you don't have to. Most of your renderer code will work as is. But you may find during your migration that something may not work as expected. And your effort is probably better spent doing the ports to the handler pattern. It is something you'll be able to maintain for a longer period of time. And it's actually not as hard hard as you think. Once you get through the first one and you figure out the pattern. It's pretty straightforward."
Prefer Grid with Explicit Size and * in DataTemplates
This came from another developer who didn't reply when Ortinau asked him about the "why."
"But I think I think the reason is, is that if you use auto and you use stack layouts inside of a data template, for reasons I already mentioned about how stack layout does sizings of things, you're probably not going to get the size and layout you expect. So probably bugs be there. But you can avoid them by being explicit with your sizing and using grids and stars."
Use MauiSplashScreen and MauiIcon for the Simple Stuff
The advice here is to "Use native implementations for anything more complex."
"Don't try to -- I see folks trying to do crazy stuff with MAUI splash screen. It's really just there to be a quickstart, super-simple for like, basic cases. If you're gonna do something funky, and a bunch of layout and everything, I know you can technically get inside of an SVG and do all kinds of crazy stuff there. You're probably better off just going back to the native implementations for those things."
Use ProjectReference, not NuGets for Your Libraries
"So this is for like your internal stuff, right? It's a whole lot easier, and a lot more is going to be available to you in what is supported if you just keep your library in a separate repo, cool. Check it out to the full solution you're working in, shared across all your different apps, fine. But that project reference is going to work so much better. When you try to package things as NuGets for sharing purposes, if you really don't know exactly what you're doing, and you don't have a PhD in NuGetology, you're going to run into things not working and you're going to get frustrated. Whereas just straight project reference -- don't overcomplicate it, this is probably the the thing I'll end on. Just don't overcomplicate it, don't be clever. Like do the thing that just works. And, you know, you're gonna be much happier -- your whole team's gonna be much happier in the long run."
David Ramel is an editor and writer for Converge360.