A miniature "desktop OS" written in Avalonia — a hands-on tour of MVVM, OOP and DI
A cross-platform desktop application that emulates a small operating-system shell: a wallpaper, a taskbar, in-app "windows" and three toy programs — Notepad, Terminal and File Explorer — running on top of a shared in-memory virtual file system. The project is deliberately structured as a portfolio piece for MVVM, dependency injection and idiomatic object-oriented design in .NET.
┌────────────────────────────────────────────────────────────────────────────┐
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 📝 │ │ 🖥 │ │ 📁 │ desktop icons (launchers) │
│ │ Notepad │ │ Terminal │ │ Files │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌─────────────────────┐ ┌──────────────────────┐ │
│ │ Notepad │ │ Terminal │ in-app windows │
│ │ /docs/welcome.txt │ │ /$ ls │ (DI-resolved VMs) │
│ │ ... │ │ docs/ scripts/ │ │
│ └─────────────────────┘ └──────────────────────┘ │
│ │
│ [📝 Notepad] [🖥 Terminal] [📁 Files] ☀ /🌙 theme │ taskbar
└────────────────────────────────────────────────────────────────────────────┘
Each toy app is a ViewModel registered in the DI container. Opening one calls
IWindowManager.Open<T>(), which adds the resolved VM to the desktop's Windows
observable collection — Avalonia's ItemsControl then templates each VM into a
WindowChrome whose DataTemplate matches the concrete VM type. Files are stored
in a shared IVirtualFileSystem, so the File Explorer sees what Notepad just saved
and re-renders on the next change event.
| Pattern / concept | Where it lives in the codebase |
|---|---|
| MVVM | OsEmulator.ViewModels/* use [ObservableProperty] and [RelayCommand] |
| Dependency Injection | App.OnFrameworkInitializationCompleted builds an IServiceProvider |
| Composition root | A single AddOsEmulator() extension wires every service |
| Strategy / Command | Terminal commands are ICommandHandlers in a CommandRegistry |
| Factory / Service Locator avoidance | IWindowManager.Open<TApp>() resolves VMs from DI, not statics |
| Observer / Reactive | IVirtualFileSystem.Changed event refreshes the explorer automatically |
| Discriminated union | VfsChange.{Created, Updated, Deleted} records |
| OOP base class | AppViewModelBase gives every app Title/Icon/OnClosed |
| Value-object record | FsItem, TerminalLine, DesktopIcon — immutable, equatable |
| Sealed records | Wire / view shapes for safety |
| Theme variant binding | IsDarkTheme toggle drives Application.RequestedThemeVariant |
os-emulator/
├── src/
│ ├── OsEmulator.Core/ Abstractions only (no dependencies)
│ │ ├── Apps/IAppViewModel.cs
│ │ ├── Windows/IWindowManager.cs
│ │ ├── Vfs/IVirtualFileSystem.cs + VfsNode + VfsChange
│ │ └── Terminal/ICommandHandler.cs + TerminalContext
│ ├── OsEmulator.Apps/ Concrete services
│ │ ├── Vfs/InMemoryVfs.cs in-memory tree + events
│ │ ├── Windows/WindowManager.cs z-order + DI-driven Open<T>()
│ │ ├── Terminal/CommandRegistry.cs + BuiltInCommands.cs (ls cd cat echo mkdir rm pwd help)
│ │ └── ServiceCollectionExtensions.cs AddOsEmulator() registration
│ ├── OsEmulator.ViewModels/ CommunityToolkit.Mvvm ViewModels
│ │ ├── AppViewModelBase.cs
│ │ ├── DesktopViewModel.cs icons + windows + theme toggle
│ │ ├── NotepadViewModel.cs
│ │ ├── TerminalViewModel.cs
│ │ └── FileExplorerViewModel.cs
│ └── OsEmulator.Desktop/ Avalonia 11 application (Views + composition root)
│ ├── App.axaml + App.axaml.cs IServiceProvider built here
│ ├── Program.cs
│ └── Views/ MainWindow, WindowChrome, {Notepad,Terminal,FileExplorer}View
└── tests/
└── OsEmulator.Tests/ xUnit + FluentAssertions
dotnet restore
dotnet run --project src/OsEmulator.DesktopThe window opens with three desktop icons (📝 Notepad, 🖥 Terminal, 📁 Files) and a taskbar. Click an icon to open the corresponding app — multiple instances are allowed. Toggle the moon/sun button in the taskbar to switch theme.
ls [path] list directory contents
cd [path] change directory (`..` and `/` work)
cat <path> print file contents
echo <args> print arguments
mkdir <path> create a directory
rm <path> delete a file or empty directory
pwd print the current directory
help list commands
dotnet format --verify-no-changes # check style
dotnet build --configuration Release # build
dotnet test # 20 xUnit tests| Layer | Library |
|---|---|
| UI | Avalonia 11 (Windows / macOS / Linux) |
| MVVM helpers | CommunityToolkit.Mvvm |
| DI | Microsoft.Extensions.DependencyInjection |
| Tests | xUnit + FluentAssertions |
MIT.