You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
                });
        }
    }
}

问题原因

  1. 静态ViewModel实例冲突:AccountPage使用了静态的accountPageViewmodel字段,页面导航返回时复用了旧的ViewModel实例,导致绑定上下文的属性变更通知无法正确触发视图更新。
  2. BindingContext重复设置:XAML中通过BindingContext="AccountPageViewModel"设置了绑定上下文,后台代码又重新实例化并赋值,双重初始化导致绑定链路混乱。
  3. 页面生命周期逻辑顺序错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 22:54:33