【问题标题】:ObservationCollection that implements ISupportIncrementalLoading within ViewModel inside PCL using MVVM architecture for WinRT & WP8/WinPRT supportObservationCollection 使用 MVVM 架构在 PCL 内部的 ViewModel 中实现 ISupportIncrementalLoading,以支持 WinRT 和 WP8/WinPRT
【发布时间】:2013-11-27 12:56:13
【问题描述】:

我的 ViewModel 位于 PCL 中,因为我正在并行开发 Windows 8.1 和 Windows Phone 应用程序。我的 ViewModel 中有一个作为 ObservableCollection 的事物列表。

我在 Windows 8.1 项目的 Page 中有一个 GridView。我希望从我的 ViewModel 中的事物列表中逐步加载项目。通常我会在 ObservableCollection 的自定义子类中实现 ISupportIncrementalLoading,但是,鉴于我的 ViewModel 在 PCL 中,ISupportIncrementalLoading 不可用(WP8 不支持它)。

所以我的问题是,是否有人对我如何在 GridView 的 ItemsSource 绑定和我的 ViewModel 的 Observable Things 属性之间创建某种转换器、适配器或抽象层有任何建议,该属性将实现 ISupportIncrementalLoading 然后调用ViewModel 的 LoadMoreThings 方法并将项目传递给 GridView。

我觉得有一些解决方案,例如在我的 View Models PCL 中创建一个自定义 ISupportIncrementalLoading,然后让 View 层委托给它。

谢谢

