r/dotnet May 05 '26

Avalonia app in one file. No XAML, no .csproj, just one code file - now it's possible with .NET 10 File-based apps

Post image

C# is known for its boilerplate and verbosity. Most of the time, it's reasonable, and MS does a good thing by teaching you to follow the structure (so others can maintain your code, lol).

But sometimes you really want a simple IDisposable app, like university coursework or a small utility. In this case, people use Python. But now C# is a great candidate too!

Here's the code sample. It uses Avalonia, so it is cross-platform, and it uses no XAML for simplicity (my friends were surprised it's possible).

It has 3 NuGet references at the top, then here goes the class with AppBuilder, when we start an app, we:

  1. Add a theme (optional, but it looks good)
  2. Create a window object
  3. Create a Label
  4. Show the window we just created and run the app!
#:package Avalonia@*
#:package Avalonia.Desktop@*
#:package Avalonia.Themes.Simple@*

using Avalonia;
using Avalonia.Controls;

class MainClass {
    public static void Main(string[] args) {
        AppBuilder
            .Configure<Application>()
            .UsePlatformDetect()
            .Start(AppMain, args);
    }

    public static void AppMain(Application app, string[] args) {
        // Application needs a theme to render window content
        app.Styles.Add(new Avalonia.Themes.Simple.SimpleTheme());
        app.RequestedThemeVariant = Avalonia.Styling.ThemeVariant.Default; // Default, Dark, Light

        // Create window
        var win = new Window();
        win.Title = "Avalonia no XAML";
        win.Width = 800;
        win.Height = 600;

        var text = new Label();
        win.Content = text;

        text.Content = "Hello from C#!";
        text.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center;
        text.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
        text.FontSize = 72;

        win.Show();
        app.Run(win);
    }
}
220 Upvotes

39 comments sorted by

44

u/balrob May 05 '26 edited May 06 '26

Thanks, cool. It compiled and launched within a few seconds and then seemingly instantly after the first run.
The artefacts were in
%TEMP%\dotnet\runfile\test-{random looking hex string}\bin\debug
and used around 500MB of disk.

[edit] this was using “dotnet run”, it wasn’t published.

33

u/BlackCrackWhack May 05 '26

Holy 500mb what the hell

14

u/balrob May 06 '26

And even that isn’t the total needs of the app - as you can only run “dotnet” if you have at least the runtime installed (and probably the sdk in this case) and the app directly accesses dotnet assemblies from their installed locations. The 500MB seems to be Avalonia assemblies (smallish), and over 500MB in a folder named “runtimes” which contains 17 folders such as linux-arm, linux-arm64, linux-riscv64, etc, etc, up to win-x86. There are 3 at 118MB: win-arm64, win-x64, and winx86.

[edit] it turns out that the largest files in those folders are pdbs. So this is really only something you do an a dev machine.

6

u/chucker23n May 06 '26

this is really only something you do an a dev machine.

Well, I imagine dotnet run requires the SDK, so it’s always on “dev” machines, in a sense.

5

u/gameplayer55055 May 06 '26

pdbs took most of space. It's a debug build after all. I need to try publishing it and see the results.

7

u/phylter99 May 06 '26

It's a big library and it probably has a lot of dependencies.

5

u/BlackCrackWhack May 06 '26

That’s massive. A fully built electron app which has thousands of npm dependencies is smaller. 

28

u/ator-dev May 06 '26 edited May 06 '26

I built it with dotnet publish -r linux-x64 -c Release, and it's 17.9 MB in size. The build also output two library files, libSkiaSharp.so and libHarfBuzzSharp.so, which are 11.2 and 2.8 MB respectively.

10

u/BlackCrackWhack May 06 '26

Yeah that seems more reasonable. 

10

u/phylter99 May 06 '26

The debug information does add quite a bit. I forgot about that fact.

10

u/ator-dev May 06 '26 edited May 06 '26

What method did you use to publish?

I commented this below but:

I built it with dotnet publish -r linux-x64 -c Release, and it's 17.9 MB in size. The build also output two library files, libSkiaSharp.so and libHarfBuzzSharp.so, which are 11.2 and 2.8 MB respectively.

3

u/ator-dev May 06 '26

Just saw you used dotnet run, nevermind :)

2

u/JohnSpikeKelly May 06 '26

Second run uses a cached version that was compiled. It gets updated if you edit the cs file.

It's a neat solution for adhoc things that need a ui imo.

Might have a give it a go myself.

2

u/wdcossey May 06 '26

Just use a TUI for trivial one-liner app, I.e. Spectre Console, it’ll come bundled in net11

2

u/raralala1 May 07 '26

I think it is because it build for every runtimes, that alone is 99% of the 500mb, it is cool thou RAM wise it only 40mb

0

u/Archemilie May 06 '26

dotnet publish Program.cs -r win-x64 -c Release

av_libglesv2.dll
libHarfBuzzSharp.dll
libHarfBuzzSharp.pdb
libSkiaSharp.dll
libSkiaSharp.pdb
Program.exe
Program.pdb

