# Dotnet Maui

> Build, review, or migrate .NET MAUI applications across Android, iOS, macOS, and Windows with correct cross-platform UI, platform integration, and native packaging assumptions.

- **Type:** Skill
- **Install:** `agentstack add skill-postpartum-genushyacinthus29-dotnet-skills-dotnet-maui`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Postpartum-genushyacinthus29](https://agentstack.voostack.com/s/postpartum-genushyacinthus29)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Postpartum-genushyacinthus29](https://github.com/Postpartum-genushyacinthus29)
- **Source:** https://github.com/Postpartum-genushyacinthus29/dotnet-skills/tree/main/skills/dotnet-maui

## Install

```sh
agentstack add skill-postpartum-genushyacinthus29-dotnet-skills-dotnet-maui
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# .NET MAUI

## Trigger On

- working on cross-platform mobile or desktop UI in .NET MAUI
- integrating device capabilities, navigation, or platform-specific code
- migrating Xamarin.Forms or aligning a shared codebase across targets
- implementing MVVM patterns in mobile apps

## Documentation

- [.NET MAUI Overview](https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui)
- [Enterprise Patterns](https://learn.microsoft.com/en-us/dotnet/architecture/maui/)
- [MVVM Pattern](https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm)
- [Controls Reference](https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/)
- [Platform Integration](https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/)

### References

- [patterns.md](references/patterns.md) - Shell navigation, platform-specific code, messaging, lifecycle, data binding, and CollectionView patterns
- [anti-patterns.md](references/anti-patterns.md) - Common MAUI mistakes and how to avoid them

## Platform Targets

| Platform | Build Host | Notes |
|----------|------------|-------|
| Android | Windows/Mac | Emulator or device |
| iOS | Mac only | Requires Xcode |
| macOS | Mac only | Catalyst |
| Windows | Windows | WinUI 3 |

## Workflow

1. **Confirm target platforms** — behavior differs across Android, iOS, Mac, Windows
2. **Separate shared UI and platform code** — use handlers and DI
3. **Follow MVVM pattern** — keep views dumb, logic in ViewModels
4. **Handle lifecycle and permissions** — platform contracts need testing
5. **Test on real devices** — emulators don't catch everything

## Project Structure

```
MyApp/
├── MyApp/                    # Shared code
│   ├── App.xaml              # Application entry
│   ├── MauiProgram.cs        # DI and configuration
│   ├── Views/                # XAML pages
│   ├── ViewModels/           # MVVM ViewModels
│   ├── Models/               # Domain models
│   ├── Services/             # Business logic
│   └── Platforms/            # Platform-specific code
│       ├── Android/
│       ├── iOS/
│       ├── MacCatalyst/
│       └── Windows/
└── MyApp.Tests/
```

## MVVM Pattern

### ViewModel with MVVM Toolkit
```csharp
public partial class ProductsViewModel(IProductService productService) : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection _products = [];

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(LoadProductsCommand))]
    private bool _isLoading;

    [RelayCommand(CanExecute = nameof(CanLoadProducts))]
    private async Task LoadProductsAsync()
    {
        IsLoading = true;
        try
        {
            var items = await productService.GetAllAsync();
            Products = new ObservableCollection(items);
        }
        finally
        {
            IsLoading = false;
        }
    }

    private bool CanLoadProducts() => !IsLoading;
}
```

### View Binding
```xml

    
        
            
                
                    
                        
                        
                    
                
            
        
    

```

## Dependency Injection

```csharp
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // Services
        builder.Services.AddSingleton();
        builder.Services.AddSingleton();

        // ViewModels
        builder.Services.AddTransient();
        builder.Services.AddTransient();

        // Pages
        builder.Services.AddTransient();
        builder.Services.AddTransient();

        return builder.Build();
    }
}
```

## Navigation

### Shell Navigation
```csharp
// Register routes
Routing.RegisterRoute(nameof(ProductDetailPage), typeof(ProductDetailPage));

// Navigate with parameters
await Shell.Current.GoToAsync($"{nameof(ProductDetailPage)}?id={product.Id}");

// Receive parameters
[QueryProperty(nameof(ProductId), "id")]
public partial class ProductDetailViewModel : ObservableObject
{
    [ObservableProperty]
    private string _productId;

