CheapAvaloniaBlazor 1.0.84
dotnet add package CheapAvaloniaBlazor --version 1.0.84
NuGet\Install-Package CheapAvaloniaBlazor -Version 1.0.84
<PackageReference Include="CheapAvaloniaBlazor" Version="1.0.84" />
<PackageVersion Include="CheapAvaloniaBlazor" Version="1.0.84" />
<PackageReference Include="CheapAvaloniaBlazor" />
paket add CheapAvaloniaBlazor --version 1.0.84
#r "nuget: CheapAvaloniaBlazor, 1.0.84"
#:package CheapAvaloniaBlazor@1.0.84
#addin nuget:?package=CheapAvaloniaBlazor&version=1.0.84
#tool nuget:?package=CheapAvaloniaBlazor&version=1.0.84
๐ CheapAvaloniaBlazor
Build cross-platform desktop applications with the web development stack you already know.
Combine Blazor Server + Your Choice of UI Framework (currently MudBlazor, more options coming) + Avalonia + Photino to create native desktop apps with full file system access across Windows, Linux, and macOS - using familiar Razor pages and C# components.
๐ง PRE-ALPHA HOBBY PROJECT ๐ง
This is an experimental project developed as a personal hobby. Expect breaking changes, incomplete features, and limited support. Use at your own risk in non-production environments.
โจ Why CheapAvaloniaBlazor?
The Problem: Building cross-platform desktop apps traditionally requires learning different UI frameworks for each platform or dealing with complex native interop.
The Solution: Use your existing web development skills (HTML, CSS, Blazor, C#) to build real desktop applications with native capabilities.
๐ฏ Perfect For:
- Web developers transitioning to desktop development
- Rapid prototyping of desktop applications
- Line-of-business apps requiring native file system access
- Cross-platform tools that need to run on Windows, Linux, and macOS
- Blazor developers wanting to break free from browser limitations
- Teams wanting UI framework flexibility (MudBlazor now, more options coming)
๐ฆ Installation Options
Option A: Command Line (VS Code/Terminal Users)
# Create a new console project (CheapAvaloniaBlazor handles the desktop setup)
dotnet new console -n MyDesktopApp
cd MyDesktopApp
# Add CheapAvaloniaBlazor package
dotnet add package CheapAvaloniaBlazor
Option B: Visual Studio 2022 GUI Users
- File โ New โ Project
- Select "Console App" (.NET 9.0)
- Name your project (e.g., "MyDesktopApp")
- Right-click project โ "Manage NuGet Packages"
- Search for "CheapAvaloniaBlazor" โ Install
Option C: Start with Avalonia Template (Advanced)
# Install Avalonia templates first
dotnet new install Avalonia.ProjectTemplates
# Create Avalonia project, then add CheapAvaloniaBlazor
dotnet new avalonia.app -n MyDesktopApp
cd MyDesktopApp
dotnet add package CheapAvaloniaBlazor
# Note: You'll need to integrate with existing Avalonia setup
๐ก Why start with Console App? CheapAvaloniaBlazor handles all the desktop framework setup for you! No need for complex Avalonia/Blazor project templates - just add the package and you're ready to build.
๐ Quick Start (5 Minutes)
Note for Visual Studio Users: Create folders and files using Solution Explorer โ Right-click project โ Add โ New Folder/Item
1. Update Project File
Edit your .csproj
file to use the Web SDK (includes MVC and Blazor support):
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CheapAvaloniaBlazor" Version="1.0.68" />
</ItemGroup>
</Project>
2. Replace Program.cs
using CheapAvaloniaBlazor.Hosting;
namespace MyDesktopApp;
class Program
{
[STAThread]
public static void Main(string[] args)
{
var builder = new HostBuilder()
.WithTitle("My Desktop App")
.WithSize(1200, 800)
.AddMudBlazor();
// Add your services
builder.Services.AddScoped<IMyService, MyService>();
// Run the app - all Avalonia complexity handled by the package
builder.RunApp(args);
}
}
3. Create Components/_Host.cshtml
Visual Studio: Right-click project โ Add โ New Folder โ "Components", then right-click Components โ Add โ New Item โ "Razor Page"
@page "/"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Desktop App</title>
<base href="~/" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<style>
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
</style>
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<div id="blazor-error-ui">
An error has occurred. This application may no longer respond until reloaded.
<a href="" class="reload">Reload</a>
<a class="dismiss">๐</a>
</div>
<script src="_framework/blazor.server.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>
4. Create App.razor
Visual Studio: Right-click project โ Add โ New Item โ "Razor Component"
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
5. Create Shared/MainLayout.razor
Visual Studio: Right-click project โ Add โ New Folder โ "Shared", then Add โ New Item โ "Razor Component"
@inherits LayoutComponentBase
<MudThemeProvider />
<MudPopoverProvider />
<MudDialogProvider />
<MudSnackbarProvider />
<MudLayout>
<MudAppBar Elevation="1">
<MudIconButton Icon="Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" />
<MudSpacer />
<MudText Typo="Typo.h6">My Desktop App</MudText>
<MudSpacer />
<MudIconButton Icon="Icons.Material.Filled.Settings" Color="Color.Inherit" />
</MudAppBar>
<MudDrawer Open="true" Elevation="1">
<MudDrawerHeader>
<MudText Typo="Typo.h6">Navigation</MudText>
</MudDrawerHeader>
<MudNavMenu>
<MudNavLink Href="/" Match="NavLinkMatch.All" Icon="Icons.Material.Filled.Home">Home</MudNavLink>
<MudNavLink Href="/files" Icon="Icons.Material.Filled.Folder">Files</MudNavLink>
</MudNavMenu>
</MudDrawer>
<MudMainContent>
<MudContainer MaxWidth="MaxWidth.Large" Class="my-16 pt-16">
@Body
</MudContainer>
</MudMainContent>
</MudLayout>
6. Create _Imports.razor
Visual Studio: Right-click project โ Add โ New Item โ "Razor Component" โ Name it "_Imports.razor"
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MudBlazor
@using CheapAvaloniaBlazor
@using CheapAvaloniaBlazor.Services
7. Create Pages/Index.razor
Visual Studio: Right-click project โ Add โ New Folder โ "Pages", then Add โ New Item โ "Razor Component"
@page "/"
@inject IDesktopInteropService Desktop
<PageTitle>Home</PageTitle>
<MudCard>
<MudCardContent>
<MudText Typo="Typo.h4" GutterBottom="true">Welcome to Your Desktop App! ๐</MudText>
<MudText Class="mb-4">
This is a native desktop application built with Blazor and running with full file system access.
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" @onclick="OpenFileDialog">
Open File Dialog
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Secondary" @onclick="ShowNotification" Class="ml-2">
Show Notification
</MudButton>
</MudCardContent>
</MudCard>
@code {
private async Task OpenFileDialog()
{
var file = await Desktop.OpenFileDialogAsync(new()
{
Title = "Select a file",
Filters = new()
{
new() { Name = "Text Files", Extensions = new[] { "*.txt", "*.md" } },
new() { Name = "All Files", Extensions = new[] { "*.*" } }
}
});
if (file != null)
{
await Desktop.ShowNotificationAsync("File Selected", $"You selected: {Path.GetFileName(file)}");
}
}
private async Task ShowNotification()
{
await Desktop.ShowNotificationAsync("Hello Desktop!", "This is a native desktop notification! ๐");
}
}
๐ง Advanced Configuration
HostBuilder Fluent API
var builder = new HostBuilder()
.WithTitle("Advanced Desktop App")
.WithSize(1400, 900)
.WithPosition(100, 100) // Custom window position
.UsePort(5001) // Custom port
.UseHttps(true) // Enable HTTPS
.EnableConsoleLogging(true) // Enable console logging
.AddMudBlazor(config => // Current: MudBlazor support
{
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft;
})
// .AddRadzen() // Coming: Radzen components
// .AddTelerik() // Coming: Telerik UI
// .AddBootstrap() // Coming: Bootstrap components
.AddHttpClient("API", client => // Named HTTP client
{
client.BaseAddress = new Uri("https://api.example.com");
})
.ConfigureOptions(options => // Advanced options
{
options.EnableDevTools = true;
options.EnableContextMenu = false;
options.Resizable = true;
});
// Add custom services
builder.Services.AddSingleton<IDataService, DataService>();
builder.Services.AddScoped<IFileManager, FileManager>();
var window = builder.Build();
window.Run();
Desktop Interop Features
@inject IDesktopInteropService Desktop
// File System Operations
var selectedFile = await Desktop.OpenFileDialogAsync();
var saveLocation = await Desktop.SaveFileDialogAsync();
var folder = await Desktop.OpenFolderDialogAsync();
// File I/O
var content = await Desktop.ReadFileAsync("document.txt");
await Desktop.WriteFileAsync("output.txt", Encoding.UTF8.GetBytes("Hello Desktop!"));
var exists = await Desktop.FileExistsAsync("somefile.txt");
// Window Management
await Desktop.MinimizeWindowAsync();
await Desktop.MaximizeWindowAsync();
await Desktop.SetWindowTitleAsync("New Title");
var state = await Desktop.GetWindowStateAsync();
// System Integration
await Desktop.OpenUrlInBrowserAsync("https://github.com");
await Desktop.ShowNotificationAsync("Title", "Message");
// Clipboard Operations
var clipboardText = await Desktop.GetClipboardTextAsync();
await Desktop.SetClipboardTextAsync("Copied from desktop app!");
// Get system paths
var appDataPath = await Desktop.GetAppDataPathAsync();
var documentsPath = await Desktop.GetDocumentsPathAsync();
๐ Project Structure
MyDesktopApp/
โโโ Program.cs # Application entry point
โโโ App.razor # Blazor router configuration
โโโ Components/
โ โโโ _Host.cshtml # Blazor host page (contains full HTML)
โโโ Pages/
โ โโโ Index.razor # Home page
โ โโโ Files.razor # File management page
โโโ Shared/
โ โโโ MainLayout.razor # Main application layout
โโโ wwwroot/ # Static web assets
โ โโโ css/
โโโ Services/ # Your business logic
โโโ IMyService.cs
๐ Architecture & Integration
How It Works
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Avalonia โ โ Blazor Server โ โ Photino โ
โ (Desktop โโโโโบโ (Web UI & โโโโโบโ (WebView โ
โ Framework) โ โ Components) โ โ Hosting) โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โฒ โฒ โฒ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Native OS โ โ MudBlazor โ โ File System โ
โ Integration โ โ (Material UI) โ โ Access โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
Technology Stack
- UI Layer: Blazor Server + Razor Pages + MudBlazor components
- Desktop Framework: Avalonia (cross-platform window management)
- WebView Host: Photino.NET (native webview embedding)
- Backend: ASP.NET Core (dependency injection, services, middleware)
- Interop: Custom desktop services (file dialogs, notifications, etc.)
๐ ๏ธ Build & Deployment
Development
# Command Line / VS Code Users
dotnet run
# Visual Studio Users
Press F5 or Debug โ Start Debugging
# Hot reload enabled automatically in both environments
# Make changes to .razor files and see instant updates
Production Builds
# Build release version
dotnet publish -c Release -r win-x64 --self-contained
# Create single-file executable
dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true
# Cross-platform builds
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishSingleFile=true
dotnet publish -c Release -r osx-x64 --self-contained -p:PublishSingleFile=true
Distribution
# Windows
MyDesktopApp.exe
# Linux
./MyDesktopApp
# macOS
./MyDesktopApp
๐ System Requirements
Runtime Requirements
- .NET 9.0 or later
- Windows 10+ โ (Tested)
- Linux with WebKit โ ๏ธ (Untested - on roadmap)
- macOS 10.15+ โ ๏ธ (Untested - on roadmap)
Development Requirements
- Visual Studio 2022 (17.8+) or VS Code
- .NET 9.0 SDK
- C# 13 language features
Package Dependencies
Avalonia 11.3.2+
- Desktop frameworkMudBlazor 8.10.0+
- Material Design componentsPhotino.NET 4.0.16+
- WebView hostingMicrosoft.AspNetCore.Components.Web 9.0.7+
- Blazor components
๐ Troubleshooting
Common Issues & Solutions
๐ซ Build Errors
# Ensure correct .NET version
dotnet --version # Should be 9.0+
# Clear and restore packages
dotnet clean
dotnet restore
dotnet build
๐ซ Window Doesn't Appear
- Check if port 5000/5001 is available
- Verify no firewall blocking local connections
- Look for exceptions in console output
- Try different port:
builder.UsePort(8080)
๐ซ MudBlazor Styles Missing
- Verify CSS reference in
_Layout.cshtml
:<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
- Check browser dev tools for 404 errors
- Ensure
AddMudBlazor()
is called in HostBuilder
๐ซ Platform Compatibility Issues
- Linux/macOS: Currently untested - if you encounter issues, please report them!
- Windows: Fully tested and supported
- Dependencies (Avalonia, Photino) should work cross-platform, but integration not verified
๐ซ Visual Studio Specific Issues
- IntelliSense not working: Rebuild solution (Build โ Rebuild Solution)
- Razor syntax errors: Install latest "ASP.NET and web development" workload
- Package restore issues: Tools โ NuGet Package Manager โ "Clear All NuGet Cache(s)"
- Hot reload not working: Enable "Hot Reload on File Save" in Debug settings
๐ซ File Dialog Not Working
- โ Fixed in v1.0.67+ - File dialogs now work via Avalonia StorageProvider
- Ensure you're using latest version:
dotnet add package CheapAvaloniaBlazor
- Check
IDesktopInteropService
injection - If still having issues, please report - architecture was completely rebuilt for file dialog support
๐ซ Hot Reload Not Working
- Restart application
- Check VS/VS Code Blazor extensions
- Verify project targets .NET 9.0
Debug Mode
var builder = new HostBuilder()
.EnableConsoleLogging(true) // Enable detailed logging
.ConfigureOptions(options =>
{
options.EnableDevTools = true; // Enable browser dev tools
});
๐ฏ Example Applications
File Manager
// Complete file browser with MudBlazor TreeView
@inject IDesktopInteropService Desktop
<MudTreeView Items="FileNodes" @bind-SelectedValue="SelectedFile">
<ItemTemplate>
<MudTreeViewItem @bind-Expanded="@context.IsExpanded"
Value="@context"
Icon="@(context.IsDirectory ? Icons.Material.Filled.Folder : Icons.Material.Filled.InsertDriveFile)">
@context.Name
</MudTreeViewItem>
</ItemTemplate>
</MudTreeView>
System Monitor
// Real-time system information dashboard
<MudGrid>
<MudItem xs="12" md="6">
<MudCard>
<MudCardContent>
<MudText Typo="Typo.h6">CPU Usage</MudText>
<MudProgressLinear Value="@cpuUsage" Color="Color.Primary" />
</MudCardContent>
</MudCard>
</MudItem>
</MudGrid>
Database Browser
// SQLite database browser with data grid
<MudDataGrid Items="@DatabaseRecords" Filterable="true" SortMode="SortMode.Multiple">
<Columns>
<PropertyColumn Property="x => x.Id" Title="ID" />
<PropertyColumn Property="x => x.Name" Title="Name" />
<PropertyColumn Property="x => x.CreatedDate" Title="Created" />
</Columns>
</MudDataGrid>
๐จ Project Status & Roadmap
Current Status: Working Alpha v1.0.68 โ
- โ Core Framework: Avalonia + Blazor + Photino integration
- โ NuGet Package: Published and functional
- โ File System Interop: WORKING - Cross-platform file dialogs via Avalonia StorageProvider
- โ Window Management: Minimize, maximize, resize, title changes
- โ JavaScript โ C# Bridge: Full bidirectional communication with ExecuteScriptAsync
- โ MudBlazor Integration: Full component library support
- โ Clean Architecture: Removed legacy code, optimized for stability
Upcoming Features ๐ฃ๏ธ
- ๐ Testing Framework: Unit and integration test support
- ๐ Cross-Platform Testing: Full compatibility validation on Linux and macOS
- ๐ Alternative WebView Hosts: Additional options beyond Photino.NET
- ๐ Alternative UI Frameworks: Support for Radzen, Telerik, Bootstrap, and other Blazor component libraries
- ๐ Enhanced Documentation: More examples and tutorials
- ๐ Performance Optimization: Startup time and memory usage
- ๐ Plugin System: Extensible architecture
- ๐ Visual Designer: Drag-and-drop UI builder
- ๐ Package Templates:
dotnet new
project templates
Known Limitations โ ๏ธ
- Alpha stage project - some breaking changes possible but architecture now stable
- Currently tested on Windows only - Linux and macOS compatibility validation in progress
- MudBlazor-focused currently - other UI framework integrations planned
- Community support - best-effort basis with active development
- Testing infrastructure in development
๐ค Contributing & Support
Project Status
This is a personal hobby project in alpha stage. The core architecture is now stable (v1.0.67+ with working file dialogs), though some features are still evolving. Limited pull requests accepted for bug fixes and documentation improvements.
How to Help
- ๐ Report Issues: Found a bug? Create an issue
- ๐ฌ Provide Feedback: Share your experience and suggestions
- ๐งช Testing: Try it in your projects and report compatibility issues
- ๐ Documentation: Suggest improvements to examples and guides
Getting Support
- GitHub Issues: Technical problems and bug reports
- Discussions: Questions and community help
- Documentation: Check this README and inline code comments
- Expectations: This is a hobby project - support is provided on a best-effort basis
๐ License & Attribution
MIT License - Use freely in personal and commercial projects.
Built With โค๏ธ Using:
- Avalonia - Cross-platform .NET desktop framework
- Blazor - Build interactive web UIs using C#
- MudBlazor - Material Design component library (current default)
- Photino - Lightweight cross-platform WebView
Future integrations planned: Radzen, Telerik, Bootstrap, Blazorise, and more!
๐ง Special Thanks
Documentation analysis and enhancement by Kowalski - the analytical penguin who never met a codebase he couldn't optimize! ๐ค
๐ Get Started Today!
Ready to build your first cross-platform desktop app with web technologies?
# Create new project
dotnet new console -n MyFirstDesktopApp
cd MyFirstDesktopApp
# Add CheapAvaloniaBlazor
dotnet add package CheapAvaloniaBlazor
# Follow the Quick Start guide above
# Build something amazing! ๐
Questions? Issues? Ideas? Open an issue and share your feedback!
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Avalonia (>= 11.3.3)
- Avalonia.Desktop (>= 11.3.3)
- Avalonia.Fonts.Inter (>= 11.3.3)
- Avalonia.Themes.Fluent (>= 11.3.3)
- Microsoft.AspNetCore.Components.Web (>= 9.0.8)
- Microsoft.Extensions.FileProviders.Embedded (>= 9.0.8)
- Microsoft.Extensions.Hosting (>= 9.0.8)
- MudBlazor (>= 8.11.0)
- Photino.NET (>= 4.0.16)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Fixed JavaScript bridge distribution - now accessible via _content path for Blazor static assets