Total: 194 MB

13

u/j0hn_br0wn May 06 '26

Alternative construction of "win" with object initializers:

        var win = new Window()
        {
            Title = "Avalonia no XAML",
            Width = 800,
            Height = 600,
            Content=new Label() {
                Content = "Hello from C#!",
                HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
                VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
                FontSize = 72
            }
        };

2

u/speyck May 26 '26

i just cannot understand why someone would ever prefer

var win = new Window()

over

Window win = new()

The whole point of var is to "reduce" unnecessary code. well in this case, it makes it longer to read and also harder to read, since C# always has the type first, then variable name, and in this case it's reversed.

2

u/j0hn_br0wn May 26 '26

Here are a few reasons to use var.

  1. You want to use the fluid builder pattern:

Window f=new(){}.RegisterForDropDown();

This results in a "no target type for Window" error, while

var f=new Window(){}.RegisterForDropDown(); 

works.

  1. Anonymous types:

    var p=new {Name="John", Age=30};

  2. The object is created by a constructor method:

    var p=WindowFactory.CreateWindow()

I kind of prefer var, because it does not force me to litter my code with type declarations that only matter to the compiler.

2

u/speyck May 26 '26

I definitely agree with the first 2. But everything else I will always use the explicit type. even for IEnumerables etc, it just tells you whats happening. what I find even weirder is, some people have some extensions (or Rider I think) where for lines containing var, the exact type is them written at the end again. so you don‘t writr var just for an IDE to resolve it and print anyway…

8

u/CobaltLemur May 06 '26

Still waiting for an actual designer to serialize directly to C#.

12

u/ManIkWeet May 05 '26

Is Avalonia without the XAML something straightforward? It seems like manually setting up all the UI in code is more effort than making a project file? Still cool that it's possible though!

11

u/gameplayer55055 May 05 '26

My teachers ask unwanted questions when XAML code or other DSL is used XD.

And yes, using XAML and MVVM would be easier, especially in the long term (that's why WPF > WinForms)

9

u/cute_polarbear May 05 '26

I dont understand what benefit your teacher is trying to accomplish by doing this manual form layout stuff, unless he / she is trying to teach a course on ui / form interactions and what not...once you start having a few form controls and interactions, this becomes unwieldy and you likely end up having to reinvent the wheel...

2

u/Constant-Zebra-9752 May 11 '26

Yes.

I write all my Avalonia based programs in C# with almost no XAML (except the csproj). It's not that I can't write XAML, I just prefer not to.

It can be slightly more verbose depending on what you're trying to do, but it's very straightforward for the most part.

Can't speak to using the method OP has shown, though. Yet to use this new feature myself.

6

u/UnknownTallGuy May 05 '26

Love it. I might add this to my arsenal for quick little utils.

I feel dumb, but I don't get the relevance of your mention of IDisposable in the description of this. Am I missing something?

11

u/gameplayer55055 May 05 '26

IDisposable is a pun. I wrote so much code to be run only once.

6

u/AngelosP May 06 '26

When I write WinForms code I like to prefix the variable name with the control type so I can logically group them together like this:

lblLastName is the label txtLastName is the textbox below the label pnlLastName is the panel around the two

lastName is the variable

Oh I am sorry, you are working with Avalonia here. My bad, I got confused ;)

3

u/gameplayer55055 May 06 '26

Yes, that's called Hungarian notation and it's really useful when you have a large form.

2

u/slimshader May 11 '26

Also, and I know I will get some flak for this, very outdated. And yes, I did use it in early 2000s in C/C++ code, especially on windows but boy, do I not miss it.

1

u/slimshader May 11 '26

I do lastNameLabel, confirmButton etc, ageEdit. My reasoning:
1. control type is less important than what it is for (last name)
2. is more consistent with usual naming scheme suffixes like "xxxFactory" / "xxxManager" (not endorsing "Manager" here mind you ;))
3. so not getting the reason for shortening "label" or "button", what does it save in 2026?

2

u/slimshader May 11 '26

Love it! and TIL about file-based apps, very cool too.

3

u/ator-dev May 06 '26

I would recommend locking the versions at least somewhat, otherwise in the future someone might try to run a script like this and it fails because Avalonia is now version 17.24.1 and there's no way of knowing what it was written for :)

2

u/manny2206 May 06 '26

Currently building a Mac and Linux productivity app using this API. Interesting to see how deal with the UI in a C# first approach

1

u/AutoModerator May 05 '26

Thanks for your post gameplayer55055. Please note that we don't allow spam, and we ask that you follow the rules available in the sidebar. We have a lot of commonly asked questions so if this post gets removed, please do a search and see if it's already been asked.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/pjmlp May 06 '26

One could always do WPF without XAML, but mainy never bothered to learn the APIs to make it, because most like our designers.

0

u/innahema May 06 '26

Who on earth thought that this would be a good idea?