# OpenMaui Linux Samples - User Guide A collection of sample applications demonstrating .NET MAUI applications running on Linux using the OpenMaui Linux platform. These samples showcase various MAUI controls, navigation patterns, and features working with SkiaSharp-based rendering on Linux desktop. ## Table of Contents - [Overview](#overview) - [Getting Started](#getting-started) - [Prerequisites](#prerequisites) - [Installation](#installation) - [Building the Samples](#building-the-samples) - [Running the Applications](#running-the-applications) - [Sample Applications](#sample-applications) - [ShellDemo](#shelldemo) - [TodoApp](#todoapp) - [WebViewDemo](#webviewdemo) - [Common Workflows](#common-workflows) - [Creating a New Linux MAUI App](#creating-a-new-linux-maui-app) - [Adding OpenMaui Linux Support](#adding-openmaui-linux-support) - [Implementing Navigation](#implementing-navigation) - [Working with Themes](#working-with-themes) - [Project Structure](#project-structure) - [API Reference](#api-reference) - [MauiProgram Configuration](#mauiprogram-configuration) - [Platform Entry Point](#platform-entry-point) - [Navigation APIs](#navigation-apis) - [Dialog Services](#dialog-services) - [Tips and Best Practices](#tips-and-best-practices) - [Troubleshooting](#troubleshooting) ## Overview This repository contains three production-ready sample applications that demonstrate different aspects of building .NET MAUI applications for Linux: - **ShellDemo**: Comprehensive showcase of MAUI controls (buttons, text input, lists, pickers, grids) with Shell-based navigation - **TodoApp**: Full-featured task management app demonstrating NavigationPage, MVVM patterns, and data binding - **WebViewDemo**: Web browser application showcasing WebKitGTK integration with HTML rendering and JavaScript execution All samples are built on the OpenMaui.Controls.Linux platform, which brings .NET MAUI to Linux desktop using SkiaSharp for rendering. ## Getting Started ### Prerequisites Before you begin, ensure you have the following installed on your Linux system: - **.NET 9.0 SDK** or later - **OpenMaui.Controls.Linux** package or source - **GTK 3** (for WebView support) - **WebKitGTK** (for WebViewDemo) Install system dependencies on Ubuntu/Debian: ```bash sudo apt-get update sudo apt-get install dotnet-sdk-9.0 libgtk-3-dev libwebkit2gtk-4.0-dev ``` ### Installation Clone the repository: ```bash git clone https://github.com/openmaui/maui-linux-samples.git cd maui-linux-samples ``` The samples support two development modes: **Local Development** (with OpenMaui source): ```bash # Clone OpenMaui alongside the samples cd .. git clone https://github.com/openmaui/maui-linux.git cd maui-linux-samples ``` **Package Reference** (using NuGet): ```bash # Set environment variable to use package reference export UsePackageReference=true ``` ### Building the Samples Build all samples in the solution: ```bash dotnet build maui-linux-samples.sln ``` Build a specific sample: ```bash cd ShellDemo dotnet build ``` Build for release: ```bash dotnet build -c Release ``` ### Running the Applications Each sample includes a `run.sh` script for easy execution: ```bash cd ShellDemo ./run.sh ``` Or run directly with `dotnet`: ```bash cd ShellDemo dotnet run ``` Run from the compiled output: ```bash cd ShellDemo/bin/Debug/net9.0 ./ShellDemo ``` ## Sample Applications ### ShellDemo A comprehensive demonstration of MAUI controls and Shell navigation patterns. **Features:** - Shell-based navigation with flyout menu - Button controls with various styles and events - Text input controls (Entry, Editor, SearchBar) - Selection controls (CheckBox, Switch, Slider) - Pickers (Picker, DatePicker, TimePicker) - CollectionView with selection and data binding - Progress indicators (ProgressBar, ActivityIndicator) - Grid layouts with various sizing options - Theme switching (Light/Dark mode) - Push/pop navigation for detail pages **Key Files:** - `AppShell.xaml` - Shell configuration with flyout menu - `Pages/HomePage.xaml` - Welcome page with feature cards - `Pages/ButtonsPage.xaml` - Button demonstrations - `Pages/ListsPage.xaml` - CollectionView examples - `Pages/GridsPage.xaml` - Grid layout demonstrations **Running ShellDemo:** ```bash cd ShellDemo ./run.sh ``` **Example: Navigating to a detail page** ```csharp // From any page in ShellDemo private void OnPushDetailClicked(object? sender, EventArgs e) { var success = LinuxViewRenderer.PushPage(new DetailPage("My Item")); Console.WriteLine($"Navigation result: {success}"); } ``` **Log File Location:** ShellDemo writes diagnostic logs to `~/shelldemo.log` for debugging. ### TodoApp A full-featured task management application demonstrating real-world MAUI patterns. **Features:** - NavigationPage-based navigation - CRUD operations (Create, Read, Update, Delete) - Data binding with INotifyPropertyChanged - ObservableCollection for reactive lists - Custom value converters for UI logic - Alternating row colors in lists - Task completion tracking with statistics - Confirmation dialogs for destructive actions - Theme support with dynamic color updates **Key Files:** - `App.xaml.cs` - NavigationPage setup - `TodoItem.cs` - Data model with property change notifications - `TodoService.cs` - Business logic and data management - `Pages/TodoListPage.xaml` - Main list view with statistics - `Pages/TodoDetailPage.xaml` - Task editing and deletion - `Pages/NewTodoPage.xaml` - Task creation form **Running TodoApp:** ```bash cd TodoApp ./run.sh ``` **Example: Adding a new task** ```csharp // In NewTodoPage.xaml.cs private async void OnSaveClicked(object? sender, EventArgs e) { var title = TitleEntry.Text?.Trim(); if (string.IsNullOrEmpty(title)) { TitleEntry.Placeholder = "Title is required!"; TitleEntry.PlaceholderColor = Colors.Red; return; } TodoService.Instance.AddTodo(title, NotesEditor.Text ?? ""); await Navigation.PopAsync(); } ``` **Example: Custom value converter** ```csharp // Converter for alternating row colors with theme support public class AlternatingRowColorConverter : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { bool isDarkMode = Application.Current?.RequestedTheme == AppTheme.Dark; if (value is int index) { if (isDarkMode) return index % 2 == 0 ? Color.FromArgb("#1E1E1E") : Color.FromArgb("#2A2A2A"); return index % 2 == 0 ? Colors.White : Color.FromArgb("#F5F5F5"); } return isDarkMode ? Color.FromArgb("#1E1E1E") : Colors.White; } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } } ``` **Log File Location:** TodoApp writes diagnostic logs to `~/todoapp.log`. ### WebViewDemo A web browser application showcasing WebKitGTK integration with MAUI. **Features:** - WebView with WebKitGTK backend - URL navigation with address bar - Back/forward navigation history - Page reload functionality - HTML content loading (local and remote) - JavaScript execution and evaluation - Progress indication during page loads - Theme switching - Double-click to select all in URL entry **Key Files:** - `Pages/WebViewPage.xaml` - Main browser interface - `Program.cs` - GTK mode initialization for WebView **Running WebViewDemo:** ```bash cd WebViewDemo ./run.sh ``` **Example: Loading a URL** ```csharp private void Navigate() { var url = UrlEntry.Text?.Trim(); if (string.IsNullOrEmpty(url)) return; // Add https:// if not present if (!url.StartsWith("http://") && !url.StartsWith("https://")) url = "https://" + url; MainWebView.Source = new UrlWebViewSource { Url = url }; UrlEntry.Text = url; } ``` **Example: Loading HTML content** ```csharp private void OnLoadHtmlClicked(object? sender, EventArgs e) { var html = @" OpenMaui WebView

