Xamarin应用Shell跳转至TaskPage后无法返回上一级页面问题求解
问题描述
操作流程如下:
- 从列表选中一个
category - 加载
Tasks page - 基于上一页选中的
categoryId加载对应Tasks,此时可以正常返回Category page✔️ - 从列表选中一个
Task - 加载
Task Page - 页面正常加载任务详情,但无法返回上一级Tasks page ❌
复现动图

相关代码
CategoriesViewModel
public class CategoriesViewModel : BaseViewModel { public ObservableCollection<CategoryModel> Categories { get; } = new ObservableCollection<CategoryModel>(); public Command LoadCategoriesCommand { get; } public Command<CategoryModel> SelectedCategory { get; } public CategoriesViewModel() { Title = "Categories"; LoadCategoriesCommand = new Command(async () => await LoadCategories()); SelectedCategory = new Command<CategoryModel>(OnSelectedCategory); } private async Task LoadCategories() { IsBusy = true; try { Categories.Clear(); var categories = await DatabaseService.GetCategoriesAsync(); foreach (var category in categories) { this.Categories.Add(category); } } catch (Exception) { throw; } finally { IsBusy = false; } } private async void OnSelectedCategory(CategoryModel category) { if (category == null) return; await Shell.Current.GoToAsync($"{nameof(TasksPage)}?{nameof(TasksViewModel.CategoryId)}={category.CategoryId}"); } public void OnAppearing() { IsBusy = true; } }
TasksViewModel
[QueryProperty(nameof(CategoryId), nameof(CategoryId))] public class TasksViewModel : BaseViewModel { public ObservableCollection<TaskModel> Tasks { get; } = new ObservableCollection<TaskModel>(); private int categoryId; public int CategoryId { get { return categoryId; } set { categoryId = value; } } public Command LoadTasksCommand { get; set; } public Command<TaskModel> SelectedTask { get; set; } public TasksViewModel() { Title = "Tasks"; LoadTasksCommand = new Command(async () => await LoadTasks()); SelectedTask = new Command<TaskModel>(OnSelectedTask); } private async Task LoadTasks() { IsBusy = true; try { this.Tasks.Clear(); var tasks = await DatabaseService.GetTasksAsync(CategoryId); foreach (var task in tasks) { this.Tasks.Add(task); } } catch (Exception) { throw; } finally { IsBusy = false; } } private async void OnSelectedTask(TaskModel task) { if (task == null) return; await Shell.Current.GoToAsync($"{nameof(TaskPage)}?{nameof(TaskViewModel.TaskId)}={task.TaskId}"); } public void OnAppearing() { IsBusy = true; } }
TaskViewModel
[QueryProperty(nameof(TaskId), nameof(TaskId))] public class TaskViewModel : BaseViewModel { private int taskId; public int TaskId { get { return taskId; } set { taskId = value; } } private string taskTitle; public string TaskTitle { get { return taskTitle; } set { taskTitle = value; OnPropertyChanged(nameof(TaskTitle)); } } private string description; public string Description { get { return description; } set { description = value; OnPropertyChanged(nameof(Description)); } } public Command LoadTaskCommand { get; } public TaskViewModel() { LoadTaskCommand = new Command(async () => await LoadTask()); } private async Task LoadTask() { IsBusy = true; try { var task = await DatabaseService.GetTaskAsync(TaskId); this.TaskTitle = task.Title; this.Description = task.Description; } catch (Exception) { throw; } finally { IsBusy = false; } } public void OnAppearing() { IsBusy = true; } }
更新1
我尝试将TasksViewModel中的跳转代码:
await Shell.Current.GoToAsync($"{nameof(TaskPage)}?{nameof(TaskViewModel.TaskId)}={task.TaskId}");
替换为:
await Shell.Current.Navigation.PushAsync(new AboutPage());
问题仍然复现。
更新2
补充TaskPage.xaml代码:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:vm="clr-namespace:SomeProject.ViewModels" x:Class="SomeProject.Views.Task.TaskPage" Title="{Binding TaskTitle}"> <ContentPage.Content> <RefreshView x:DataType="vm:TaskViewModel" Command="{Binding LoadTaskCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}"> <StackLayout> <Label Text="{Binding Description}" /> </StackLayout> </RefreshView> </ContentPage.Content> </ContentPage>
以及TaskPage.xaml.cs代码:
public partial class TaskPage : ContentPage { TaskViewModel _viewModel; public TaskPage() { InitializeComponent(); BindingContext = _viewModel = new TaskViewModel(); } protected override void OnAppearing() { base.OnAppearing(); _viewModel.OnAppearing(); } }
更新3
补充路由注册代码:
public AppShell() { InitializeComponent(); Routing.RegisterRoute(nameof(CategoriesView), typeof(CategoriesView)); Routing.RegisterRoute(nameof(TasksPage), typeof(TasksPage)); Routing.RegisterRoute(nameof(TaskPage), typeof(TaskPage)); }
修复方案
问题原因
所有ViewModel的OnAppearing方法仅将IsBusy属性设为true,但未触发对应的加载命令,导致IsBusy始终为true。由于页面中RefreshView的IsRefreshing属性与IsBusy双向绑定,RefreshView会一直处于刷新状态,拦截所有用户交互事件,最终导致导航返回按钮点击无响应。
修复步骤
- 修改
CategoriesViewModel的OnAppearing方法,触发分类加载命令,保证加载完成后IsBusy会被重置为false:
public void OnAppearing() { IsBusy = true; LoadCategoriesCommand.Execute(null); }
- 修改
TasksViewModel的OnAppearing方法,触发任务列表加载命令:
public void OnAppearing() { IsBusy = true; LoadTasksCommand.Execute(null); }
- 修改
TaskViewModel的OnAppearing方法,触发任务详情加载命令:
public void OnAppearing() { IsBusy = true; LoadTaskCommand.Execute(null); }
验证与额外检查
- 若暂时不需要进入页面自动刷新,可直接注释掉
OnAppearing中设置IsBusy = true的代码,或临时移除TaskPage中的RefreshView,即可快速验证返回功能是否恢复。 - 请确认
BaseViewModel中的IsBusy属性正确实现了INotifyPropertyChanged属性变更通知,否则RefreshView无法感知IsBusy的状态变更,也会导致刷新状态卡住。
内容的提问来源于stack exchange,提问作者DeveloperLV
相关产品推荐
相关产品推荐