    partial void OnProductIdChanged(string value)
    {
        LoadProduct(value);
    }
}
```

### Navigation Service
```csharp
public interface INavigationService
{
    Task NavigateToAsync(object? parameter = null);
    Task GoBackAsync();
}

public class NavigationService : INavigationService
{
    public async Task NavigateToAsync(object? parameter = null)
    {
        var route = typeof(TViewModel).Name.Replace("ViewModel", "Page");
        var query = parameter is null ? "" : $"?id={parameter}";
        await Shell.Current.GoToAsync($"{route}{query}");
    }

    public Task GoBackAsync() => Shell.Current.GoToAsync("..");
}
```

## Platform-Specific Code

### Using Partial Classes
```csharp
// Services/DeviceService.cs (shared)
public partial class DeviceService
{
    public partial string GetDeviceId();
}

// Platforms/Android/DeviceService.cs
public partial class DeviceService
{
    public partial string GetDeviceId()
    {
        return Android.Provider.Settings.Secure.GetString(
            Android.App.Application.Context.ContentResolver,
            Android.Provider.Settings.Secure.AndroidId);
    }
}

// Platforms/iOS/DeviceService.cs
public partial class DeviceService
{
    public partial string GetDeviceId()
    {
        return UIKit.UIDevice.CurrentDevice.IdentifierForVendor?.ToString() ?? "";
    }
}
```

### Conditional Compilation
```csharp
public string GetPlatformInfo()
{
#if ANDROID
    return $"Android {Android.OS.Build.VERSION.Release}";
#elif IOS
    return $"iOS {UIKit.UIDevice.CurrentDevice.SystemVersion}";
#elif MACCATALYST
    return "macOS Catalyst";
#elif WINDOWS
    return "Windows";
#else
    return "Unknown";
#endif
}
```

## Anti-Patterns to Avoid

| Anti-Pattern | Why It's Bad | Better Approach |
|--------------|--------------|-----------------|
| God ViewModel | Unmaintainable | Split into focused ViewModels |
| Logic in code-behind | Hard to test | Use MVVM and commands |
| Platform code everywhere | Defeats cross-platform | Use handlers/DI |
| Direct service calls in Views | Tight coupling | Use ViewModel |
| Ignoring lifecycle | Crashes, leaks | Handle lifecycle events |

## Performance Best Practices

1. **Use compiled bindings:**
   ```xml
   
   ```

2. **Virtualize long lists:**
   ```xml
   
   ```

3. **Optimize images:**
   ```csharp
   var image = ImageSource.FromFile("image.png");
   // Use appropriate resolution for platform
   ```

4. **Avoid synchronous work on UI thread:**
   ```csharp
   // Bad
   var data = service.GetData(); // Blocks UI

   // Good
   var data = await service.GetDataAsync();
   ```

## Testing

```csharp
[Fact]
public async Task LoadProducts_UpdatesCollection()
{
    var mockService = new Mock();
    mockService.Setup(s => s.GetAllAsync())
        .ReturnsAsync(new[] { new Product { Name = "Test" } });

    var viewModel = new ProductsViewModel(mockService.Object);

    await viewModel.LoadProductsCommand.ExecuteAsync(null);

    Assert.Single(viewModel.Products);
    Assert.Equal("Test", viewModel.Products[0].Name);
}
```

## Deliver

- shared MAUI code with explicit platform seams
- MVVM pattern with testable ViewModels
- navigation and lifecycle behavior that fits each target
- a realistic build and deployment path for the chosen platforms

## Validate

- cross-platform reuse is real, not superficial
- platform-specific behavior is isolated and testable
- MVVM pattern is followed consistently
- build assumptions for Mac/iOS and Windows are explicit
- performance is acceptable on target devices

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Postpartum-genushyacinthus29](https://github.com/Postpartum-genushyacinthus29)
- **Source:** [Postpartum-genushyacinthus29/dotnet-skills](https://github.com/Postpartum-genushyacinthus29/dotnet-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-postpartum-genushyacinthus29-dotnet-skills-dotnet-maui
- Seller: https://agentstack.voostack.com/s/postpartum-genushyacinthus29
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
