Als Microsoft .NET MAUI erstmals ankündigte, wurde das Cross-Plattform-Framework als „Evolution von Xamarin.Forms, von mobilen erweitert auf Desktop-Szenarien“ vorgestellt.
SIE LIEBEN UI?
Entdecken Sie die BASTA! Tracks
Auch wenn es die versprochene Erweiterung mit .NET 6 schon gab – schließlich lief MAUI bereits unter Windows und macOS –, wirkte die Desktopumsetzung an einigen Stellen noch unfertig. Umso erfreulicher ist es, dass der Fokus der neuen Funktionen von .NET MAUI klar im Desktopbereich liegt.
Fensterbauer
Das Öffnen mehrerer Anwendungsfenster ist aus der Desktopentwicklung seit über 30 Jahren nicht mehr wegzudenken. Unter .NET MAUI und dem Vorgänger Xamarin.Forms war es aufgrund der ursprünglichen Fokussierung auf mobile Betriebssysteme jedoch nicht ohne weiteres möglich. Mit .NET 7 gibt es nun den in Listing 1 gezeigten plattformübergreifenden Weg zum Öffnen neuer Fenster über die Methode OpenWindow des Application-Objekts.
Listing 1
private void OpenDetailsPageInNewWindow(Session session)
{
var detailsVM = new SessionDetailPageViewModel
{
Session = session
};
var sessionPage = new SessionDetailPage(detailsVM);
var window = new Window(sessionPage);
Application.Current.OpenWindow(window);
}
Die neue Funktionalität, die Sie in Abbildung 1 im Einsatz sehen, kann unter Android, macOS und iPadOS (also iOS auf dem iPad) genutzt werden. Auf dem iPhone können keine weiteren Fenster geöffnet werden.
Das Öffnen neuer Fenster ist nicht die einzige Verbesserung im Fensterumfeld, die .NET MAUI unter .NET 7 mitbringt. Unter Windows können außerdem Fenstergröße und Fensterposition konfiguriert werden, wie Listing 2 zeigt. Der abgebildete Quellcode läuft so jedoch nur unter Windows. Unter macOS gibt es zumindest einen Workaround, über den die Fenstergröße gesetzt werden kann, unter Android und iOS ist es gar nicht möglich. Eine detaillierte Anleitung zum Workaround finden Sie unter [1].
Listing 2
protected override Window CreateWindow(IActivationState activationState)
{
var window = base.CreateWindow(activationState);
// Fensterposition setzen
window.X = 300;
window.Y = 100;
// Größe des Fensters definieren
window.Width = 700;
window.Height = 800;
return window;
}
ZUM NEWSLETTER
Regelmäßig News zur Konferenz und der .NET-Community
Menüs und ToolTipps
Bereits unter Xamarin.Forms gab es das SwipeView-Element, mit dem über eine horizontale Wischgeste ein seitliches Kontextmenü, ähnlich wie man es von E-Mail- oder Messenger-Apps auf dem Mobiltelefon kennt, öffnen kann. Dieses Feature gibt es auch unter .NET MAUI, nur ist es leider für die meisten Desktopnutzer:innen unbenutzbar. Denn eine Wischgeste zum Öffnen eines Menüs ist unter Desktopbetriebssystemen im Unterschied zu Mobilgeräten unüblich und dürfte daher von den meisten Benutzer:innen übersehen werden. Außerdem lässt sich diese Geste nicht mit der Maus durchführen, sondern nur auf einem Windows-Gerät mit Touchscreen.
Microsoft löst das Problem mit den in Abbildung 1 gezeigten neuen Kontextmenüs. Sie lassen sich unter Windows und macOS wie gewohnt über einen Klick auf die sekundäre Maustaste öffnen. Unter Android und iOS stehen sie nicht zur Verfügung. Wie Listing 3 zeigt, werden Kontextmenüeinträge durch MenuFlyoutItem-Elemente umgesetzt, die über einen Text, ein Command oder einen Click Event Handler sowie ein Icon verfügen können. Icons werden jedoch nur unter Windows, nicht aber unter macOS unterstützt. Neben MenuFlyoutItem-Elementen gibt es noch den MenuFlyoutSeperator zur Anzeige einer Trennlinie und das MenuFlyoutSubItem-Element zur Definition von Untermenüs.
Die neuen ToolTips sind übrigens ein eleganter Weg, die Benutzer:innen dezent darauf hinzuweisen, dass ein Kontextmenü geöffnet werden kann. Genau wie Kontextmenüs, die Sie auch in Listing 3 sehen, können ToolTips zu jedem Bildschirmelement hinzugefügt werden.
Listing 3
<Grid RowDefinitions="25, auto"
ToolTipProperties.Text="Kontextmenü über Rechtsklick öffnen">
<Label Text="{Binding Time}" />
<Label Text="{Binding Title}" Grid.Row="1" FontSize="Subtitle" FontAttributes="Bold"/>
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Favorit" Command="{Binding FavoriteCommand }" />
<MenuFlyoutItem Text="In neuem Fenster öffnen" Command="{Binding OpenNewWindowCommand }" >
<MenuFlyoutItem.IconImageSource>
<FontImageSource FontFamily="FA-6-Solid-900" Glyph="{x:StaticResource IconUpRightFromSquare}" Color="{AppThemeBinding Light=Black, Dark=White}" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutSeparator/>
<MenuFlyoutSubItem Text="Sprecher anrufen">
<MenuFlyoutItem Text="Geschäftlich" Command="{Binding CallSpeakerBusinessCommand }"/>
<MenuFlyoutItem Text="Privat" Command="{Binding CallSpeakerPrivateCommand }" />
</MenuFlyoutSubItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
</Grid>
Mausinteraktionen
Auf mobilen Geräten wird primär der Finger zur Interaktion mit Apps genutzt. Neben Tipp- sind Wischgesten die häufigsten Aktionen, die man hier ausführt. Maus-Events wie MouseOver, MouseEnter oder MouseMove konnten bisher unter .NET MAUI genau wie zuvor unter Xamarin.Forms aufgrund der Fokussierung auf Fingereingaben auf Mobilgeräten nicht behandelt werden.
Unter .NET 7 können Entwickler:innen nun auch die folgenden Mausgesten behandeln:
-
PointerEntered: Die Maus wird erstmalig über einem Element positioniert.
-
PointerMoved: Die Maus wird über einem Element bewegt.
-
PointerExited: Die Maus verlässt ein Element.
Zusätzlich zu den drei Gesten kann nun auch der sekundäre bzw. Rechtsklick behandelt werden. Listing 4 zeigt die Registrierung der entsprechenden Event Handler im XAML-Code, Listing 5 eine beispielhafte Behandlung in C#. Im gezeigten Beispiel wird die Farbe eines Rechtecks über eine Farbanimation verändert, sobald Benutzer:innen die Maus über das Rechteck bewegen oder sie wieder hinausbewegen.
Listing 4
<Rectangle BackgroundColor="{StaticResource Primary}"
WidthRequest="300"
HeightRequest="200"
HorizontalOptions="Center"
VerticalOptions="Center"
ToolTipProperties.Text="Hover und Klick">
<Rectangle.GestureRecognizers>
<TapGestureRecognizer Buttons="Primary" Tapped="PrimaryTapped" />
<TapGestureRecognizer Buttons="Secondary" Tapped="SecondaryTapped" />
<PointerGestureRecognizer
PointerEntered="OnPointerEntered"
PointerExited="OnPointerExited"
PointerMoved="OnPointerMoved" />
</Rectangle.GestureRecognizers>
</Rectangle>
<Label x:Name="PositionLabel" Text="Pos:"/>
Listing 5
private async void SecondaryTapped(object sender, TappedEventArgs e)
{
await DisplayAlert("Click", "Secondary", "OK");
}
private async void OnPointerEntered(object sender, PointerEventArgs e)
{
var rectangle = sender as Rectangle;
await rectangle.BackgroundColorTo(Colors.Red);
}
private async void OnPointerExited(object sender, PointerEventArgs e)
{
var rectangle = sender as Rectangle;
await rectangle.BackgroundColorTo(Color.FromArgb("#512BD4"));
}
private void OnPointerMoved(object sender, PointerEventArgs e)
{
Point? windowPosition = e.GetPosition(null);
PositionLabel.Text =
$"Position: x: {windowPosition.Value.X} - y: {windowPosition.Value.Y}";
}
Wer die Änderung der Optik eines Steuerelements, sobald die Maus darüber bewegt wird, lieber deklarativ durchführt, kann dazu den neuen PointerOver VisualState des VisualStateManager nutzen. In Listing 6 sehen Sie, wie es geht. Im gezeigten Beispiel wird die Hintergrundfarbe eines Buttons auf Rot geändert, sobald die Maus über den Button bewegt wird. Eine ausführliche Dokumentation zu Visual States unter .NET MAUI gibt es unter [2].
Listing 6
<Button Text="Beweg die Maus über mich!">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Blue" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Button>
Karten
Alle bisher in diesem Artikel vorgestellten neuen Funktionen bezogen sich auf die Entwicklung von Desktop-Apps mit .NET MAUI. Die letzte neue Funktion, die Darstellung von Karten, wird unter macOS, Android und iOS unterstützt. Unter Windows wirft die Kartenkomponente einen Laufzeitfehler, da das zugrunde liegende UI-Framework WinUI 3 keine Kartenkomponente besitzt. Auf den drei anderen Betriebssystemen wird jeweils das native Kartensteuerelement des Betriebssystems genutzt. Abbildung 2 zeigt zum Beispiel die Nutzung von Google Maps unter Android.
Mit dem Map-Element können Sie im Code auf verschiedene Weise interagieren. Listing 7 zeigt, wie Sie den gezeigten Kartenausschnitt deklarativ über die Eigenschaft MapSpan festlegen können. In Listing 8 sehen Sie, wie Sie per C#-Quellcode einen Pin zur Karte hinzufügen können.
Listing 7
<maps:Map x:Name="map">
<x:Arguments>
<MapSpan>
<x:Arguments>
<sensors:Location>
<x:Arguments>
<x:Double>50.5014483</x:Double>
<x:Double>7.308297599999</x:Double>
</x:Arguments>
</sensors:Location>
<x:Double>0.01</x:Double>
<x:Double>0.01</x:Double>
</x:Arguments>
</MapSpan>
</x:Arguments>
Listing 8
Pin qualityBytesPin = new Pin
{
Location = new Location(50.5014483, 7.308297599999),
Label = "Quality Bytes GmbH",
Address = "Bad Breisig",
Type = PinType.Place
};
map.Pins.Add(qualityBytesPin);
Zusätzlich zu den beiden gezeigten Funktionen können Sie außerdem Formen wie Kreise, Linien oder Polygone auf die Karte zeichnen oder die Darstellung über die Eigenschaft MapType der Karte auf eine Straßen-, Satelliten-, oder Hybridansicht festlegen.
Performance und Stabilität
Zusätzlich zur Umsetzung neuer Funktionen setzte sich das .NET-MAUI-Team intensiv mit dem Thema Performance auseinander. Unter [3] beschreibt Microsoft, welche Verbesserungen unter der Haube vorgenommen wurden, um die Performance zu optimieren. Im Fokus standen bei der Optimierung die allgemeine Rendering-Geschwindigkeit sowie das Scroll-Verhalten in Listen.
Über die Performance hinaus arbeitete Microsoft auch an der Stabilität. Im Bereich Visual-Studio-Integration konnten 80 Prozent der Bugs von Visual Studio 2022 17.3 und .NET MAUI 6 unter Visual Studio 2022 17.4 und .NET MAUI 7 behoben werden. Außerdem wurde eine beträchtliche Anzahl an GitHub-Issues geschlossen.
Dass das Team hier jedoch noch einen weiten Weg vor sich hat, zeigen die knapp 2 000 offenen Issues. Der Fairness halber muss allerdings erwähnt werden, dass nicht alle Issues Fehler sind, sondern auch Feature-Requests sowie Duplikate.
Fazit
Die Anzahl der neuen .NET-MAUI-Funktionen mag auf den ersten Blick enttäuschen, schließlich lassen sie sich an zwei Händen abzählen. Zieht man jedoch in Betracht, dass das Team nur fünfeinhalb Monate Zeit hatte und darüber hinaus an Performance und Stabilität gearbeitet hat, sieht es gar nicht mehr so schlecht aus.
Die neuen Funktionen haben sicherlich einen geringen Wow-Faktor, schließen aber wichtige Funktionslücken und machen .NET MAUI somit immer mehr zu einer validen Alternative für die Entwicklung plattformübergreifender Desktopanwendungen.
Links & Literatur
[2] https://learn.microsoft.com/de-de/dotnet/maui/user-interface/visual-states?view=net-maui-7.0
[3] https://devblogs.microsoft.com/dotnet/dotnet-7-performance-improvements-in-dotnet-maui/