ObservationCollection 使用 MVVM 架构在 PCL 内的 ViewModel 中实现 ISupportIncrementalLoading 以支持 WinRT 和 WP8/WinPRT

2024-04-12

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

我在 Windows 8.1 项目的页面内有一个 GridView。我希望从 ViewModel 中的事物列表中增量加载项目。通常我会在 ObservableCollection 的自定义子类中实现 ISupportIncrementalLoading,但是,考虑到我的 ViewModel 位于 PCL 中,ISupportIncrementalLoading 不可用(WP8 不支持它)。

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

我觉得有一些解决方案,例如在我的视图模型 PCL 中创建自定义 ISupportIncrementalLoading,然后将视图层委托给它。

thanks


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

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

  • The GridView是一个 WinRT 组件,可以绑定到ObservableCollection<T>. ObservableCollection<T>里面可用both视图层和视图模型层。如果您想支持应用程序内部的增量加载(这对于大型数据集来说是必须的),那么您需要创建一个特殊的子类ObservableCollection<T>实现ISupportIncrementalLoading。我们什么want只需在 ViewModel 项目中创建该子类即可。但我们不能这样做因为ISupportIncrementalLoading仅在 WinRT 项目中可用。

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

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

现在而不是打电话new ObservableCollection<Thing>()在您调用的 ViewModel 中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);
    }
}

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

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;
    }

我希望这可以帮助别人。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ObservationCollection 使用 MVVM 架构在 PCL 内的 ViewModel 中实现 ISupportIncrementalLoading 以支持 WinRT 和 WP8/WinPRT 的相关文章

  • 从程序集中加载 ResourceDictionary

    我在文件系统的某个地方有一个程序集 例如 C temp test dll 在该程序集中有一个 ResourceDictionary 例如 abc xaml 我怎样才能获得该资源字典 也许有一种使用反射的方法 到目前为止我还没有找到解决方案
  • 我可以将 XAML/WPF 窗口复制到第二个窗口中,例如画中画电视吗?

    我有一个带有两个 XAML WPF 窗口 派生自 NavigationWindow 的应用程序 每个窗口都包含一个父 UserControl 其中放置了所有子控件 在其中一个窗口中 我想以画中画电视的方式显示第二个窗口的内容 实际上只是父
  • Silverlight/WPF 按钮上的 MouseOver 样式

    努力为鼠标悬停的按钮设置样式 我已经成功地设置了按钮的样式 纯红色 但我希望每当鼠标悬停时它都会更改为纯黑色 我是 XAML 新手 我可以看到它需要某种类型的故事板 动画 不确定具体如何执行此操作 任何帮助将不胜感激 这与 WPF 和 Si
  • Python:导航到便携式设备目录 (Windows 7)

    我正在尝试解决我创建的一个问题 https stackoverflow com questions 21381652 python find record time of mp4 movie noredirect 1 comment3234
  • 文件夹结构中的链接文件

    在 Visual Studio 中 当我将文件从一个项目链接到下一个项目时 在尝试加载链接的资源时出现以下错误 仅当链接文件位于文件夹内时才会发生这种情况 Error Message pack application projectName
  • AppBarButton.Icon 在运行时不会改变

    根据某些条件 我在运行时更新 AppBarButton 图标时遇到问题
  • 如何让我的 Windows Phone 7 应用程序出现在 Windows Phone 8 市场中?

    当我进行构建时 我一定做错了什么 我希望当前的更新适用于 Windows Phone 7 和 Windows Phone 8 我正在使用 WP8 SDK 并面向 OS 8 当我上传 XAP 时 我得到了 AnyCPU xap 现在我的应用程
  • 如何在 XAML 中根据窗口或屏幕大小设置网格列最大宽度

    我的窗口中有一个 3 列网格 第一列上有一个 GridSplitter 我想将第一列的 MaxWidth 设置为父窗口或页面的三分之一Width or ActualWidth 并且如果可能的话我更愿意在 XAML 中执行此操作 这是一些在
  • Xamarin.Forms:如何访问 ListView.GroupHeaderTemplate 中 ListView.ItemTemplate 中使用的集合?

    我需要访问 ListView GroupHeaderTemplate 中 Switch 中的 OnOff 属性 该属性位于 ListView ItemTemplate 使用的集合中 我尝试了很多方法但没有成功 有人遇到过这个问题吗 gt
  • 如何像在iOS中一样在WP8中使用Prefix.pch?

    在 iOS 中 我有 5 个使用相同代码的应用程序 我能够引用公共类并使用 Prefix pch 文件中的值来分隔应用程序名称 主题和颜色 我的前缀文件有这些 define APPLICATION NAME Application01 de
  • 如果在构造函数中初始化该属性,则似乎不会设置 XAML 绑定

    当属性在构造函数内初始化时 我遇到了控件模板内数据绑定的问题 这是展示案例 您也可以下载样品溶液 http www filehosting org file details 75794 WpfApplication5 zip 自定义控件1
  • Windows Phone 8 性能进度条

    我是否需要在 Windows Phone 8 中使用性能进度条 在我的 Windows Phone 7 7 5 应用程序中 我使用了工具包附带的进度条 性能进度条 是否有必要在Windows Phone 8中使用相同的或者简单的进度条就足够
  • 从模板绑定到 viewmodel 的属性

    我为我的 GameViewModel 创建了一个视图 我有一些像这样的xaml
  • Windows 8.1 上的 Meteor 构建错误

    我已经在 Windows 8 1 64 位上安装了 Meteor 以继续开发现有的 Meteor 应用程序 但是 我无法启动它 Problem 我运行 meteor run 它启动代理 MongoDB 选择包 在最后一步 构建应用程序 之后
  • XAML解析异常

    我有一个简单的 XAML 页面 当它作为 Visual Studio 中任何应用程序的一部分加载时 加载效果良好 但是 当我使用 ClickOnce 部署此应用程序时 出现以下异常 Type System Windows Markup Xa
  • 屏幕键盘显示时调整 winrt 页面大小

    我有一个 Windows 8 1 C 应用程序 它显示一个带有相当大文本框的页面 几乎覆盖整个页面 它是一个书写应用程序 当屏幕键盘出现时 它会覆盖文本框的一半 我想调整文本框 甚至整个页面 的大小 使其不被键盘覆盖 我现在尝试使用静态 I
  • 使用 INotifyPropertyChanged

    有人可以解释一下为什么在 wpf 中使用绑定时需要使用 INotifyPropertyChanged 的 实现吗 我可以在不实现此接口的情况下绑定属性吗 例如我有代码 public class StudentData INotifyProp
  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • 如何订阅 C++/CX 中 Windows 运行时组件内引发的事件?

    我在 Windows 运行时组件 用 C 编写 中有一个引发事件的类 我无法弄清楚如何在引用该组件的 C CX 应用程序中订阅这些事件 C 代码 在 Windows 运行时组件中 public sealed class Messenger
  • Winforms 中的 WPF ElementHost 最大化时崩溃 (Windows)

    我正在尝试将新的 WPF 控件集成到现有的 WinForms 应用程序中 并使用 ElementHost Dock Fill 来托管以下 XAML UserControl NET 4 当我将 WinForm 设置为最大化时 我的整个操作系统

随机推荐