【问题标题】:xamarin.forms: tracking only user intiated changesxamarin.forms:仅跟踪用户发起的更改
【发布时间】:2018-05-08 09:51:46
【问题描述】:

我找不到满足以下要求的 Xamarin.Forms 应用概念:

  • 用户直接发起的对任何值的任何更改都需要在“更改表”中添加时间戳和跟踪

  • 可能无法跟踪对非用户直接发起的任何值的任何更改(例如,应用程序启动时屏幕初始化引起的更改,或后台任务拉入的更新)。尽管如此,它们还是需要出现在屏幕上。

示例:在屏幕上,有一个“名称”输入。在应用程序首次加载时,输入的姓氏会从某个本地持久性存储中提取,并写入屏幕,但不会记录此更改。

后台任务不时地向 Web 服务查询“名称”,如果找到,名称会在屏幕和持久性存储中被覆盖。不会跟踪这两个更改,因为它们不是用户发起的。

然而,一旦用户键入名称,该值就会存储在持久性存储中,并且会跟踪更改(时间戳并写入本地持久性存储中的“更改表”。

相同的原则必须适用于所有 Xamarin.forms 控件,例如开关也是如此。如果绑定到 bool 属性的“完成”开关被用户翻转,则需要跟踪更改,如果在初始化期间或通过后台更新设置开关,则不得跟踪更改。我提到了开关控件,因为我已经遇到了关于 SO 和其他地方描述的 Xamarin.forms 开关控件的特定问题(如果以编程方式更改基础值,则 OnToggled 会意外触发)。

谁能帮忙?谢谢!

【问题讨论】:

    标签: xamarin.forms


    【解决方案1】:

    如果您使用的是 MVVM 模式,您可以执行以下操作:

    为记录的视图模型创建一个抽象

    public interface ILoggedViewModel : INotifyPropertyChanged
    {
        bool IsChangingProgrammatically { get; }
    }
    

    您必须记录的每个视图模型(很可能是每个视图模型)都将实现此接口(包括由INotifyPropertyChanged 定义的PropertyChanged 事件)。当您创建视图模型时(一个非常好的解决方案是 DI,例如使用 Prisms ViewModelLocator - 也许您必须手动注册视图模型)您使用订阅 PropertyChanged 事件的类注册它们。

    在以编程方式更改任何属性之前,您将IsChangingProgrammatically 设置为true,然后将其重置(可能在finally-block 中,以防止在发生异常时不会重置属性)。每当引发PropertyChangedEvent 时,都会调用记录器上的处理程序。然后记录器检查发件人上的IsChangingProgrammatically,如果是false,则假定更改已由用户启动并记录活动。

    编辑:

    为了使其正常工作,您不能在开始长时间运行操作之前设置IsChangingProgrammatically,然后再将其重置,而只能围绕设置值,否则长时间运行操作期间的用户交互不会记录下来。

    private async void UpdateData()
    {
        try
        {
            var data = await _repository.GetData();
            this.IsChangingProgrammatically = true;
            Data = data;
        }
        finally
        {
            this.IsChangingProgrammatically = false;
        }
    }
    

    编辑 2:

    有一种方法可以避免这些时间问题。您可以从 PropertyChangedEventArgs 创建一个派生类,该派生类被抛出以指示属性已以编程方式更改

    class PropertyChangedProgrammaticallyEventArgs : PropertyChangedEventArgs
    {
        public PropertyChangedProgrammaticallyEventArgs (string propertyName) : base(propertyName) { }
    }
    

    在您的基本视图模型中,您声明类似

    void ChangePropertyBackingField<T>(ref T backingField, string propertyName, T value)
    {
        backingField = value;
        PropertyChanged?.Invoke(this, new PropertyChangedProgrammaticallyEventArgs(propertyName));
    }
    

    当属性以编程方式更改但仍导致 UI 更新时,创建一个特殊的 PropertyChangedEventArgs 实现。通过这种方式,您可以根据记录器中的其他更改更改编程更改

    void OnPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
    {
        if(!(eventArgs is PropertyChangedProgrammaticallyEventArgs ))
        {
            // log user interaction
        }
    }
    

    【讨论】:

    • 我已经有了这样的方法,但失败的原因有两个: - 一旦后台任务启动并开始更新项目,就会设置 IsCachangeProgrammatically 标志。当任务尚未完成时,用户更改的项目不会记录。 - 当应用程序对数据库进行初始读取时会出现同样的问题。
    • 根据您上次的编辑:是否围绕每个单独的项目设置 IsCchangeProgrammatically 确实可以解决丢失用户更新日志记录的问题?恕我直言,这种方法只会降低日志中缺少用户更改的可能性。
    • 你不会 100% 确定它不会发生,但我不认为 Xamarin.Forms 无论如何都可以执行用户交互的防水记录。它的可能性有多大,取决于许多因素,例如以编程方式更改值的频率。
    • @Nimral 还有另一种可能,请参阅我的第二次编辑。通过这种方式,您应该能够区分用户交互和程序化交互。
    • 感谢 Paul 的 Edit2,还没有尝试过,但看起来很有希望。如果我测试它,我会告诉你会发生什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-30
    • 1970-01-01
    • 1970-01-01
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多