向后导航不记得输入的数据

navigating back doesn't remember entered data

我正在尝试在 Xamarin Forms Shell 中创建一个登录流程,其中包括用户在其中输入电子邮件地址的第一页和输入密码的第二页。 我希望用户能够从密码页面返回并更改输入的电子邮件地址。

这一切都很好,如果最初导航到第一页时,我没有通过查询参数设置电子邮件...如果我这样做了,当用户从密码页面返回到电子邮件地址页面时,显示最初传入的电子邮件 - 无论用户对其进行了任何更改。

EG:

如果电子邮件页面最初导航到使用此代码:

await Shell.Current.GoToAsync($"{nameof(test1)}");

电子邮件地址默认为空。如果用户将其更改为 hello@world.com 并“继续”到密码页面,然后导航回来,电子邮件地址仍然是预期的 hello@world.com

但是,如果电子邮件页面最初导航到使用此代码:

var testEmail = "invalid$";    
await Shell.Current.GoToAsync($"{nameof(test1)}?EmailAddress={testEmail}");

电子邮件地址按预期显示为 invalid$。如果用户随后将其更改为 hello@world.com 并“继续”到密码页面,密码页面将按预期在标签中显示 hello@world.com,但如果用户返回,电子邮件地址将恢复为 invalid$ 在电子邮件输入页面(第一页)。

这是我的视图模型:

[QueryProperty(nameof(EmailAddress), nameof(EmailAddress))]
    public class LoginViewModel : BaseViewModel
    {
        string emailAddress;
        public string EmailAddress
        {
            get
            {
                return emailAddress;
            }
            set
            {
                SetProperty(ref emailAddress, Uri.UnescapeDataString(value));
            }
        }
    }

这是第一页(电子邮件条目):

<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"             
             xmlns:vm="clr-namespace:The_In_Plaice.ViewModels"             
             mc:Ignorable="d"
             x:Class="The_In_Plaice.Views.test1">
    <ContentPage.BindingContext>
        <vm:LoginViewModel/>
    </ContentPage.BindingContext>
    <ContentPage.Content>
        <StackLayout>
            <Entry x:Name="txtEmail" Text="{Binding EmailAddress}" Placeholder="email"/>
            <Button Clicked="Button_Clicked"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

(代码隐藏)

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class test1 : ContentPage
{
    public test1()
    {
        InitializeComponent();
    }

    private async void Button_Clicked(object sender, EventArgs e)
    {
        var email = txtEmail.Text;
        await Shell.Current.GoToAsync($"{nameof(test2)}?EmailAddress={email}");
    }
}

第二页:

<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"             
             xmlns:vm="clr-namespace:The_In_Plaice.ViewModels"             
             mc:Ignorable="d"
             x:Class="The_In_Plaice.Views.test2">
    <ContentPage.BindingContext>
        <vm:LoginViewModel/>
    </ContentPage.BindingContext>
    <ContentPage.Content>
        <StackLayout>
            <Label Text="{Binding EmailAddress}"/>
            <Entry Text="{Binding Password}" IsPassword="True" Placeholder="password"/>
            <Button/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

(代码隐藏)

 [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class test2 : ContentPage
    {
        public test2()
        {
            InitializeComponent();
        }
    }

知道我做错了什么吗?

不要使用QueryPropertyAttribute 传递数据,可能在导航到登录页面时,会触发setter方法并恢复为原始值。

可能的解决方法:

使视图模型继承自 IQueryAttributable,您可以从 ApplyQueryAttributes 方法中检索值。

public class LoginViewModel : IQueryAttributable, BaseViewModel
{
    string emailAddress;
    public string EmailAddress
    {
        get
        {
           return emailAddress;
        }
        set
        {
           SetProperty(ref emailAddress, Uri.UnescapeDataString(value));
        }
   }

    public void ApplyQueryAttributes(IDictionary<string, string> query)
    {
        // The query parameter requires URL decoding.
        string email = HttpUtility.UrlDecode(query["EmailAddress"]);
        EmailAddress = email ;
    }
}

详情参考https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation#process-navigation-data-using-a-single-method.

我在这里寻求的解决方案是根据 ToolMakerSteve 的评论实施“hack”。

以下更改已添加到后面的第一页代码中,它基本上只是在消失时存储当前电子邮件地址,并在出现时替换视图模型中的任何内容(如果已设置)。

唯一需要注意的是,如果您想在后续页面中对其进行更改,此 hack 将不起作用,但就我的目的而言,它工作得很好。

        private string EditedEmailAddress;
        protected override void OnAppearing()
        {
            if (!string.IsNullOrEmpty(EditedEmailAddress))
                ViewModel.EmailAddress = EditedEmailAddress;            
            base.OnAppearing();
        }

        protected override void OnDisappearing()
        {
            EditedEmailAddress = ViewModel.EmailAddress;
            base.OnDisappearing();
        }