【问题标题】:Advanced MVVM scenario in UWP app - How to use a model class for multiple viewmodels?UWP 应用程序中的高级 MVVM 场景 - 如何将模型类用于多个视图模型?
【发布时间】:2020-01-16 11:00:43
【问题描述】:

我已经编码 4 年多了...现在是时候我需要了解如何以完美的方式使用 MVVM,以便能够完成更困难的编码任务。

更具体地说,我想知道如何将模型对象用于多个视图模型和视图。

示例场景

让我们有一个有两个视图的应用程序:第一个包含ItemModel 对象列表(显示所有可用对象),另一个包含仅包含收藏夹的列表。 ItemModel 有一个 IsFavorite 布尔属性来确定它是否是一个。

直到今天,我总是这样完成这样的场景:

ItemModel类(同时是一个model-viewmodel类):

public class ItemModel : BaseBind   // This class implements INotifyPropertyChanged
{
    public ItemModel()
    {
        // ...
    }

    private bool isFavorite;
    public Boolean IsFavorite
    {
        get { return isFavorite; }
        set { SetProperty(ref isFavorite, value); OnPropertyChanged(); }
    }

    // All the model's and viewmodel's properties and functions are stored here...

    public void GenericFunc()
    {
        App.Current.AppViewModel.GenericAppFunc(this);
    }

    public void FavoriteFunc()
    {
        App.Current.AppViewModel.FavoriteAppFunc(this);
    }

    public ItemModel Clone()
    {
        // Cloning the item with "new" constructor...
    }
}

AppViewModel 类:

public class AppViewModel
{
    public AppViewModel()
    {
        // Initializing...
    }

    public ObservableCollection<ItemModel> ItemsList { get; set; }
    public ObservableCollection<ItemModel> FavoritesList { get; set; }


    public void UpdateFavorites()
    {
        FavoritesList.Clear();

        foreach (var it in ItemsList)
        {
            if (it.IsFavorite) FavoritesList.Add(it.Clone());
        }
    }

    public void GenericAppFunc(ItemModel item)
    {
        // ...
    }

    public void FavoriteAppFunc(ItemModel item)
    {
        // ...
    }

}

第一个视图,列出所有项目:

<ListView ItemsSource="{x:Bind ViewModel.ItemsList, Mode=OneWay}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="vm:ItemModel">
                        <StackPanel>
                            <TextBlock Text="{x:Bind ItemTitle, Mode=OneWay}"/>
                            <Button Content="Generic func" Click="{x:Bind GenericFunc}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

第二个视图,只列出收藏夹:

<ListView ItemsSource="{x:Bind ViewModel.FavoritesList, Mode=OneWay}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="vm:ItemModel">
                        <StackPanel>
                            <TextBlock Text="{x:Bind ItemTitle, Mode=OneWay}"/>
                            <Button Content="Favorite func" Click="{x:Bind FavoriteFunc}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

这是我做这一切的方式...有两个完全独立的列表并在用户每次导航到收藏夹视图时克隆对象。

它有效,它总是这样做......但这绝对不是一个好的模式。

总而言之,这就是我目前正在做的事情......

...这就是我想要实现的目标:

为了有这样的轻量级模型...

public class ItemModel
{
    public ItemModel()
    {
        // ...
    }

    public Boolean IsFavorite { get; set; }

    // Only the model's properties and functions are stored here...

}

...并将其用于多个视图模型/视图。

最好的问候,感谢您的关注。

【问题讨论】:

  • 您可以完全按照您在轻量级模型中编写的操作,但让我澄清一下,您是说您想在多个视图模型中使用/访问相同的项目列表吗?另外,为什么不在 ListView 上使用 item click 事件,这样就可以使用中继命令在 viewmodel 中移动事件处理程序?

标签: c# mvvm data-binding uwp


【解决方案1】:

在 UWP 中,没有在单个数据源之上过滤数据。虽然在 WPF 中可以使用相同的项目 ObservableCollection 并仅在使用 CollectionViewSource 绑定视图时进行过滤,但似乎唯一的解决方案是 having two different collections 就像你正在做的那样。

在 UWP 的 CollectionViewSource 类中确实没有 Filter 属性,因此 [...] 您将不得不过滤源集合本身(或者像您已经在做的那样使用两个不同的集合)。

当然,您可以通过挂钩到您的项目的PropertyChanged 事件来消除调用UpdateFavorites() 的需要,并在设置它们的IsFavorite 属性时将它们从一个集合移动到另一个集合,如下所示:

public AppViewModel()
{
    // Subscribing to changes in collection of items
    ItemsList .CollectionChanged += ItemList_CollectionChanged;
}

private void ItemList_CollectionChanged(object sender, EventArgs e) {
    //Here subscribe/unsubscribe to PropertyChanged event of each item
    //as they come and go in the collection
}

private void Item_PropertyChanged(object sender, EventArgs e) {
    //Here check IsFavorite property and add/remove the item from the favorites list.
    //You can even instantiate another type of viewmodel object specific to favorites items.
}

关于 MVVM 模式,我建议像您建议的那样拥有一个轻量级的 ItemModel 模型,并将它们的集合存储在您的模型中。然后你可以有两个集合,一个包含ItemViewMode 对象,一个包含FavoriteItemViewModel 对象在您的AppViewModel 中。您还可以拥有一个带有IsFavorite 属性的ItemViewModel 类,并使用触发器和样式让它们以不同的方式显示。

【讨论】:

    【解决方案2】:

    我看不出克隆的理由。

    会有什么问题:

    foreach (var it in ItemsList)
    {
        //if (it.IsFavorite) FavoritesList.Add(it.Clone());
          if (it.IsFavorite) FavoritesList.Add(it);
    }
    

    或者,只需将收藏夹列表设置为过滤版本:

    public List<Item> FavoritesList => ItemsList.Where(x => x.IsFavorit);
    

    只要您通知正确的操作,这应该有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-04
      • 2013-10-12
      • 2010-12-07
      • 1970-01-01
      • 2015-03-05
      • 2018-01-11
      • 1970-01-01
      相关资源
      最近更新 更多