Creating minimal Avalonia next generation (NXUI, next-gen UI) application using C# 10 and .NET 8
s6WC0Uol0x.mp4
<PackageReference Include="NXUI" Version="11.3.0" />
Additionally, depending on the application type:
For Desktop extensions:
<PackageReference Include="NXUI.Desktop" Version="11.3.0" />
or using plain Avalonia:
<PackageReference Include="Avalonia.Desktop" Version="11.3.0" />
<PackageReference Include="Avalonia.Browser" Version="11.3.0" />
dotnet workload install wasm-tools
Window Build() => Window().Content(Label().Content("NXUI"));
AppBuilder.Configure<Application>()
.UsePlatformDetect()
.UseFluentTheme()
.StartWithClassicDesktopLifetime(Build, args);
var count = 0;
Window Build()
=> Window(out var window)
.Title("NXUI").Width(400).Height(300)
.Content(
StackPanel()
.Children(
Button(out var button)
.Content("Welcome to Avalonia, please click me!"),
TextBox(out var tb1)
.Text("NXUI"),
TextBox()
.Text(window.BindTitle()),
Label()
.Content(button.ObserveOnClick().Select(_ => ++count).Select(x => $"You clicked {x} times."))))
.Title(tb1.ObserveText().Select(x => x?.ToUpper()));
AppBuilder.Configure<Application>()
.UsePlatformDetect()
.UseFluentTheme()
.WithApplicationName("NXUI")
.StartWithClassicDesktopLifetime(Build, args);
Minimalistic Desktop app:
Run(
() => Window().Content(Label().Content("NXUI")),
"NXUI",
args,
ThemeVariant.Dark);
C#
cd src/Generator
dotnet run -- ../NXUI/Generated
F#
cd src/Generator
dotnet run -- ../NXUI.FSharp/Generated -fsharp
Using .NET 10 you can run GUI apps using scripts: https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#using-shebang-lines-for-shell-scripts
Note: You might need to adjust shebang line to #!/usr/bin/dotnet run
App.cs
#!/usr/local/share/dotnet/dotnet run
#:package NXUI.Desktop@11.3.0
NXUI.Desktop.NXUI.Run(
() => Window().Content(Label().Content("NXUI")),
"NXUI",
args,
ThemeVariant.Dark,
ShutdownMode.OnLastWindowClose);
chmod +x App.cs
./App.cs
More complex app:
#!/usr/local/share/dotnet/dotnet run
#:package NXUI.Desktop@11.3.0
var count = 0;
Window Build()
=> Window(out var window)
.Title("NXUI").Width(400).Height(300)
.Content(
StackPanel()
.Children(
Button(out var button)
.Content("Welcome to Avalonia, please click me!"),
TextBox(out var tb1)
.Text("NXUI"),
TextBox()
.Text(window.BindTitle()),
Label()
.Content(button.ObserveOnClick().Select(_ => ++count).Select(x => $"You clicked {x} times."))))
.Title(tb1.ObserveText().Select(x => x?.ToUpper()));
AppBuilder.Configure<Application>()
.UsePlatformDetect()
.UseFluentTheme()
.WithApplicationName("NXUI")
.StartWithClassicDesktopLifetime(Build, args);
From F# 9.0 and above the compiler resolves extension methods instead of instrinsic properties so, there's no need for a separate F# package or any additional changes to your project files.
Extension methods provided by the main package NXUI
open Avalonia
open Avalonia.Controls
open NXUI.Extensions
open NXUI.Desktop
open type NXUI.Builders
let Build () =
let mutable count = 0
let mutable window = Unchecked.defaultof<Window>
let mutable button = Unchecked.defaultof<Button>
let mutable tb1 = Unchecked.defaultof<TextBox>
Window(window)
.Title("NXUI")
.Width(400)
.Height(300)
.Content(
StackPanel()
.Children(
Button(button).Content("Welcome to Avalonia, please click me!"),
TextBox(tb1).Text("NXUI"),
TextBox().Text(window.BindTitle()),
Label()
.Content(
button.ObserveOnClick()
|> Observable.map (fun _ ->
count <- count + 1
count)
|> Observable.map (fun x -> $"You clicked {x} times.")
|> _.ToBinding()
)
)
)
.Title(tb1.ObserveText())
[<EntryPoint>]
let Main argv = NXUI.Run(Build, "NXUI", argv)
The compiler feature is available in the .NET9 SDK and above so even if you target a lower dotnet version you don't need to change your project files.
However, if you must to use the .NET8 SDK you only need to set the language version to preview In your *.fsproj project and you'll get the same benefits.
<PropertyGroup> <TargetFramework>net8.0</TargetFramework> <LangVersion>preview</LangVersion> </PropertyGroup>
NXUI ships with a rich set of extension methods and builder helpers so that all UI composition can be expressed in C#. The code generator produces most of these members for every Avalonia control and property.
NXUI.Builders
exposes factory methods for every control type. Each method
creates the control instance and overloads let you capture it via out var
for
later use.
For each Avalonia property the following methods are generated:
<Name>(value)
– set the property value.<Name>(IBinding, mode, priority)
– bind with an Avalonia binding.<Name>(IObservable<T>, mode, priority)
– bind from an observable.Bind<Name>(mode, priority)
– create a binding descriptor.Observe<Name>()
– observable of property values.On<Name>(handler)
– pass the observable to a handler.ObserveBinding<Name>()
– observe binding values including errors.OnBinding<Name>(handler)
– receive the binding observable.Observe<Name>Changed()
– observe full change events.On<Name>Changed(handler)
– handler for change observable.
Enum properties get convenience methods for each enum value, e.g.
HorizontalAlignmentCenter()
.
For routed and CLR events:
ObserveOn<EventName>(routes)
– returns anIObservable
sequence.On<EventName>(handler, routes)
– handler receiving the observable.On<EventName>Handler(action, routes)
– attach a simple callback.
Set<ClassName><PropertyName>
methods on Style
and KeyFrame
let you define
style values using constants, bindings or observables.
NXUI.Extensions.AvaloniaObjectExtensions
provides BindOneWay
and
BindTwoWay
to link properties or observables without verbose binding code.
NXUI.Extensions.ReactiveObservableExtensions
adds utilities for reactive
workflows:
ObserveOnUiThread
/SubscribeOnUiThread
TakeUntilDetachedFromVisualTree
/SubscribeUntilDetached
DisposeWith
DataTemplate<T>
WhenAnyValue
(single or multiple expressions)
Together these extensions enable complex, reactive UIs built entirely in code while managing resources with minimal overhead.