推迟PropertyChanged事件,直到视图绑定设置完成

时间:2014-01-07 06:23:21

标签: mvvmcross

我们的一些MVVMcross视图依赖于远程服务来完全显示自己。我们通常使用ViewModel的Init()中的任务来启动它以使其异步。 ViewModel属性在完成时在Task中设置,通过PropertyChanged通知更新UI。

有时远程数据(和任务)在View绑定它的侦听器之前完成,因此没有收到属性更改事件。

async Init and Property Changed in MvvmCross触及了这个问题,但解决方案感觉就像复制了表示逻辑。

我们已成功缓存PropertyChanged通知,直到ViewDidLoad结束,但我们想通过挂钩MVX框架将下面的内容变成更通用的解决方案。

有没有办法挂钩mvvmcross的视图创建,以便在viewDidLoad完成后关闭我们的代码?

基本视图模型

public abstract class BaseViewModel : MvxViewModel{
        protected bool _deferPropertyChangedEvents = true;
        private readonly List<PropertyChangedEventArgs> _deferedPropertyChangedEvents = new List<PropertyChangedEventArgs>();

        public override void RaisePropertyChanged(PropertyChangedEventArgs changedArgs)
        {
            lock(_deferedPropertyChangedEvents){
               if (!_deferPropertyChangedEvents)
               {
                  base.RaisePropertyChanged(changedArgs);
               }
               else
               {
                  // buffer it up
                  _deferedPropertyChangedEvents.Add(changedArgs);
               }
            }
        }

        public void EndDeferringPropertyChangedEvents()
        {
            lock(_deferedPropertyChangedEvents){    
              _deferPropertyChangedEvents = false;

              // playback all buffered notifications
              foreach (var e in _deferedPropertyChangedEvents)
              {
                  RaisePropertyChanged(e);
              }

              _deferedPropertyChangedEvents.Clear();
            }
        }
}

示例视图

public class SomeView : MvxViewController
    {
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            var bindings = this.CreateBindingSet<StopView, SomeViewModel>();
            .....
            bindings.Apply();

            // plays back any PropertyChanged() notifications that were buffered
            // up while the view was initializing
            // ---> want to find a way to have MVX call this
            ViewModel.EndDeferringPropertyChangedEvents();
        }
    }

1 个答案:

答案 0 :(得分:1)

作为一个简单的答案,我相信您可以使用BaseViewModel演员轻松调用自己的专栏:

        // ---> want to find a way to have MVX call this
        ((BaseViewModel)ViewModel).EndDeferringPropertyChangedEvents();

但是,在更技术性的说明中,我认为进一步检查和理解为什么需要这个Deferring代码可能是有用的 - 进一步了解潜在的线程问题是什么。

目前有很多因素让我感到困惑::

  1. 在第bindings.Apply();行中,所有当前绑定属性值都应从ViewModel转移到View - 因此调用EndDeferringPropertyChangedEvents();在下一行应该(理论上)很少得到不同的值。

  2. 此外,默认的MvvmCross RaisePropertyChanged方法将通知更改为UI线程。因为在UI线程上也调用ViewDidLoad,这意味着RaisePropertyChanged期间在后台线程上进行的任何ViewDidLoad调用都应自动延迟,直到ViewDidLoad完成后UI线程可用。

  3. 查看MvxNotifyPropertyChanged代码,我可以看到mutli-threading可能通过此自动RaisePropertyChanged延迟找到方法的唯一潜在差距在于此优化检查:

            // check for subscription before potentially causing a cross-threaded call
            if (PropertyChanged == null)
                return;
    

    (来自https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNotifyPropertyChanged.cs#L76

  4. 如果您的ViewModel Init方法也使用async进行Task管理,那么此async代码也应该使用UI线程 - 所以此异步操作的“回调”也应该编组回UI线程(因此不应在ViewDidLoad本身执行)。

  5. 正如我所说,这些因素令我感到困惑 - 我没有明确的答案/解释 - 抱歉!但是,我希望看到一个示例问题并尝试在通用级别帮助解决它。