【问题讨论】:

    标签: xaml windows-phone-8 windows-runtime winrt-xaml windows-8.1


    【解决方案1】:

    最后,我使用了抽象工厂模式。事实是:

    • 您不能从 PCL ViewModel 层引用 View 层,因为 VM 层不应与 View 层相关。这样做的好处之一是您可以创建 ViewModel 层的另一个使用者,而不依赖于目标平台。例如在一个 ViewModel 库 PCL 项目的背后创建一个 Windows 8 和 Windows Phone 8 应用程序。

    • GridView 是一个 WinRT 组件,可以绑定到 ObservableCollection<T>ObservableCollection<T> 在 View 层和 ViewModel 层中可用。如果你想在你的应用程序中支持增量加载(这对于大型数据集来说是必须的),那么你需要创建一个特殊的ObservableCollection<T> 子类来实现ISupportIncrementalLoading。我们想要做的只是在 ViewModel 项目中创建那个子类,然后你就完成了。 但我们不能这样做,因为ISupportIncrementalLoading 仅在 WinRT 项目中可用。

    这个问题可以通过使用抽象工厂模式来解决。 ViewModel 真正想要的是一个ObservableCollection<T>,但视图层需要一个实现ISupportIncrementalLoading 的 ObservableCollection。所以答案是在 ViewModel 层中定义一个接口,为 ViewModel 提供它想要的东西;我们称之为IPortabilityFactory。然后在 View 层定义IPortabilityFactory 的具体实现,称为PortabilityFactory。在 View 层中使用 IoC 将 IPortabilityFactory(ViewModel 接口)映射到 PortabilityFactory(View 层具体实现)。

    在 ViewModel 类的构造函数中,注入一个 IPortabilityFactory 实例。现在 ViewModel 有一个工厂,它将给它一个 ObservableCollection<T> 实例。

    现在,您无需在 ViewModel 中调用 new ObservableCollection<Thing>(),而是调用 factory.GetIncrementalCollection<Thing>(...)

    好的,我们完成了 ViewModel 层;现在我们需要ObservableCollection<T> 的自定义实现。它被称为IncrementalLoadingCollection,并在视图层中定义。它实现了ISupportIncrementalLoading

    这里是代码和解释,以及 ISupportIncrementalLoading 的实现。

    在 ViewModel 层 (PCL) 我有一个抽象工厂接口。

    public interface IPortabilityFactory
    {
        ObservableCollection<T> GetIncrementalCollection<T>(int take, Func<int, Task<List<T>>> loadMoreItems, Action onBatchStart, Action<List<T>> onBatchComplete);
    }
    

    在视图层(本例中为 Windows 8 应用)我实现了一个像这样的具体工厂:

    public class PortabilityFactory : IPortabilityFactory 
    {
        public ObservableCollection<T> GetIncrementalCollection<T>(int take, Func<int, Task<List<T>>> loadMoreItems, Action onBatchStart, Action<List<T>> onBatchComplete)
        {
            return new IncrementalLoadingCollection<T>(take, loadMoreItems, onBatchStart, onBatchComplete);
        }
    }
    

    同样,在 View 层中,我碰巧将 Unity 用于我的 IoC。创建 IoC 时,我将 IPortabilityFactory(在 PCL 中)映射到 PortabilityFactory(在 View 层;应用程序项目)。

    Container.RegisterType<IPortabilityFactory, PortabilityFactory>(new ContainerControlledLifetimeManager());
    

    我们现在需要创建 ObservableCollection 的子类,代码如下:

    public class IncrementalLoadingCollection<T> 
            : ObservableCollection<T>, ISupportIncrementalLoading
        {
            private Func<int, Task<List<T>>> _loadMoreItems = null;
            private Action<List<T>> _onBatchComplete = null;
            private Action _onBatchStart = null;
    
    
            /// <summary>
            /// How many records to currently skip
            /// </summary>
            private int Skip { get; set; }
    
            /// <summary>
            /// The max number of items to get per batch
            /// </summary>
            private int Take { get; set; }
    
            /// <summary>
            /// The number of items in the last batch retrieved
            /// </summary>
            private int VirtualCount { get; set; }
    
            /// <summary>
            /// .ctor
            /// </summary>
            /// <param name="take">How many items to take per batch</param>
            /// <param name="loadMoreItems">The load more items function</param>
            public IncrementalLoadingCollection(int take, Func<int, Task<List<T>>> loadMoreItems, Action onBatchStart, Action<List<T>> onBatchComplete)
            {
                Take = take;
                _loadMoreItems = loadMoreItems;
                _onBatchStart = onBatchStart;
                _onBatchComplete = onBatchComplete;
                VirtualCount = take;
            }
    
            /// <summary>
            /// Returns whether there are more items (if the current batch size is equal to the amount retrieved then YES)
            /// </summary>
            public bool HasMoreItems
            {
                get { return this.VirtualCount >= Take; }
            }
    
            public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
            {
               CoreDispatcher dispatcher = Window.Current.Dispatcher;
               _onBatchStart(); // This is the UI thread
    
               return Task.Run<LoadMoreItemsResult>(
                    async () =>
                    {
                        var result = await _loadMoreItems(Skip);
                        this.VirtualCount = result.Count;
                        Skip += Take;
    
                        await dispatcher.RunAsync(
                            CoreDispatcherPriority.Normal,
                            () =>
                            {
                                foreach (T item in result) this.Add(item);
                                _onBatchComplete(result); // This is the UI thread
                            });
    
                        return new LoadMoreItemsResult() { Count = (uint)result.Count };
    
                    }).AsAsyncOperation<LoadMoreItemsResult>();
            }
        }
    

    IncrementalLoadingCollection 的构造函数要求 ViewModel 通过工厂提供四个参数:

    • take - 这是页面大小

    • loadMoreItems - 这是对 ViewModel 中的函数的委托引用,该函数将检索下一批项目(重要的是,此函数不会在 UI 线程中运行)

    • onBatchStart - 这将在调用 loadMoreItems 方法之前被调用。这允许我对可能影响视图的 ViewModel 上的属性进行更改。例如,有一个可观察的 IsProcessing 属性,该属性绑定到进度条的 Visibility 属性。

    • onBatchComplete - 这将在检索最新批次并传入项目后立即调用。至关重要的是,此函数将在 UI 线程上调用。

    在 ViewModel 层,我的 ViewModel 上有一个构造函数,它接受一个 IPortabilityFactory 对象:

    public const string IsProcessingPropertyName = "IsProcessing";
    
    private bool _isProcessing = false;
    public bool IsProcessing
    {
        get
        {
            return _isProcessing;
        }
        set
        {
            if (_isProcessing == value)
            {
                return;
            }
            RaisePropertyChanging(IsProcessingPropertyName);
            _isProcessing = value;
            RaisePropertyChanged(IsProcessingPropertyName);
            }
    }
    
        private IPortabilityFactory _factory = null;
        public ViewModel(IPortabilityFactory factory)
        {
            _factory = factory;
            Initialize();
        }
    
    
        private async void Initialize()
        {
            Things = _factory.GetIncrementalCollection<Thing>(10, LoadThings, 
               () => IsProcessing = true, BatchLoaded);
        }
    
        private void BatchLoaded(List<Thing> batch)
        {
            IsProcessing = false;
        }
    
        private async Task<List<Thing>> LoadThings(int skip)
        {
            var items = await _service.GetThings(skip, 10 /*page size*/);
            return items;
        }
    

    我希望这对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-16
      • 2014-10-12
      • 1970-01-01
      • 1970-01-01
      • 2018-03-28
      • 2018-11-29
      相关资源
      最近更新 更多