Avalonia ReactiveUI RoutedViewHost导航后界面不更新问题排查
在基于ReactiveUI的Avalonia应用中实现登录页面时,执行登录后的路由代码:
await HostScreen.Router.Navigate.Execute(App.Services.GetRequiredService<MainPageViewModel>()).ToTask();
仅成功实例化MainPageViewModel,但MainPage的构造函数未被调用,界面无变化。但登录结果缓存后,重启应用时OnAppearingAsync能正常打开MainPage。
相关代码
启动代码
public partial class App : Application { public static IServiceProvider Services = null!; // Initialized before use static App() { Akavache.Registrations.Start("App"); var services = new ServiceCollection(); services.UseMicrosoftDependencyResolver(); var resolver = Locator.CurrentMutable; resolver.InitializeSplat(); resolver.InitializeReactiveUI(); ConfigureServices(services); Services = services.BuildServiceProvider(); Services.UseMicrosoftDependencyResolver(); } ..... private static IServiceCollection ConfigureServices(IServiceCollection services) { .... services.AddSingleton<MainWindowViewModel>(); services.AddSingleton<IScreen, MainWindowViewModel>(); services.AddSingleton<IViewFor<MainWindowViewModel>, MainWindow>(); services.AddTransient<IViewFor<LoginViewModel>, Login>(); services.AddTransient<LoginViewModel>(); services.AddTransient<IViewFor<MainPageViewModel>, MainPage>(); services.AddTransient<MainPageViewModel>(); return services; } }
MainWindow.xaml
<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" .... <rxui:RoutedViewHost Router="{Binding Router}"> </rxui:RoutedViewHost> </Window>
MainWindowViewModel
public class MainWindowViewModel : ReactiveObject, IScreen, IActivatableView { public MainWindowViewModel(AuthenticationManager authenticationManager) { AuthenticationManager = authenticationManager; } public RoutingState Router { get; } = new RoutingState(); public ViewModelActivator Activator { get; } = new ViewModelActivator(); public AuthenticationManager AuthenticationManager { get; } public async Task OnAppearingAsync() { if (AuthenticationManager.IsAuthenticated) { await Router.Navigate.Execute(App.Services.GetRequiredService<MainPageViewModel>()).ToTask(); } else { await Router.Navigate.Execute(App.Services.GetRequiredService<LoginViewModel>()).ToTask(); } } }
LoginViewModel
public class LoginViewModel : ReactiveObject, IRoutableViewModel, IActivatableViewModel { private readonly ISchedulerProvider schedulerProvider; public LoginViewModel(IScreen hostScreen, ISchedulerProvider schedulerProvider, AuthenticationManager authenticationManager) { HostScreen = hostScreen; this.schedulerProvider = schedulerProvider; AuthenticationManager = authenticationManager; Login = ReactiveCommand.CreateFromTask(LoginAsync, outputScheduler: schedulerProvider.MainThread); } public string? UrlPathSegment { get; } = nameof(Views.Login); public IScreen HostScreen { get; } public ViewModelActivator Activator { get; } = new ViewModelActivator(); public ReactiveCommand<Unit, Unit> Login { get; } private AuthenticationManager AuthenticationManager { get; } private async Task LoginAsync() { //ToDo: lock button try { if (!AuthenticationManager.IsAuthenticated) { await AuthenticationManager.LoginAsync(); } await HostScreen.Router.Navigate.Execute(App.Services.GetRequiredService<MainPageViewModel>()).ToTask(); } catch (Exception ex) { throw; } } }
1. 线程调度不规范
登录操作在后台线程执行,即便ReactiveCommand指定了outputScheduler,路由命令仍可能未在UI线程执行。ReactiveUI的RoutingState导航操作必须在UI线程调度,否则视图绑定不会触发更新。
解决方法:
用RxApp.MainThreadScheduler包裹路由操作,强制在UI线程执行:
await RxApp.MainThreadScheduler.Schedule(async () => { await HostScreen.Router.Navigate.Execute(App.Services.GetRequiredService<MainPageViewModel>()).ToTask(); });
2. 依赖解析器初始化顺序错误
App静态构造函数中,先调用services.UseMicrosoftDependencyResolver(),再构建ServiceProvider并重复调用该方法,可能导致视图与ViewModel的映射未正确注册到ReactiveUI视图定位器。
解决方法:
调整初始化顺序,先构建服务容器,再设置依赖解析器并初始化Splat/ReactiveUI:
static App() { Akavache.Registrations.Start("App"); var services = new ServiceCollection(); ConfigureServices(services); Services = services.BuildServiceProvider(); // 先绑定服务容器,再初始化框架组件 Services.UseMicrosoftDependencyResolver(); var resolver = Locator.CurrentMutable; resolver.InitializeSplat(); resolver.InitializeReactiveUI(); }
3. MainPageViewModel未实现IRoutableViewModel
RoutedViewHost依赖IRoutableViewModel接口来匹配对应的视图,当前MainPageViewModel未实现该接口,导致视图无法被定位。
解决方法:
为MainPageViewModel添加IRoutableViewModel实现:
public class MainPageViewModel : ReactiveObject, IRoutableViewModel, IActivatableViewModel { public MainPageViewModel(IScreen hostScreen) { HostScreen = hostScreen; } public string? UrlPathSegment { get; } = nameof(Views.MainPage); public IScreen HostScreen { get; } public ViewModelActivator Activator { get; } = new ViewModelActivator(); }
4. 路由绑定未正确生效
检查MainWindow.xaml中RoutedViewHost的Router绑定是否正确,确保MainWindow的DataContext已设置为MainWindowViewModel实例,否则Router绑定会失效,无法响应导航变化。
内容的提问来源于stack exchange,提问作者Bagroy

