导航时加载图标不显示问题求助(WPF/Prism)
解决Prism导航时加载图标不显示的问题
这个问题我之前也碰到过,核心原因是导航操作在UI线程同步执行,导致设置LoadingIcon后,UI还没来得及刷新就被导航的阻塞操作占满了,所以图标根本显示不出来。等导航完成时当前视图已经被替换,自然看不到加载状态;返回旧视图时,LoadingIcon还是Visible状态,所以才会突然出现。
问题根源分析
你在NavigateToMainMenu方法里的执行顺序是:
- 设置
LoadingIcon = Visibility.Visible - 立刻调用
_regionManager.RequestNavigate
但这两步都在UI线程执行,而RequestNavigate内部的视图初始化、渲染逻辑是同步阻塞的——WPF来不及更新LoadingIcon的UI状态,就被导航操作占满了线程资源,导致加载图标永远没机会显示。
解决方案
我们需要让UI线程先完成加载图标的刷新,再执行导航操作,同时用异步命令避免UI长时间阻塞。
1. 修改命令为异步支持
Prism提供了AsyncDelegateCommand(在Prism.Commands命名空间下),用来支持异步命令逻辑,替换原来的DelegateCommand。
2. 让UI先刷新加载图标
用await Task.Yield()让出当前UI线程的执行权,让WPF优先处理UI渲染队列,确保加载图标先显示出来,再执行导航。
3. 优化属性变更通知
Prism的ViewModelBase的SetProperty方法已经自动触发PropertyChanged事件,不需要额外调用NotifyPropertyChanged,避免重复触发。
修改后的代码
ViewModel代码
using Prism.Commands; using Prism.Mvvm; using Prism.Regions; using System.Threading.Tasks; using System.Windows; public class LoginViewModel : ViewModelBase, INavigationAware { private readonly IRegionManager _regionManager; private readonly IEventAggregator _eventAggregator; public LoginViewModel(IRegionManager regionManager, IEventAggregator eventAggregator) { _regionManager = regionManager; _eventAggregator = eventAggregator; NavigateToMainMenuCommand = new AsyncDelegateCommand(NavigateToMainMenu); LoadingIcon = Visibility.Hidden; } public AsyncDelegateCommand NavigateToMainMenuCommand { get; private set; } private Visibility _loadingIcon; public Visibility LoadingIcon { get { return _loadingIcon; } set { SetProperty(ref _loadingIcon, value); } // SetProperty已自动触发PropertyChanged } private async Task NavigateToMainMenu() { LoadingIcon = Visibility.Visible; // 让出UI线程,让WPF先刷新加载图标 await Task.Yield(); string mainUri = nameof(SomeRoute.MainMenu); // 执行导航,可选添加回调处理导航结果 _regionManager.RequestNavigate(Regions.MainRegion, mainUri, NavigationCompleted); } private void NavigationCompleted(NavigationResult result) { // 导航完成/失败时的处理(可选) if (!result.Success) { LoadingIcon = Visibility.Hidden; // 这里可以添加导航失败的提示逻辑 } } }
XAML代码修正(注意ListBox拼写)
<UserControl xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" xmlns:fa="http://schemas.fontawesome.io/icons/"> <Grid> <!-- 修正Listbox为ListBox --> <ListBox ItemsSource="{Binding ItemsList}"></ListBox> <Canvas VerticalAlignment="Center" HorizontalAlignment="Center" Margin="-550, 180, 0, 0"> <fa:ImageAwesome Visibility="{Binding LoadingIcon, UpdateSourceTrigger=PropertyChanged}" Icon="Spinner" Spin="True" Canvas.Left="56" Canvas.Top="-17" /> </Canvas> </Grid> <UserControl.InputBindings> <KeyBinding Key="Enter" Command="{Binding NavigateToMainMenuCommand}"/> </UserControl.InputBindings> </UserControl>
额外说明
Task.Yield()的作用是让当前UI线程先处理完pending的渲染任务,确保加载图标先显示,再执行后续的导航阻塞操作。- 如果你的目标视图初始化逻辑特别耗时,还可以考虑把视图的初始化逻辑放到后台线程(注意UI相关操作必须切回UI线程),进一步减少UI阻塞。
内容的提问来源于stack exchange,提问作者Gundwane
相关产品推荐
相关产品推荐

