.NET MAUI
Microsoft's native cross-platform development framework. Enables development of Windows, macOS, iOS, and Android apps from single codebase. Positioned as Xamarin.Forms successor, achieving native performance.
GitHub Overview
dotnet/maui
.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
Topics
Star History
Framework
.NET MAUI (Multi-platform App UI)
Overview
.NET MAUI (Multi-platform App UI) is a cross-platform UI framework developed by Microsoft that enables building native applications for Windows, macOS, Android, and iOS from a single C# codebase.
Details
.NET MAUI (Multi-platform App UI) is the evolution of Xamarin.Forms, released in 2022 as a comprehensive cross-platform development framework. Using C# and XAML, developers can create native applications targeting Windows, macOS, Android, and iOS from a single codebase. MAUI's key strength lies in its use of platform-specific UI controls, delivering native look and feel across all platforms. On Windows, it integrates with Windows App SDK and WinUI framework for full-featured desktop application development. On macOS, it leverages Mac Catalyst to run iOS apps natively on macOS. The Blazor WebView component enables seamless integration of web technologies within native applications. Hot Reload functionality significantly improves development efficiency, while complete integration with the NuGet package ecosystem provides access to a rich library collection. MAUI represents Microsoft's unified approach to cross-platform development, combining the power of .NET with native platform capabilities.
Advantages and Disadvantages
Advantages
- Single Codebase: Develop for multiple platforms using C# and XAML
- Native Performance: Uses platform-specific UI controls for optimal performance
- Rich Ecosystem: Access to .NET ecosystem and NuGet packages
- Hot Reload: Real-time UI updates and debugging capabilities
- Microsoft Official Support: Continuous development and enterprise support
- Blazor Integration: Ability to use web-based UI components
- Visual Studio Integration: Excellent development experience and tooling
Disadvantages
- Learning Curve: Requires knowledge of XAML, C#, and platform-specific concepts
- App Size: Relatively large due to .NET runtime inclusion
- Platform Limitations: Some advanced native features require platform-specific implementation
- New Technology: Migration from Xamarin.Forms required
- Windows-Centric: Designed primarily around Microsoft technology stack
Key Links
- .NET MAUI Official Site
- .NET MAUI Documentation
- GitHub - dotnet/maui
- .NET MAUI Samples
- Microsoft Learn - .NET MAUI
- .NET MAUI Community
Code Examples
Hello World Application
// MainPage.xaml.cs
namespace HelloMaui;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
CounterBtn.Text = $"Clicked {count} time";
if (count == 1)
CounterBtn.Text += "s";
SemanticScreenReader.Announce(CounterBtn.Text);
}
int count = 0;
}
XAML UI Layout
<!-- MainPage.xaml -->
<ContentPage x:Class="HelloMaui.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ScrollView>
<VerticalStackLayout Spacing="25" Padding="30,0" VerticalOptions="Center">
<Image Source="dotnet_bot.png" HeightRequest="185" Aspect="AspectFit" />
<Label x:Name="WelcomeLabel" Text="Hello, World!" FontSize="32" HorizontalOptions="Center" />
<Label Text="Welcome to .NET Multi-platform App UI" FontSize="18" HorizontalOptions="Center" />
<Button x:Name="CounterBtn" Text="Click me" FontSize="18" Clicked="OnCounterClicked" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Data Binding
// ViewModel Example
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class MainPageViewModel : INotifyPropertyChanged
{
private string _name = "World";
private int _clickCount = 0;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public int ClickCount
{
get => _clickCount;
set => SetProperty(ref _clickCount, value);
}
public string Greeting => $"Hello, {Name}!";
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Platform-Specific Code
// Platforms/Windows/WindowsSpecific.cs
#if WINDOWS
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace HelloMaui.Platforms.Windows
{
public static class WindowsSpecific
{
public static void ConfigureWindow(Window window)
{
// Windows-specific configuration
window.Title = "My MAUI App";
window.ExtendsContentIntoTitleBar = true;
}
}
}
#endif
Database Access (SQLite)
// Models/Note.cs
using SQLite;
public class Note
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
}
// Services/NoteDatabase.cs
public class NoteDatabase
{
SQLiteAsyncConnection Database;
public NoteDatabase()
{
}
async Task Init()
{
if (Database is not null)
return;
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
var result = await Database.CreateTableAsync<Note>();
}
public async Task<List<Note>> GetNotesAsync()
{
await Init();
return await Database.Table<Note>().ToListAsync();
}
public async Task<int> SaveNoteAsync(Note note)
{
await Init();
if (note.Id != 0)
return await Database.UpdateAsync(note);
else
return await Database.InsertAsync(note);
}
}
HTTP Communication and API Calls
// Services/ApiService.cs
using System.Text.Json;
public class ApiService
{
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonOptions;
public ApiService()
{
_httpClient = new HttpClient();
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
}
public async Task<List<Todo>> GetTodosAsync()
{
try
{
var response = await _httpClient.GetAsync("https://jsonplaceholder.typicode.com/todos");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<List<Todo>>(json, _jsonOptions);
}
catch (Exception ex)
{
Debug.WriteLine($"Error: {ex.Message}");
return new List<Todo>();
}
}
}
// Usage in ViewModel
public class TodosViewModel : ObservableObject
{
private readonly ApiService _apiService;
public ObservableCollection<Todo> Todos { get; } = new();
public TodosViewModel(ApiService apiService)
{
_apiService = apiService;
LoadTodosCommand = new AsyncRelayCommand(LoadTodos);
}
public IAsyncRelayCommand LoadTodosCommand { get; }
private async Task LoadTodos()
{
var todos = await _apiService.GetTodosAsync();
Todos.Clear();
foreach (var todo in todos)
{
Todos.Add(todo);
}
}
}