Avalonia UI rendert alle Elemente selbst. Das hat insbesondere den Vorteil, dass die eigene Applikation und damit das eigene Corporate Branding auf jeder Plattform gleich aussieht. Es gibt noch einige andere Vorteile dieses Ansatzes. In diesem Artikel möchte ich aber darauf eingehen, wie man mit Stellen umgehen kann, an denen es Unterschiede zwischen den verschiedenen Betriebssystemen gibt. Es gibt i. d. R. nur Wenige, eines davon ist die Menüleiste. Für Windows und Linux wird sie typischerweise im Hauptfenster ganz oben angezeigt. Unter macOS ist das anders – dort stellt das Betriebssystem selbst eine Menüleiste am oberen Bildschirmrand bereit. Somit ist es unter macOS gar nicht notwendig, das Hauptmenü selbst zu zeichnen. Hier im Artikel beschreibe ich, wie ich das Thema bei GPXviewer 2 mittels einer MarkupExtension gelöst habe.
OpenSource
GPXviewer 2 – Mit Avalonia UI auf mehrere Plattformen
Es gibt eine lange Liste möglicher Frontend-Fameworks, zwischen denen .NET-Entwicklungsteams wählen können. Ein sehr spannender Kandidat ist Avalonia UI [1]. Ursprünglich als Cross-Plattform Alternative zu WPF gestartet, bietet es heute neben den bekannten Desktops von Windows, macOS und Linux auch Unterstützung für mobile Plattformen (Android, iOS), für den Browser (via WebAssembly) und als Alleinstellungsmerkmal auch für Embedded Linux. Für mein OpenSource Tool GPXviewer war für mich neben Windows insbesondere macOS relevant. Ganz einfach aus dem Grund, weil ich mittlerweile mehr Zeit auf meinem MacBook verbringe, als auf meinem Windows Laptop [2]. Daher habe ich mich entschieden, GPXviewer von WPF nach Avalonia UI zu portieren – daraus entstand dann das Projekt GPXviewer 2 [3]. In diesem und nachfolgenden Artikeln möchte ich einige Erfahrungen teilen, die ich damit sammeln durfte.
Texture sampling in SeeingSharp 2
In den letzten Tagen hatte ich wieder Zeit, an der 3D-Engine SeeingSharp 2 weiterzuarbeiten. Diesmal ging es darum, allgemein das Arbeiten mit Texturen zu verbessern. Genauer geht es um das Texture sampling. Texture sampling bezeichnet den Prozess, wie Farbinformationen aus Texturen gelesen werden. Direct3D bietet hierbei eine lange Liste von Einstellungen. So kann etwa beeinflusst werden, nach welcher Logik Texturen auf einer Fläche gekachelt werden sollen. Auch die Art, wie die Farben zweier benachbarter Pixel auf einer Textur vermischt werden, kann konfiguriert werden. Letzteres ist dann relevant, wenn eine Textur am Monitor mehr oder weniger Pixel beansprucht, als in der Textur selbst zur Verfügung stehen. Insgesamt hat SeeingSharp 2 bis jetzt nur ein Standard-Set an Einstellungen mit Anisotroper Filterung [1] unterstützt. Passt dieses Standard-Set nicht, so hatte man bis dato keine Möglichkeit, das Texture sampling zu beeinflussen.
RingBuffer in C#
In .Net existiert eine Vielzahl von Klassen zur Auflistung von Objekten oder Strukturen. Ein einfaches und häufig verwendetes Beispiel ist die Array. Die List<T> ist ebenso stark verbreitet. Daneben gibt es eine Vielzahl von Klassen, welche diverse Spezialfälle abbilden. So etwa für parallelen Zugriff mit den Klassen aus dem Namespace System.Collections.Concurrent. Einen Spezialfall hätte ich allerdings schon häufiger vermisst: Den RingBuffer (oder auch Circular Buffer [1]). Ein RingBuffer dient dazu, eine fortlaufende Liste mit einer maximalen Länge abzubilden. Wenn über die maximale Länge hinaus geschrieben wird, so werden einfach die ältesten Elemente überschrieben. Gehen wir von der max. Länge von 100 aus. Wenn das 101. Element hinzugefügt wird, fliegt das erste Element raus. Dieses neue Element ist dann aber nicht der neue Start der Liste. Der Start der Liste ist immer das älteste Element. Wo benötigt man so ein Verhalten? Ein gutes Beispiel dazu habe ich in der 3D-Engine SeeingSharp. Zur Berechnung der durchschnittlichen FPS (Frames per Second bzw. Bilder pro Sekunde) wird der aktuelle FPS Wert je Sekunde berechnet und an einer Liste angehängt. Nachdem diese Liste eine bestimmte Länge erreicht hat, wird mit jedem neuen Wert der älteste Eintrag entfernt. Die durchschnittlichen FPS ergeben sich anschließend mittels des Durchschnittswerts aller Elemente in der Liste. Mit einer List<T> könnte man dieses Verhalten per Add und RemoveAt(0) relativ einfach abbilden, bei einem RingBuffer dagegen steckt dieses Verhalten bereits im Add. Dazu gibt es beim RingBuffer noch weitere Vorteile aus Sicht der Performance, doch dazu im Verlauf dieses Artikels mehr.
Testautomatisierung beim Parsing von Dokumenten
Als Entwickler steht man regelmäßig vor der Aufgabe, Dokumente mit einem bestimmten Format zu lesen und weiterzuverarbeiten. Ebenso kommt es immer wieder vor, dass es für das gewünschte Dokumentenformat noch keinen passenden Parser gibt. Daraus entsteht die Aufgabe, selbst einen kleinen Parser zu entwickeln. Gesagt, getan. Bei Xml- oder Json-Dokumenten etwa ist das i. d. R. auch relativ schnell erledigt. Nicht selten gibt es aber dennoch verschiedene Besonderheiten, die es zu beachten gilt. Etwa bestimmte Elemente in der Datei, die nicht immer enthalten sind. Nehmen wir als Beispiel ein Dokument, welches einen Kassenbon abbildet. Standardmäßig stehen alle Artikel drin, welche der Kunde gekauft hat. Es gibt allerdings weitere Fälle, wie Artikelrückgaben (Pfand), Stornierung einzelner Zeilen durch das Kassenpersonal, rabattierte Artikel etc. Schnell ergibt sich eine längere Liste von Fällen, die das Parsing und die anschließende Weiterverarbeitung beeinflussen. Aus diesem Grund kann es eine gute Idee sein, möglichst viele Teile davon per Testautomatisierung abzusichern.
Worklog SeeingSharp 2: Getting Started für WinUI 3
Bisher gab es in SeeingSharp 2 zwei GettingStarted Projekte: Einmal für Windows.Forms und einmal für WPF. WinUI 3 hat nun zusammen mit dem WindowsAppSdk 1.1 einen recht guten Entwicklungsstand erreicht. Somit wird es Zeit, ebenfalls für WinUI 3 ein GettingStarted Projekt bereitzustellen. Das Projekt zeigt, wie SeeingSharp 2 in ein WinUI 3 Projekt eingebunden und verwendet werden kann. Im Ergebnis wird ein dreidimensionaler Würfel erzeugt, welcher sich in einer endlosen Animation um seine eigene Y-Achse dreht. Unter [1] ist das Coding des Projekts verfügbar.
Überwachen von Coding Conventions per Roslyn Analyzer
Das Projekt RK GPXviewer ist ein Modulith in Form einer Desktop Applikation. Das heißt, dass das Programm in mehrere lose gekoppelte Module aufgeteilt ist. Jedes Modul hat hierbei eine öffentliche Schnittstelle und eine nur innerhalb des Moduls sichtbare Logik. Diese Trennung zwischen öffentlicher Schnittstelle und privaten Logikklassen ist hierbei eine strenge Regel. Sie soll sicherstellen, dass das Geflecht an Modulen auch in Zukunft sauber wartbar und erweiterbar bleibt. Nur wie lässt sich die Einhaltung einer solchen Regel am besten sicherstellen? Alle Module befinden sich in der gleichen Projektmappe, eine Regelverletzung wirkt geradezu einladend. Ist eine notwendige Methode oder Eigenschaft gerade nicht in der Schnittstelle enthalten, ist es relativ einfach, ohne Umwege direkt auf die Logik-Klassen zuzugreifen. Damit das nicht passiert, lassen sich für solche Regeln Roslyn Analyzer schreiben. Diese prüfen den Code während des Compile-Vorgangs und geben nach Wahl direkt Fehler oder Warnungen aus. In diesem Artikel möchte ich auf den Roslyn Analyzer eingehen, welchen ich für RK GPXviewer zur Einhaltung obiger Regel umgesetzt habe.
Worklog SeeingSharp 2: Unterstützung für WinUI 3 Desktop
Im März 2021 wurde die erste stabile Version von WinUI 3 zusammen mit Project Reunion 0.5 veröffentlicht. Vorher gab es bereits mehrere Preview-Releases von WinUI 3, welche einen ersten Blick auf das Framework ermöglicht haben. Nun aber durch das erste stabile Release ist ein guter Zeitpunkt zur Umsetzung der WinUI 3 Desktop Unterstützung in SeeingSharp 2 erreicht. Mit WinUI allgemein beschäftige ich mich schon länger, so basiert die Beispielapplikation von SeeingSharp 2 für die Universal Windows Plattform (UWP) bereits auf Komponenten von WinUI 2 [1]. WinUI 2 ist allerdings noch auf die UWP beschränkt, mit WinUI 3 entfällt diese Abhängigkeit. Mit der neuen Version des Frameworks ist es nun möglich, WinUI Komponenten direkt in einer Desktop-Applikation zu verwenden.