Xamarin Forms页面绑定值存在但不显示问题求助
问题描述
我创建了一个AccountPage,通过绑定AccountPageViewModel的Username、Name和Description字段展示数据。应用会检查token是否存在,若无则跳转至LoginPage。
当我从LoginPage跳转到CreateAccountPage创建账号后返回AccountPage时,AccountPage的OnAppearing方法已触发,且ViewModel中的绑定值正确,但视图无数据显示。关闭并重启应用后,视图可正常显示绑定值。请问这是什么原因?该如何修复?
相关代码
AccountPage的XAML代码
ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewmodel="clr-namespace:DoggoApp.ViewModels" x:DataType="viewmodel:AccountPageViewModel" BindingContext="AccountPageViewModel" Title="{Binding Username}" BackgroundColor="#FBFDFB" x:Class="DoggoApp.Views.AccountPage"> <ContentPage.Content> <Grid> <Grid.RowDefinitions> <RowDefinition Height="0.27*"/> <RowDefinition Height="0.1*"/> <RowDefinition Height="0.3*"/> <RowDefinition Height="0.7*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <StackLayout Orientation="Horizontal" Grid.Row="0"> <Frame CornerRadius="100" Padding="0" Margin="25" IsClippedToBounds="True"> <Image Source="default_user.jpg" Aspect="AspectFill" Scale="0.44" Margin="-60"/> </Frame> <StackLayout Margin="10,40"> <Label Text=""/> <Label Text="Beiträge" Style="{StaticResource AccountPage}"/> </StackLayout> <StackLayout Margin="10,40"> <Label Text=""/> <Label Text="Follower" Style="{StaticResource AccountPage}"/> </StackLayout> <StackLayout Margin="10,40"> <Label Text=""/> <Label Text="Following" Style="{StaticResource AccountPage}"/> </StackLayout> </StackLayout> <StackLayout Grid.Row="1" Margin="15,-20"> <Label Text="{Binding Name}" FontAttributes="Bold" Style="{StaticResource AccountPage}"/> <Label Text="{Binding Description}" Style="{StaticResource AccountPage}"/> <Frame Padding="0,15"> <Button Text="Edit Profile" Margin="-20" Clicked="EditAccount_Clicked"/> </Frame> </StackLayout>
AccountPage代码后台
namespace DoggoApp.Views { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class AccountPage : ContentPage { private static AccountPageViewModel accountPageViewmodel; public AccountPage() { InitializeComponent(); BindingContext = accountPageViewmodel = new AccountPageViewModel(); } protected override async void OnAppearing() { accountPageViewmodel.NotRefreshTokenAction = (async (obj) => { await DisplayAlert("DoggoApp", "Please log in again", "Ok"); }); base.OnAppearing(); bool LoggedIn = await accountPageViewmodel.Login(); if (!LoggedIn) { await Navigation.PushAsync(new LoginPage()); return; } }
AccountPageViewModel代码
public partial class AccountPageViewModel : ObservableObject, INotifyPropertyChanged { // == constants == private static readonly IAccountService accountService = DependencyService.Get<IAccountService>(); private static readonly ISaveService saveService = DependencyService.Get<ISaveService>(); // == observable Properties == private string username; [ObservableProperty] private string name; [ObservableProperty] private string description; public string Username { get { return username; } set { username = value; OnPropertyChanged(nameof(Username)); } } public Action<bool> NotRefreshTokenAction { get; set; } // == Relay Commands == [RelayCommand] public async Task<bool> Login() { try { bool refreshed = await accountService.RefreshToken(); if(refreshed) { AccountDto dto = await accountService.GetDisplayAccount(); Username = dto.username; Name = dto.name; Description = dto.description; return true; } throw new Exception("refreshed was false"); } catch(Exception ex) { string message = ex.Message; Console.WriteLine(message); return false; } }
CreateAccountPage代码后台
namespace DoggoApp.Views { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class CreateAccountPage : ContentPage { private static CreateAccountViewModel createAccountViewModel; public CreateAccountPage() { InitializeComponent(); BindingContext = createAccountViewModel = new CreateAccountViewModel(); createAccountViewModel.AccountCreatedAction = (async (obj) => { await DisplayAlert("Doggoapp", "Registration Successfull", "Ok"); await Navigation.PopToRootAsync(); }); } } }
问题原因
- 静态ViewModel实例冲突:AccountPage使用了静态的
accountPageViewmodel字段,页面导航返回时复用了旧的ViewModel实例,导致绑定上下文的属性变更通知无法正确触发视图更新。 - BindingContext重复设置:XAML中通过
BindingContext="AccountPageViewModel"设置了绑定上下文,后台代码又重新实例化并赋值,双重初始化导致绑定链路混乱。 - 页面生命周期逻辑顺序错误:OnAppearing中先设置回调再执行
base.OnAppearing(),可能导致页面渲染时ViewModel属性还未完成更新,且未遵循正确的生命周期触发顺序。
修复方案
1. 移除静态ViewModel字段
将AccountPage中的静态ViewModel改为实例字段,确保每次页面初始化都使用新的ViewModel实例:
// 替换原静态字段 private AccountPageViewModel accountPageViewmodel; public AccountPage() { InitializeComponent(); BindingContext = accountPageViewmodel = new AccountPageViewModel(); }
2. 删除XAML中的BindingContext设置
移除XAML里的BindingContext="AccountPageViewModel",仅保留后台代码设置绑定上下文,避免重复初始化:
// 修改后的XAML头部 ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewmodel="clr-namespace:DoggoApp.ViewModels" x:DataType="viewmodel:AccountPageViewModel" Title="{Binding Username}" BackgroundColor="#FBFDFB" x:Class="DoggoApp.Views.AccountPage">
3. 调整OnAppearing逻辑顺序
先执行base.OnAppearing()确保页面生命周期正确触发,再设置回调和执行登录逻辑:
protected override async void OnAppearing() { base.OnAppearing(); // 优先执行基类生命周期方法 accountPageViewmodel.NotRefreshTokenAction = async (obj) => { await DisplayAlert("DoggoApp", "Please log in again", "Ok"); }; bool LoggedIn = await accountPageViewmodel.Login(); if (!LoggedIn) { await Navigation.PushAsync(new LoginPage()); return; } }
4. 确保主线程更新属性(可选)
如果accountService.GetDisplayAccount()是后台线程执行,需要切换到主线程更新ViewModel属性,保证绑定通知被视图接收:
[RelayCommand] public async Task<bool> Login() { try { bool refreshed = await accountService.RefreshToken(); if(refreshed) { AccountDto dto = await accountService.GetDisplayAccount(); // 切换到主线程更新属性 MainThread.BeginInvokeOnMainThread(() => { Username = dto.username; Name = dto.name; Description = dto.description; }); return true; } throw new Exception("refreshed was false"); } catch(Exception ex) { string message = ex.Message; Console.WriteLine(message); return false; } }
内容的提问来源于stack exchange,提问作者WoistdasNiveau
相关产品推荐
相关产品推荐

