After working as a full-stack developer using primarily dotnet core services, I knew I wanted to keep developing my knowledge of dotnet and Microsoft’s push into the cross platform world. Although Microsoft themselves haven’t yet realised their ideal of dotnet becoming as widespread in the cross platform desktop environment, as their server stack is (namely, they haven’t produced a cross compatible GUI framework yet), the folks behind AvaloniaUI stepped up to the challenge and produced an implementation of a dotnet GUI framework which closely mimicks the functionality of WPF.
Having already created a desktop application for Windows using dotnet core at my office job, I eagerly jumped into development of my remake of Moodkiller/xdelta3-gui-2.0, with the focus being to rewrite the functionality in that project from the ground up using Avalonia and dotnet core, allowing users on all platforms to take advantage of this awesome tool. Coincidentally, an issue ticket was raised on Moodkiller/xdelta3-gui-2.0, asking for clarification of whether users on non Windows platforms could use the tool as well. After noticing this ticket, I was sure that this was the project for me.
After only two half days, I was ready to publish version 1.0.0 of my first public project, which I had decided to name xDelta3 Cross GUI. At this stage, I hadn’t put much work into testing and validating my application on the target platforms, MacOS being especially challenging, since I didn’t have access to a device running the operating system at the time. As I was releasing bugfixes over the next few days, Lee Binder, the individual who had posted the issue ticket on Moodkiller’s repository, was quick to jump to help, sending over a design idea for an app icon, which I took inspiration from in creating the final icon, as well as submitting a few issues, which I hurried to fix.
Most of the bugs I faced were simple oversights, such as not taking potential spaces in file paths into account, and the like, but there were a few more interesting developments as well. What I was really excited about was creating a simple way to publish my project from any platform, to any platform, with the most professional outcome possible. To this end, I developed my Publish scripts, which I plan on refining and keeping in a seperate repository for future projects. Using these scripts, I could publish and package my application for Windows, Linux, and MacOS, without hassle.
Starting from version 1.0.4 I decided to build to self-contained dotnet core applications for each platform (this is the default behaviour when building a project with a specified runtime identifier, in the case of exectuable projects). This, combined with the PublishTrimmed=true
build parameter, which removes libraries which go unused in the project, eliminated the need for additional installation steps, with a minimal (~15-20MB) filesize cost. While even this filesize increase is not insignificant, for my application, I don’t think it’s an unreasonable compromise, especially as building the project from source isn’t too many steps removed from installing dotnet core manually, if space-savings are a demand for the user.
By using self-contained builds, I can confidently package MacOS builds of my application into a native, .app
file using my Publish script, which MacOS users can simply run from any location on their machine. Similarly, the AppImage
format for Linux came to my attention, as the new primary operating system of my laptop, PopOS! helped me learn more about Linux software distribution. While AppImage provided a way to release my application on Linux with the tidiness of a MacOS App (single file release with integrated metadata such as a desktop icon), it also meant that the application dependecies are packaged into the final app file, removing exposure of the Assets/exec
folder, where users could place their desired replacement executables for xDelta3. Unlike App files, which can be freely extracted and repackaged, allowing users to change the files within before resealing the App, AppImage hashes its contents, making it unviable to extract, change files, and simply repackage.
To circumvent this issue, I decided to release non-packaged Linux builds as well, so that anyone wanting this use-case could continue to replace these executable files as they wish, but the long-term solution will be to allow users to specify their executable locations within the application itself, so I do not have to produce multiple builds for a single platform. Producing AppImage and App files for Linux and MacOS, I made my life on Windows easier by only building for x86, rather than x64 as well. This cut my Windows release size almost in half, while maintaining compatibility with all current (non-ARM) Windows devices. Although it might be time for most devices to move to 64bit operating systems (the Ubuntu developers clearly are of this opinion), I don’t want to rule out any potential users from being able to run as simple an application as this, which clearly doesn’t demand the benefits of 64bit OS.
As dotnet 5 rolls around, Microsoft have clearly fully committed to the cross platform future of their ecosystem and I’m curious to see if they bring a competing UI framework to the table. While Avalonia served me well for this project, it certainly has its drawbacks and complexities when compared to WPF. For one, while WPF automatically binds fields between View and Model, Avalonia functions using properties rather than fields. While this doesn’t seem like too big a difference, it leads to quite a bit of code duplication, as all fields effectively need to be stored as variables with exposed properties to get and set them with, which also have to trigger an OnChange
event to let Avalonia know to update the value when setting the property, unlike WPF which automatically updates field values in the View when they are changed in the Model.
Besides this, there are a few WPF controls which don’t have adequate alternatives in Avalonia at the moment, though the open-source community is hard at work creating plug and play solutions like Aura.UI to fill this void.
All in all, I’m really glad I decided to undertake this project, and I’ve learned a lot from it, from various multiplatform publishing techniques, to a new GUI framework, to running a public repository with releases, patch notes, and an attempt at a professional presentation. I have to say I’ve enjoyed every minute of it (including this writeup!).
Here are some comparison screenshots between Moodkiller/xdelta3-gui-2.0 and my application at version 1.0.4 (current at the time of writing) (Moodkillers’ first):
And during the creation of patches: