如何在ViewModels之间访问数据?

时间:2017-03-25 20:01:49

标签: c# wpf mvvm viewmodel

我目前正在开展一个项目,为了简化说明,我们说TabControl中有两个标签...

在一个选项卡中,您将文件夹添加到ListBox。

在另一个标签中,有一个列表框,显示您添加的所有文件夹中的所有项目。

每个选项卡都是一个ViewModel(这是为了简化代码,因为在一个ViewModel中转储所有代码会使其难以阅读和理解)。

为了让这个程序工作,两个ViewModel都需要访问项目列表:一个是因为它必须显示它们,另一个因为它必须添加它们。

我无法弄清楚如何做到这一点。我最初认为共享数据很糟糕,这不应该首先发生,但后来我意识到我无法想到其他任何方式来做到这一点。

我是MVVM的新手(这是我使用MVVM的第一个真正的应用程序)并且最初开始使用它,因为我无法在类之间访问数据,并且认为MVVM会以某种方式排序这个问题,但在这里我又来了

如果有人能告诉我如何做到这一点,我会很感激,并可能通过示例代码解释它。我也愿意接受对我的方法的建议和建设性的批评。

6 个答案:

答案 0 :(得分:3)

首先,您应该了解MVVM中的 View 是什么。把它看作一个小组。可以嵌入Window,TabControl甚至是ListBox项目的面板。一个也可以包含子面板的面板。基本上,如果您的应用程序不仅仅是一个简单的输入表单,那么它很可能会有多个 View 。不要试图将所有内容放在一个 View / ViewModel 中,因为这会使事情变得非常复杂。您希望拥有所谓的层次结构视图及其相应的 ViewModels 。会有很多 Views / ViewModels 但它们会相对简单且易于维护(这里是使用PRISM在视图之间切换的小例子,但你可以得到主要的想法https://youtu.be/ZfBy2nfykqY?t=45m34s)。

  

每个选项卡都是一个ViewModel(这是为了简化代码,因为在一个ViewModel中转储所有代码会使其难以阅读和理解)。

这是正确的方法。下面是一些伪代码,它描述了您的示例应用程序方案的外观:

// MODEL:

public class Model
{
    ObservableCollection<Item> ListOfItems;
}

public class Item
{
}

// VIEWMODELS:

public class MainWindowViewModel
{
    Model Model { get; set; }

    Tab1ViewModel Tab1ViewModel { get; set; }
    Tab2ViewModel Tab2ViewModel { get; set; }

    public MainWindowViewModel()
    {
        Model = new Model();
        Tab1ViewModel = new Tab1ViewModel(Model);
        Tab2ViewModel = new Tab2ViewModel(Model);
    }
}

public class Tab1ViewModel
{
    ObservableCollection<ItemViewModel> ItemViewModels { get; set; } // Bind it to ListBox's ItemsSource

    public Tab1ViewModel(Model m)
    {
        ItemViewModels = new ObservableCollection<ItemViewModel>();
        // Populate ItemViewModels and keep it in sync with ListOfItems model by subscribing to its CollectionChanged event.
    }
}

public class Tab2ViewModel
{
    ObservableCollection<ItemViewModel> ItemViewModels { get; set; } // Bind it to ListBox's ItemsSource

    public Tab2ViewModel(Model m)
    {
        ItemViewModels = new ObservableCollection<ItemViewModel>();
        // Populate ItemViewModels and keep it in sync with ListOfItems model by subscribing to its CollectionChanged event.
    }

}

public class ItemViewModel
{
    Item Item { get; set; } // Model

    public ItemViewModel(Item item)
    {
        Item = item;
    }
}

现在,您可以在不同的 Views 中显示相同的数据,并对其执行不同的操作。每个 View 都会自动更新,因为它引用了相同的模型

您也可以使用 EventAggregator 或类似内容在 ViewModels 之间进行通信。

尽量避免使用可从应用程序中的任何位置访问的数据的静态类/单例,因为这会破坏 Encapsulation 原则。

答案 1 :(得分:0)

您可以拥有单个对象并从任何地方获取/设置其属性。

看看这个例子;

public sealed class ApplicationState
{
    private static readonly ApplicationState instance = new ApplicationState();

    static ApplicationState()
    {
    }

    private ApplicationState()
    {
    }

    public static ApplicationState Instance
    {
        get
        {
            return instance;
        }
    }


    public string SharedString {get;set;}
}

现在您可以从任何位置设置此SharedString属性;

ApplicationState.Instance.SharedString = "hello from VM1"

并从另一个视图模型中读取它;

Debug.WriteLine(ApplicationState.Instance.SharedString)

您可以查看this link以了解有关单身人士的更多信息

你甚至可以使你的ApplicationState单例成为ObservableObject并从你的视图绑定到它的属性,如;

Value="{Binding SharedString, Source={x:Static ApplicationState.Instance}}"

答案 2 :(得分:0)

  

因为在一个ViewModel中转储所有代码会使其难以阅读和理解。)

如果要在页面上使用一组主要数据,则该页面通常具有一个视图模型。除非每个选项卡都是可在其他地方使用的可重用单元,否则我不需要为每个标签页设置VM。

  

难以阅读和理解

我不同意,这就是用来提供使用逻辑的注释。

我的建议是将它带到一个虚拟机并解决这个问题。

  

两个ViewModel都需要访问项目列表:

您可以在程序的应用程序上放置静态属性,并将VM实例分配给这些静态。然后,每个VM都可以访问其他VM的数据。

在我需要访问虚拟机之前,我已经完成了类似操作,并且使用通用应用作为访问位置。

  

我是MVVM的新手

MVVM只是一个三层数据系统,用于将业务逻辑(VM)与实体(模型)从数据视图中分离出来。不要试图让MVVM成为教条。

答案 3 :(得分:0)

另一种方法是使用消息系统。例如,在PRISM中,您可以使用IEventAggregator在应用程序中发送消息。我想IEventAggregator可以作为独立的dll使用。

它非常强大且易于使用。您定义了一条消息,ViewModel可以添加项目,发送此消息的实例(以item作为参数)。带有列表的ViewModel可以捕获此消息并将该项添加到其列表中。

一个优点是两个ViewModel不必相互了解。

答案 4 :(得分:0)

使用单个父视图模型,该模型引用了您描述的两个子视图模型。我假设在用户正在选择的viewmodel中将视图绑定到ObservableCollection。

在您的父视图模型中,您可以在源视图模型中订阅ObservableCollection上的更改通知事件,然后在第二个视图模型上调用方法以填充更改。

使用ObservableCollection.CollectionChanged事件的示例:https://dotnetcodr.com/2015/05/29/getting-notified-when-collection-changes-with-observablecollection-in-c-net/

另一个选项,取决于您使用的MVVM框架是使用Messenging模式在断开连接的ViewModel之间传递消息 - Use MVVM Light's Messenger to Pass Values Between View Model

答案 5 :(得分:0)

只需使用mvvm库,如mvvm light,prism ...... 每个mvvm库都可以在视图模型之间进行构建,以便您可以使用它。 - 不要写新的。不要重新发明轮子。 - 不要在另一个viewmodel中使用参数viewmodel实例。如果你这样做会让你痛苦