Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[![NuGet](https://img.shields.io/nuget/v/AsyncNavigation.svg?label=Core&color=004880)](https://www.nuget.org/packages/AsyncNavigation)
[![NuGet](https://img.shields.io/nuget/v/AsyncNavigation.Avalonia.svg?label=Avalonia&color=8b45e0)](https://www.nuget.org/packages/AsyncNavigation.Avalonia)
[![NuGet](https://img.shields.io/nuget/v/AsyncNavigation.Wpf.svg?label=WPF&color=0078d4)](https://www.nuget.org/packages/AsyncNavigation.Wpf)
[![WinUI 3](https://img.shields.io/badge/WinUI%203-in%20development-orange)](samples/Sample.WinUI)
[![License: MIT](https://img.shields.io/github/license/NeverMorewd/AsyncNavigation)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-8.0%2B-512BD4)](https://dotnet.microsoft.com)

Expand All @@ -31,6 +32,19 @@

---

## Platform Support

| Platform | Status | Package / Sample |
|---|---|---|
| Avalonia | Stable | `AsyncNavigation.Avalonia` |
| WPF | Stable | `AsyncNavigation.Wpf` |
| WinUI 3 | **In development** | [`Sample.WinUI`](samples/Sample.WinUI) |

> [!WARNING]
> WinUI 3 support is under active development. It includes content, items, tab, `NavigationView`, dialog, window, and indicator support, but its APIs and behavior may still change. Test it carefully before using it in production.

---

## Installation

```bash
Expand All @@ -41,6 +55,8 @@ dotnet add package AsyncNavigation.Avalonia
dotnet add package AsyncNavigation.Wpf
```

WinUI 3 is currently available from source and through the repository sample while development continues.

---

## Quick Start
Expand Down
16 changes: 16 additions & 0 deletions readme_zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[![NuGet](https://img.shields.io/nuget/v/AsyncNavigation.svg?label=Core&color=004880)](https://www.nuget.org/packages/AsyncNavigation)
[![NuGet](https://img.shields.io/nuget/v/AsyncNavigation.Avalonia.svg?label=Avalonia&color=8b45e0)](https://www.nuget.org/packages/AsyncNavigation.Avalonia)
[![NuGet](https://img.shields.io/nuget/v/AsyncNavigation.Wpf.svg?label=WPF&color=0078d4)](https://www.nuget.org/packages/AsyncNavigation.Wpf)
[![WinUI 3](https://img.shields.io/badge/WinUI%203-%E5%BC%80%E5%8F%91%E4%B8%AD-orange)](samples/Sample.WinUI)
[![License: MIT](https://img.shields.io/github/license/NeverMorewd/AsyncNavigation)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-8.0%2B-512BD4)](https://dotnet.microsoft.com)

Expand All @@ -31,6 +32,19 @@

---

## 平台支持

| 平台 | 状态 | 包 / 示例 |
|---|---|---|
| Avalonia | 稳定 | `AsyncNavigation.Avalonia` |
| WPF | 稳定 | `AsyncNavigation.Wpf` |
| WinUI 3 | **开发中** | [`Sample.WinUI`](samples/Sample.WinUI) |

> [!WARNING]
> WinUI 3 支持仍处于积极开发阶段。目前已包含 Content、Items、Tab、`NavigationView`、对话框、窗口和加载指示器支持,但 API 与行为仍可能发生变化。在生产环境使用前请充分测试。

---

## 安装

```bash
Expand All @@ -41,6 +55,8 @@ dotnet add package AsyncNavigation.Avalonia
dotnet add package AsyncNavigation.Wpf
```

WinUI 3 目前可通过源码和仓库中的示例项目进行体验,正式包仍在开发中。

---

## 快速开始
Expand Down
3 changes: 3 additions & 0 deletions samples/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
<PackageVersion Include="Avalonia.Browser" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Android" Version="$(AvaloniaVersion)" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
<PackageVersion Include="ReactiveUI.WinUI" Version="23.2.28" />
<PackageVersion Include="SkiaSharp" Version="$(SkiaSharpVersion)" />
<PackageVersion Include="SkiaSharp.NativeAssets.WebAssembly" Version="$(SkiaSharpVersion)" />
<PackageVersion Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.2.0.2" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.6" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.3.1" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.2526" />
<PackageVersion Include="Pipboy.Avalonia" Version="1.0.0" />
<PackageVersion Include="ReactiveUI" Version="23.2.28" />
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="2.5.1" />
Expand Down
6 changes: 6 additions & 0 deletions samples/Sample.Common/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ private void ShowWindow(string param)
var result = _dialogService.ShowWindowDialog(param);
}

[ReactiveCommand(CanExecute = nameof(SupportDialog))]
private async Task AsyncShowWindow(string param)
{
await _dialogService.ShowWindowDialogAsync(param);
}

[ReactiveCommand]
private async Task GoForward()
{
Expand Down
2 changes: 1 addition & 1 deletion samples/Sample.Common/Sample.Common.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
15 changes: 15 additions & 0 deletions samples/Sample.WinUI/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Application
x:Class="Sample.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:Sample.WinUI.Converters">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibility" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityInverted" Invert="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
74 changes: 74 additions & 0 deletions samples/Sample.WinUI/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using AsyncNavigation;
using AsyncNavigation.Core;
using AsyncNavigation.WinUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using ReactiveUI.Builder;
using Sample.Common;
using Sample.WinUI.Views;
using Sample.WinUI.Regions;

namespace Sample.WinUI;

public partial class App : Application
{
private Window? _window;

public App()
{
InitializeComponent();
}

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
RxAppBuilder.CreateReactiveUIBuilder()
.WithWinUI()
.BuildApp();
var services = new ServiceCollection();
services.AddNavigationSupport()
.AddSingleton<MainWindowViewModel>()
.RegisterRegionAdapter<ListBoxRegionAdapter>()
.RegisterView<LightView, LightViewModel>(nameof(LightView))
.RegisterView<ItemsRegionView, ItemsRegionViewModel>(nameof(ItemsRegionView))
.RegisterView<ChildContentRegionView, ChildContentRegionViewModel>(nameof(ChildContentRegionView))
.RegisterView<TabRegionView, TabRegionViewModel>(nameof(TabRegionView))
.RegisterView<NavigationViewRegionView, LightViewModel>(nameof(NavigationViewRegionView))
.RegisterView<HeavyView, HeavyViewModel>(nameof(HeavyView))
.RegisterDialogWindow<AWindow, LightViewModel>(nameof(AWindow))
.RegisterInnerIndicatorProvider<InnerIndicatorProvider>()
.RegisterRegionIndicatorProvider<MessageBoxIndicatorProvider>()
.RegisterView<ListBoxRegionView, ListBoxRegionViewModel>(nameof(ListBoxRegionView))
.RegisterRouter((mapper, _) =>
{
mapper.MapNavigation("Path_ChildHeavyView",
new NavigationTarget("MainRegion", nameof(ChildContentRegionView)),
new NavigationTarget("ChildContentRegion", nameof(HeavyView)));
mapper.MapNavigation("Path_ChildAView",
new NavigationTarget("MainRegion", nameof(ChildContentRegionView)),
new NavigationTarget("ChildContentRegion", nameof(LightView)));
mapper.MapNavigation("Path_TabHeavyView",
new NavigationTarget("MainRegion", nameof(TabRegionView)),
new NavigationTarget("TabRegion", nameof(HeavyView)));
mapper.MapNavigation("Tab.Tab_A",
new NavigationTarget("MainRegion", nameof(TabRegionView)),
new NavigationTarget("TabRegion", nameof(LightView)))
.WithSegments("Tab", "Tab_A");
mapper.MapNavigation("Path_UnknownView", new NavigationTarget("UnknownRegion", "UnknownView"))
.WithFallback(new NavigationTarget("MainRegion", nameof(LightView)));
});

var serviceProvider = services.BuildServiceProvider();
Resources[nameof(IconDescriptorConverter)] = serviceProvider.GetRequiredService<IconDescriptorConverter>();

_window = new MainWindow
{
Content = new MainPage
{
DataContext = serviceProvider.GetRequiredService<MainWindowViewModel>()
}
};
_window.Title = "AsyncNavigation · WinUI 3 Sample";
_window.AppWindow.Resize(new Windows.Graphics.SizeInt32(1100, 720));
_window.Activate();
}
}
15 changes: 15 additions & 0 deletions samples/Sample.WinUI/Converters/BoolToVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;

namespace Sample.WinUI.Converters;

public sealed class BoolToVisibilityConverter : IValueConverter
{
public bool Invert { get; set; }

public object Convert(object value, Type targetType, object parameter, string language)
=> value is bool visible && visible != Invert ? Visibility.Visible : Visibility.Collapsed;

public object ConvertBack(object value, Type targetType, object parameter, string language)
=> throw new NotSupportedException();
}
39 changes: 39 additions & 0 deletions samples/Sample.WinUI/InnerIndicatorProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using AsyncNavigation;
using AsyncNavigation.WinUI;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;

namespace Sample.WinUI;

internal sealed class InnerIndicatorProvider : IInnerIndicatorProvider
{
public bool HasErrorIndicator(NavigationContext context) => true;
public bool HasLoadingIndicator(NavigationContext context) => true;

public UIElement GetLoadingIndicator(NavigationContext context)
{
var cancel = new Button { Content = "Cancel", HorizontalAlignment = HorizontalAlignment.Center };
cancel.Click += async (_, _) =>
{
cancel.IsEnabled = false;
cancel.Content = "Cancelling…";
await context.CancelAndWaitAsync();
};
var panel = new StackPanel { Spacing = 12, VerticalAlignment = VerticalAlignment.Center };
panel.Children.Add(new TextBlock { Text = "Loading…", FontSize = 24, HorizontalAlignment = HorizontalAlignment.Center });
panel.Children.Add(new TextBlock { Text = context.ToString(), TextWrapping = TextWrapping.Wrap, HorizontalAlignment = HorizontalAlignment.Center });
panel.Children.Add(new ProgressBar { IsIndeterminate = true, Width = 260 });
panel.Children.Add(cancel);
return new Border { Padding = new Thickness(24), Child = panel };
}

public UIElement GetErrorIndicator(NavigationContext context)
{
var panel = new StackPanel { Spacing = 12, VerticalAlignment = VerticalAlignment.Center };
panel.Children.Add(new TextBlock { Text = "Navigation failed", FontSize = 24, Foreground = new SolidColorBrush(Colors.Red), HorizontalAlignment = HorizontalAlignment.Center });
panel.Children.Add(new TextBlock { Text = context.ToString(), TextWrapping = TextWrapping.Wrap, HorizontalAlignment = HorizontalAlignment.Center });
return new Border { Padding = new Thickness(24), Child = panel };
}
}
42 changes: 42 additions & 0 deletions samples/Sample.WinUI/MainPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Page
x:Class="Sample.WinUI.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:an="using:AsyncNavigation.WinUI">
<Grid ColumnDefinitions="260,*" RowDefinitions="*,Auto">
<ScrollViewer Grid.Column="0" Padding="16">
<StackPanel Spacing="8">
<TextBlock FontSize="24" FontWeight="SemiBold" Text="Navigation" />
<ComboBox
Header="Registered view"
ItemsSource="{Binding Views}"
SelectedItem="{Binding SelectedView, Mode=TwoWay}" />
<Button Command="{Binding GoBackCommand}" Content="Go back" />
<Button Command="{Binding GoForwardCommand}" Content="Go forward" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="LightView" Content="Light view" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="HeavyView" Content="Heavy view" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="HeavyView:Delay" Content="Heavy view (delayed)" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="LightView:New" Content="New light view" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="HeavyView:Error" Content="Heavy view (error)" />
<Button Command="{Binding AsyncNavigateAndForgetCommand}" CommandParameter="LightView:New" Content="Light view (forget/new)" />
<Button Command="{Binding AsyncNavigateAndForgetCommand}" CommandParameter="HeavyView:Delay" Content="Heavy view (forget/delayed)" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="ItemsRegionView" Content="Items region" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="TabRegionView" Content="TabView region" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="NavigationViewRegionView" Content="NavigationView region" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="ChildContentRegionView" Content="Child content region" />
<Button Command="{Binding AsyncNavigateCommand}" CommandParameter="ListBoxRegionView" Content="Custom ListBox region" />
<Button Command="{Binding ShowCommand}" CommandParameter="LightView" Content="Show light dialog" />
<Button Command="{Binding ShowCommand}" CommandParameter="HeavyView" Content="Show heavy dialog" />
<Button Command="{Binding AsyncShowDialogCommand}" CommandParameter="LightView" Content="Show dialog async" />
<Button Command="{Binding AsyncShowDialogWithCancellingCommand}" CommandParameter="LightView" Content="Show dialog async (cancel after 2s)" />
<Button Content="ShowDialog sync (not supported by WinUI)" IsEnabled="False" />
<Button Command="{Binding AsyncShowWindowCommand}" CommandParameter="AWindow" Content="Show custom window" />
<Button Command="{Binding CollectCommand}" Content="Collect cached views" />
</StackPanel>
</ScrollViewer>
<Border Grid.Column="1" Margin="0,16,16,0" BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" BorderThickness="1" CornerRadius="8">
<ContentControl an:RegionManager.RegionName="MainRegion" />
</Border>
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" Margin="16,10" HorizontalAlignment="Center" Text="{Binding FooterText}" />
</Grid>
</Page>
11 changes: 11 additions & 0 deletions samples/Sample.WinUI/MainPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.UI.Xaml.Controls;

namespace Sample.WinUI;

public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
}
}
7 changes: 7 additions & 0 deletions samples/Sample.WinUI/MainWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Microsoft.UI.Xaml;

namespace Sample.WinUI;

public sealed class MainWindow : Window
{
}
61 changes: 61 additions & 0 deletions samples/Sample.WinUI/MessageBoxIndicatorProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using AsyncNavigation;
using AsyncNavigation.Abstractions;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;

namespace Sample.WinUI;

internal sealed class MessageBoxIndicatorProvider : IRegionIndicatorProvider
{
private readonly MessageBoxIndicator _indicator = new();
public bool HasIndicator(string regionName) => true;
public IRegionIndicator GetIndicator(string regionName) => _indicator;
}

internal sealed class MessageBoxIndicator : IRegionIndicator
{
private Window? _loadingWindow;

public Task ShowLoadingAsync(NavigationContext context)
{
_loadingWindow = CreateWindow("Loading", context.ToString(), Colors.Orange);
_loadingWindow.Activate();
return Task.CompletedTask;
}

public Task ShowErrorAsync(NavigationContext context, Exception? innerException = null)
{
var window = CreateWindow("Navigation error", $"{context}\n\n{innerException}", Colors.Red);
window.Activate();
return Task.CompletedTask;
}

public Task OnLoadedAsync(NavigationContext context) => CloseLoadingWindow();
public Task OnCancelledAsync(NavigationContext context) => CloseLoadingWindow();

private Task CloseLoadingWindow()
{
_loadingWindow?.Close();
_loadingWindow = null;
return Task.CompletedTask;
}

private static Window CreateWindow(string title, string? message, Windows.UI.Color color)
{
var window = new Window { Title = title };
window.Content = new ScrollViewer
{
Content = new TextBlock
{
Margin = new Thickness(24),
Text = message,
TextWrapping = TextWrapping.Wrap,
Foreground = new SolidColorBrush(color)
}
};
window.AppWindow.Resize(new Windows.Graphics.SizeInt32(480, 240));
return window;
}
}
Loading
Loading