diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..2c4e243 --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,1226 @@ + +# 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 +