Hello from OpenMaui Linux!

This HTML is rendered by WebKitGTK.

"; MainWebView.Source = new HtmlWebViewSource { Html = html }; } ``` **Example: Evaluating JavaScript** ```csharp private async void OnEvalJsClicked(object? sender, EventArgs e) { try { var result = await MainWebView.EvaluateJavaScriptAsync("document.title"); StatusLabel.Text = $"Page title: {result}"; } catch (Exception ex) { StatusLabel.Text = $"Error: {ex.Message}"; } } ``` **GTK Mode Requirement:** WebViewDemo requires GTK mode to be enabled in `Program.cs`: ```csharp LinuxApplication.Run(app, args, options => { options.UseGtk = true; }); ``` **Log File Location:** WebViewDemo writes diagnostic logs to `~/webviewdemo.log`. ## Common Workflows ### Creating a New Linux MAUI App Create a new .NET MAUI application that runs on Linux: **Step 1: Create project structure** ```bash mkdir MyLinuxApp cd MyLinuxApp dotnet new console -n MyLinuxApp ``` **Step 2: Update the .csproj file** ```xml Exe net9.0 enable enable true My Linux App com.mycompany.mylinuxapp 1.0.0 true true linux-x64;linux-arm64 ``` **Step 3: Create Program.cs** ```csharp using Microsoft.Maui.Platform.Linux; namespace MyLinuxApp; class Program { static void Main(string[] args) { var logPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "mylinuxapp.log"); using var logWriter = new StreamWriter(logPath, append: false) { AutoFlush = true }; var multiWriter = new MultiTextWriter(Console.Out, logWriter); Console.SetOut(multiWriter); Console.WriteLine($"Starting MyLinuxApp at {DateTime.Now}"); var app = MauiProgram.CreateMauiApp(); LinuxApplication.Run(app, args); } } class MultiTextWriter : TextWriter { private readonly TextWriter[] _writers; public MultiTextWriter(params TextWriter[] writers) => _writers = writers; public override System.Text.Encoding Encoding => System.Text.Encoding.UTF8; public override void Write(char value) { foreach (var w in _writers) w.Write(value); } public override void WriteLine(string? value) { foreach (var w in _writers) w.WriteLine(value); } } ``` **Step 4: Create MauiProgram.cs** ```csharp using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform.Linux.Hosting; namespace MyLinuxApp; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder.UseMauiApp(); builder.UseLinux(); return builder.Build(); } } ``` **Step 5: Create App.xaml and App.xaml.cs** ```xml ``` ```csharp // App.xaml.cs using Microsoft.Maui.Controls; namespace MyLinuxApp; public partial class App : Application { public App() { InitializeComponent(); } protected override Window CreateWindow(IActivationState? activationState) { return new Window(new MainPage()); } } ``` **Step 6: Build and run** ```bash dotnet build dotnet run ``` ### Adding OpenMaui Linux Support Add Linux support to an existing MAUI project: **Step 1: Update .csproj conditionally** ```xml linux-x64;linux-arm64 ``` **Step 2: Update MauiProgram.cs** ```csharp using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform.Linux.Hosting; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder.UseMauiApp(); // Add Linux platform support builder.UseLinux(); return builder.Build(); } } ``` **Step 3: Create Linux entry point (Program.cs)** ```csharp using Microsoft.Maui.Platform.Linux; namespace YourApp; class Program { static void Main(string[] args) { var app = MauiProgram.CreateMauiApp(); LinuxApplication.Run(app, args); } } ``` ### Implementing Navigation OpenMaui Linux supports multiple navigation patterns: **Shell Navigation (ShellDemo pattern)** ```csharp // AppShell.xaml ``` ```csharp // AppShell.xaml.cs public partial class AppShell : Shell { public AppShell() { InitializeComponent(); // Register routes for push navigation Routing.RegisterRoute("details", typeof(DetailsPage)); } } // Navigate using routes await Shell.Current.GoToAsync("details?id=123"); ``` **NavigationPage Pattern (TodoApp pattern)** ```csharp // App.xaml.cs protected override Window CreateWindow(IActivationState? activationState) { var navigationPage = new NavigationPage(new MainPage()) { BarBackgroundColor = Color.FromArgb("#5C6BC0"), BarTextColor = Colors.White }; return new Window(navigationPage); } // Push a new page await Navigation.PushAsync(new DetailsPage()); // Pop back await Navigation.PopAsync(); ``` **Direct Push/Pop with LinuxViewRenderer** ```csharp // Push a page directly var success = LinuxViewRenderer.PushPage(new DetailPage()); // Pop the current page var success = LinuxViewRenderer.PopPage(); // Navigate to a Shell route LinuxViewRenderer.NavigateToRoute("settings"); ``` ### Working with Themes All samples support Light and Dark themes with dynamic switching. **Step 1: Define theme resources in App.xaml** ```xml #5C6BC0 #FFFFFF #212121 #3949AB #121212 #FFFFFF ``` **Step 2: Use AppThemeBinding in XAML** ```xml ``` **Step 3: Use SetAppThemeColor in code** ```csharp var label = new Label { Text = "Hello World" }; label.SetAppThemeColor( Label.TextColorProperty, Color.FromArgb("#212121"), // Light Color.FromArgb("#FFFFFF") // Dark ); ``` **Step 4: Toggle theme programmatically** ```csharp private void OnThemeToggleClicked(object? sender, EventArgs e) { if (Application.Current == null) return; var currentTheme = Application.Current.UserAppTheme; var newTheme = currentTheme == AppTheme.Dark ? AppTheme.Light : AppTheme.Dark; Application.Current.UserAppTheme = newTheme; } ``` **Step 5: Respond to theme changes** ```csharp protected override void OnAppearing() { base.OnAppearing(); UpdateThemeUI(); } private void UpdateThemeUI() { var isDark = Application.Current?.UserAppTheme == AppTheme.Dark || (Application.Current?.UserAppTheme == AppTheme.Unspecified && Application.Current?.RequestedTheme == AppTheme.Dark); ThemeIcon.Source = isDark ? "light_mode.svg" : "dark_mode.svg"; } ``` ## Project Structure All sample projects follow a consistent structure: ``` SampleApp/ ├── Program.cs # Linux platform entry point ├── MauiProgram.cs # MAUI app configuration ├── App.xaml # Application resources and theme ├── App.xaml.cs # Application code-behind ├── AppShell.xaml # Shell navigation (ShellDemo only) ├── Pages/ # Application pages │ ├── HomePage.xaml │ ├── HomePage.xaml.cs │ └── ... ├── Resources/ # Images, icons, fonts │ ├── AppIcon/ │ └── Images/ ├── run.sh # Launch script └── SampleApp.csproj # Project file ``` **Key Components:** | File | Purpose | |------|---------| | `Program.cs` | Platform entry point, sets up logging and exception handling | | `MauiProgram.cs` | Configures MAUI app with `UseLinux()` extension | | `App.xaml` | Application-level resources, themes, and styles | | `AppShell.xaml` | Shell navigation structure (flyout, tabs, routes) | | `*.csproj` | Project configuration with conditional Linux support | | `run.sh` | Convenience script for launching the app | ## API Reference ### MauiProgram Configuration Configure your MAUI app for Linux in `MauiProgram.cs`: ```csharp using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform.Linux.Hosting; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); // Register your app builder.UseMauiApp(); // Add Linux platform support (registers all handlers) builder.UseLinux(); // Configure fonts (optional) builder.ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); return builder.Build(); } } ``` ### Platform Entry Point Create a Linux entry point in `Program.cs`: ```csharp using Microsoft.Maui.Platform.Linux; class Program { static void Main(string[] args) { // Optional: Set up logging var logPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "myapp.log"); using var logWriter = new StreamWriter(logPath, append: false) { AutoFlush = true }; var multiWriter = new MultiTextWriter(Console.Out, logWriter); Console.SetOut(multiWriter); Console.SetError(multiWriter); // Optional: Global exception handlers AppDomain.CurrentDomain.UnhandledException += (sender, e) => { var ex = e.ExceptionObject as Exception; Console.WriteLine($"[FATAL] Unhandled exception: {ex?.Message}"); }; try { var app = MauiProgram.CreateMauiApp(); // Run with default options LinuxApplication.Run(app, args); // Or run with GTK mode (required for WebView) // LinuxApplication.Run(app, args, options => // { // options.UseGtk = true; // }); } catch (Exception ex) { Console.WriteLine($"[FATAL] Exception in Main: {ex.Message}"); throw; } } } ``` ### Navigation APIs Navigate between pages using these APIs: **LinuxViewRenderer.PushPage** ```csharp // Push a new page onto the navigation stack bool success = LinuxViewRenderer.PushPage(new DetailPage()); ``` **LinuxViewRenderer.PopPage** ```csharp // Pop the current page from the navigation stack bool success = LinuxViewRenderer.PopPage(); ``` **LinuxViewRenderer.NavigateToRoute** ```csharp // Navigate to a registered Shell route LinuxViewRenderer.NavigateToRoute("settings"); ``` **Navigation Property (in NavigationPage)** ```csharp // Push a page await Navigation.PushAsync(new DetailPage()); // Pop a page await Navigation.PopAsync(); // Pop to root await Navigation.PopToRootAsync(); ``` **Shell Navigation** ```csharp // Navigate to a route await Shell.Current.GoToAsync("details"); // Navigate with parameters await Shell.Current.GoToAsync($"details?id={itemId}"); // Navigate back await Shell.Current.GoToAsync(".."); ``` ### Dialog Services Show alerts and confirmation dialogs: **LinuxDialogService.ShowAlertAsync** ```csharp // Show confirmation dialog bool confirmed = await LinuxDialogService.ShowAlertAsync( "Delete Task", "Are you sure you want to delete this task?", "Delete", "Cancel"); if (confirmed) { // User clicked "Delete" DeleteTask(); } ``` **DisplayAlert (MAUI standard)** ```csharp // Show alert with OK button await DisplayAlert("Success", "Task saved successfully", "OK"); // Show confirmation dialog bool answer = await DisplayAlert( "Confirm", "Delete this item?", "Yes", "No"); ``` ## Tips and Best Practices ### Performance 1. **Use compiled bindings** for better performance: ```xml