Avalonia UI is Open Source alternative to Microsoft's WPF for building cross platform application in C#.Net. We are going to use dotnet sdk 9 in this tutorial and will help you get started with it and to build a basic Login Page Example in dotnet sdk 9 and JetBrain's Rider IDE which comes with free non comerial Licence. JetBrain's IDEs are no doubt one of the best IDEs and their ecosystem is very strong. But the real problem is pricing and most of the times when someone don't want to purchase the non comercial free linces is a great option from JetBrain.
Install DotNet SDK
The first step after installing the JetBrains IDE is to install the dotnet SDK. I would prefer to download and install the SDK by myself rather relaying on the IDE installation. After installation is done you should add the SDK path to the system veriable so that you be able to access the sdk via command line. If everything is setup you should be able to run the following command
dotnet --version
and it should return the SDK 9 version information. If you have multiple SDK versions installed, you can use following command to check list all the available SDKs in your system.
dotnet --list-sdks
Install Avalonia UI template
Now that we have dotnet sdk installed and it is available in our cmd terminal, we are good to install the Avalonia UI Template. For this you can simply run the following command.
dotnet new install Avalonia.Templates
This will install and create a project with this template if you are in a right directory but we will skip the project creation process and we will jump into the JetBrains's Rider IDE for this purpose.
Create New Avalonia UI Project
Open the JetBrain's Rider IDE and in new project creation window click on New Solution Button.
Now that you have Here on the left panel you will see the Avalonia UI Project Creation option. I will select the Avalonia MVVM App. You can also choose other options but for this tutorial we are going with the following settings
Don't forget to choose C# as your language instead of F#, and select the SDK 9 for the dotnet SDK. Now you are good to start coding. One more thing I forgot to mention, You should go with ReactiveUI when asked instead of Community UI.
Understanding the Folder Structure
Now that you are inside of your project you should see the folder structure. You can simply see your main.axaml file under the views
directory. You should keep in mind that Avalonia UI uses it's own file structure which is axaml
as mentioned in their official documentation.
Here you can see the App.axaml.cs
file where the mainwindow is being initialized and started as a first window.
ViewModel for MainWindow
Now all you have to do is to move to your viewmodel
directory and navigate to the file named MainWindowViewModel.cs
. This file is your main View Model for your Main Window. You can change it to include the necessary variables and some initializations. Here is what my MainWindowViewModel.cs
file looks like.
using ReactiveUI;
using System;
using System.Reactive;
using System.Diagnostics;
using Avalonia.Media;
namespace AvaloniaApplication2.ViewModels;
public class MainWindowViewModel : ViewModelBase
{
private string _username;
public string Username
{
get => _username;
set => this.RaiseAndSetIfChanged(ref _username, value);
}
private string _password;
public string Password
{
get => _password;
set => this.RaiseAndSetIfChanged(ref _password, value);
}
private string _statusMessage;
public string StatusMessage
{
get => _statusMessage;
set => this.RaiseAndSetIfChanged(ref _statusMessage, value);
}
public ReactiveCommand<Unit, Unit> LoginCommand { get; }
public MainWindowViewModel()
{
// Define the dummy login credentials
const string DUMMY_USERNAME = "admin";
const string DUMMY_PASSWORD = "password123";
// Create the ReactiveCommand for the login action
LoginCommand = ReactiveCommand.Create(() =>
{
// Clear previous status message
StatusMessage = string.Empty;
// Simple validation check
if (string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password))
{
StatusMessage = "Please enter both username and password.";
Debug.WriteLine("Login failed: Empty credentials.");
return;
}
// Dummy login logic
if (Username.Equals(DUMMY_USERNAME, StringComparison.OrdinalIgnoreCase) && Password == DUMMY_PASSWORD)
{
StatusMessage = "Login successful!";
Debug.WriteLine($"Login successful for user: {Username}");
// You can add navigation or other logic here upon successful login
}
else
{
StatusMessage = "Invalid username or password.";
Debug.WriteLine("Login failed: Invalid credentials.");
}
});
}
}
Implements MVVM with ReactiveUI using ViewModelBase that inherits from ReactiveObject
Defines 3 bindable properties with RaiseAndSetIfChanged:
Username (string) – stores user input
Password (string) – stores password input
StatusMessage (string) – shows login status feedback
Exposes a ReactiveCommand
named LoginCommand:
Created using ReactiveCommand.Create(...)
Executes login logic when invoked (bound to Login button)
Login validation logic inside command:
Checks for empty Username or Password
Compares input against hardcoded values:
Username: admin
Password: password123
Updates StatusMessage
based on result
Writes debug info using Debug.WriteLine(...)
Usage in XAML
Now it's time to use our ViewModel into our Main.axaml
file for building our frontend UI. Here is main variables which we are going to use in our frontend.
- Binds
TextBox.Text
to Username and Password - Binds
Button.Command
toLoginCommand
- Displays
StatusMessage
to inform user of success or failure
No navigation yet, but placeholder comment added for future logic
Here is my complete Login Page UI code in XML
<reactiveUi:ReactiveWindow x:TypeArguments="viewModels:MainWindowViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveUi="clr-namespace:Avalonia.ReactiveUI;assembly=Avalonia.ReactiveUI"
xmlns:viewModels="clr-namespace:AvaloniaApplication2.ViewModels"
mc:Ignorable="d"
x:Class="AvaloniaApplication2.Views.MainWindow"
x:DataType="viewModels:MainWindowViewModel"
Title="Login"
Width="420"
Height="500"
CanResize="False"
WindowStartupLocation="CenterScreen"
Background="#121826"
RequestedThemeVariant="Dark"
>
<Border CornerRadius="20"
Padding="30"
Width="360"
Background="#1F2937CC"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BoxShadow="0 0 40 0 #00000080">
<StackPanel Spacing="20">
<TextBlock Text="Welcome Back"
FontSize="26"
Foreground="White"
FontWeight="SemiBold"
HorizontalAlignment="Center" />
<TextBlock Text="Login to your account"
FontSize="14"
Foreground="#9CA3AF"
HorizontalAlignment="Center"
Margin="0,0,0,10"/>
<StackPanel>
<TextBlock Text="Username" Foreground="#9CA3AF" FontSize="12" Margin="0,0,0,5"/>
<TextBox Text="{Binding Username, Mode=TwoWay}"
Background="#2C3444"
Foreground="White"
CornerRadius="8"
BorderBrush="Transparent"
Padding="8"/>
</StackPanel>
<StackPanel>
<TextBlock Text="Password" Foreground="#9CA3AF" FontSize="12" Margin="0,10,0,5"/>
<TextBox Text="{Binding Password, Mode=TwoWay}"
PasswordChar="●"
Background="#2C3444"
Foreground="White"
CornerRadius="8"
BorderBrush="Transparent"
Padding="8"/>
</StackPanel>
<TextBlock Text="{Binding StatusMessage}"
Foreground="White"
HorizontalAlignment="Center"
Margin="0, -10, 0, 10" />
<Button Content="Login"
Command="{Binding LoginCommand}"
Background="#4F46E5"
Foreground="White"
FontWeight="SemiBold"
CornerRadius="8"
Padding="12"
Width="100"
HorizontalAlignment="Center">
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="Background" Value="#6366F1"/>
</Style>
</Button.Styles>
</Button>
<TextBlock HorizontalAlignment="Center" Margin="0,10,0,0">
<Run Text="Don't have an account? " Foreground="#9CA3AF"/>
<Run Text="Register" Foreground="#4F46E5" TextDecorations="Underline" />
</TextBlock>
</StackPanel>
</Border>
</reactiveUi:ReactiveWindow>
Login Backend
Here is simple Main.axaml.cs
file for handling the frontend UI control for simulating a dummy login process based on our MainViewModel
using System;
using Avalonia.Input;
using Avalonia.ReactiveUI;
using AvaloniaApplication2.ViewModels;
using ReactiveUI;
namespace AvaloniaApplication2.Views;
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
{
public MainWindow()
{
InitializeComponent();
this.WhenActivated(disposables => { });
}
private void OnRegisterClicked(object? sender, PointerPressedEventArgs e)
{
Console.WriteLine("Register clicked"); // replace with navigation
}
}
Mostly everything is handled in the viewmodel and nothing much required here. Only the Navigation to Registration page is left which we would do in some upcoming tutorials. This is how our final UI looks like
Top comments (0)