【问题标题】:navigating back doesn't remember entered data向后导航不记得输入的数据
【发布时间】:2021-12-16 23:10:44
【问题描述】:

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

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

EG:

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

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

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

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

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

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

这是我的视图模型:

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

有什么想法我在这里做错了吗?

【问题讨论】:

  • 登录页面OnAppearing有代码吗?
  • 不。只需单击按钮(验证电子邮件,如果有效,则导航到密码页面)
  • 我没有尝试做你正在做的事情。但是一个想法,如果没有人有更好的答案:覆盖页面的 OnAppearing 和 OnDisappearing。在这些以及 EmailAddress 的设置器上设置断点:登录页面的第二次 OnAppearing 是否在 EmailAddress 设置回(不希望的)初始值之前或之后发生?如果是 AFTER,那么您可以在 OnDisappearing 中缓存,在 OnAppearing 中恢复。为避免弄乱第一次调用,如果您的缓存值为空,请跳过恢复。
  • @ToolmakerSteve 所以,OnAppearing 发生在 viewmodel 更新为原始值之后,所以我实现了你的第一个 hack 想法。这样可行。我不喜欢它大声笑.. 但它肯定让我通过了这个问题。感谢您的想法!
  • 很高兴破解成功了!如果有机会,请在下方添加“您的答案”。这将向其他人展示未来如何解决这个问题。 [虽然这是我的想法,但对人们最有帮助的是查看实际工作代码——我不想花时间输入确切的代码!](48 小时后,您可以回来“接受”你自己的答案。)

标签: xamarin.forms xamarin-shell


【解决方案1】:

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

可能的解决方法:

让viewmodel继承自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

【讨论】:

  • 刚刚尝试了这种解决方法,不幸的是我得到了相同的结果。每当我从第二页回击时,它仍然会恢复到最初传入的内容。开始认为这可能是 Xamarin 的一个错误,因为我无法想象这是预期的结果?
  • 这个答案可能没有解决@Sk93 的问题,但它解决了我的问题,并且比我所做的要好得多(在每个 ViewModel 的顶部声明查询参数并创建设置器来解析字符串值)。
【解决方案2】:

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

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

唯一需要注意的是,如果您想在后续页面中更改它,这种 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();
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    相关资源
    最近更新 更多