简体   繁体   English

如何在ViewModels之间访问数据?

[英]How to Access Data between ViewModels?

I'm currently working on a project, and for the sake of simplifying explanations, let's say that there are two tabs in a TabControl... 我目前正在开发一个项目,为了简化解释,让我们说TabControl中有两个选项卡...

In one tab, you add folders to a ListBox. 在一个选项卡中,您将文件夹添加到ListBox。

In the other tab, there is a ListBox that displays all of the items in all of the folders you have added. 在另一个选项卡中,有一个ListBox,显示您添加的所有文件夹中的所有项目。

Each tab is a ViewModel (this is to simplify code, as dumping all the code in one ViewModel makes it hard to read and understand). 每个选项卡都是一个ViewModel(这是为了简化代码,因为在一个ViewModel中转储所有代码使得难以阅读和理解)。

In order for this program to work, both ViewModels need to access the list of items: one because it has to display them, and the other because it has to add to them. 为了使这个程序工作,两个ViewModel都需要访问项目列表:一个是因为它必须显示它们,另一个因为它必须添加它们。

I am having trouble figuring how to do this. 我无法弄清楚如何做到这一点。 I initially thought that sharing data is bad and this shouldn't have occurred in the first place, but then I realised that I can't think of any other way to do this. 我最初认为共享数据很糟糕,这不应该首先发生,但后来我意识到我无法想到任何其他方式来做到这一点。

I am new to MVVM (this is my first real application using MVVM) and initally started using it, as I couldn't access data between classes, and thought MVVM would somehow sort this issue, but here I am again. 我是MVVM的新手(这是我使用MVVM的第一个真正的应用程序)并且最初开始使用它,因为我无法在类之间访问数据,并且认为MVVM会以某种方式对此问题进行排序,但在这里我再次。

I would appreciate it if someone can tell me how I could do this, and potentially explain it with example code. 如果有人能告诉我如何做到这一点,我会很感激,并可能通过示例代码解释它。 I am also open to suggestions and constructive criticism to my methods. 我也对我的方法提出建议和建设性的批评。

First of all you should understand what a View in MVVM is. 首先,您应该了解MVVM中的View是什么。 Look of it as a panel. 把它看作一个小组。 A panel that could be embedded in a Window, TabControl or even in a ListBox item. 可以嵌入Window,TabControl甚至是ListBox项目的面板。 A panel that also could contain child panels. 一个也可以包含子面板的面板。 Basically, if your application isn't just a simple input form there is a great probability it would have more than one View . 基本上,如果您的应用程序不仅仅是一个简单的输入表单,那么它很可能会有多个View Don't try to put everything in one View / ViewModel because it would complicate things tremendously later on. 不要试图将所有内容都放在一个View / ViewModel中,因为它会在以后会使事情变得复杂。 You want to have so called hierarchy of Views and their corresponding ViewModels . 您希望拥有所谓的视图层次结构及其相应的ViewModel There will be a lot of Views / ViewModels but they will be relatively simple and easy to maintain (here is small example of switching between views using PRISM but you can get the main idea https://youtu.be/ZfBy2nfykqY?t=45m34s ). 将会有很多Views / ViewModels,但它们相对简单易维护(这里是使用PRISM在视图之间切换的小例子,但你可以得到主要的想法https://youtu.be/ZfBy2nfykqY?t=45m34s )。

Each tab is a ViewModel (this is to simplify code, as dumping all the code in one ViewModel makes it hard to read and understand). 每个选项卡都是一个ViewModel(这是为了简化代码,因为在一个ViewModel中转储所有代码使得难以阅读和理解)。

This is the right approach. 这是正确的方法。 Here is some pseudo code which describes how your example application scheme should look like: 下面是一些伪代码,它描述了您的示例应用程序方案应如何:

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

Now you can display the same data in different Views and perform different operations on it. 现在,您可以在不同的视图中显示相同的数据,并对其执行不同的操作。 Every View will automatically be updated because it references the same Model . 每个视图都会自动更新,因为它引用了相同的模型

You may also use EventAggregator or something similar to communicate between ViewModels . 您也可以使用EventAggregator或类似的东西在ViewModel之间进行通信。

Try to avoid static classes/Singletons with the data that could be accessed from anywhere in your application because this breaks the Encapsulation principle. 尝试避免使用可以从应用程序中的任何位置访问的数据的静态类/单例,因为这会破坏Encapsulation原则。

You can have a singleton object and get/set its properties from anywhere. 您可以拥有单个对象并从任何位置获取/设置其属性。

Look at this example; 看看这个例子;

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

now you can set this SharedString property from anywhere like; 现在你可以从任何地方设置这个SharedString属性;

ApplicationState.Instance.SharedString = "hello from VM1"

and read it from another view model like; 并从另一个视图模型中读取它;

Debug.WriteLine(ApplicationState.Instance.SharedString)

You can take a look at this link to learn more about singletons 您可以查看此链接以了解有关单身人士的更多信息

you can even make your ApplicationState singleton an ObservableObject and bind to its properties from your views like; 你甚至可以使你的ApplicationState单例成为ObservableObject并从你的视图绑定到它的属性,如;

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

as dumping all the code in one ViewModel makes it hard to read and understand). 因为在一个ViewModel中转储所有代码使得难以阅读和理解)。

If one has a main set of data to be used on a page, the page generally has one view model. 如果要在页面上使用一组主要数据,则该页面通常具有一个视图模型。 Unless each tab is a re-usable unit to be used elsewhere, having a VM for each tab page seems unnecessary to me. 除非每个选项卡都是可在其他地方使用的可重用单元,否则我不需要为每个标签页设置VM。

hard to read and understand 难以阅读和理解

I beg to differ, that is what comments are used for to provide a logic on usage. 我不同意,这是用于提供使用逻辑的注释。

My advice is to bring it to one VM and solve this problem. 我的建议是将它带到一个VM并解决这个问题。

both ViewModels need to access the list of items: 两个ViewModels都需要访问项目列表:

You can put static properties on the app of the program and have the VM instances assigned to those statics. 您可以在程序的应用程序上放置静态属性,并将VM实例分配给这些静态。 Then each VM can access the other VM's data. 然后,每个VM都可以访问其他VM的数据。

I have done similar before where I need to access a VM and I use the common app as an access place. 在我需要访问VM之前,我已经完成了类似操作,并且使用通用应用程序作为访问位置。

I am new to MVVM 我是MVVM的新手

MVVM is just a 3-tiered data system to separate business logic (the VM) from the entities (Models) from the view of data. MVVM只是一个三层数据系统,用于将业务逻辑(VM)与实体(模型)从数据视图中分离出来。 Don't get hung up on trying to make MVVM a dogma. 不要试图让MVVM成为教条。

Another approach would be using a message system. 另一种方法是使用消息系统。 In PRISM for example you have the IEventAggregator to send messages inside your application. 例如,在PRISM中,您可以使用IEventAggregator在应用程序中发送消息。 I guess the IEventAggregator is available as stand alone dll. 我想IEventAggregator可以作为独立的dll使用。

It's really powerfull and easy to use. 它非常强大且易于使用。 You define a message and the ViewModel which can add items send an instance of this message (with the item as argument). 您定义了一条消息,ViewModel可以添加项目,发送此消息的实例(以item作为参数)。 The ViewModel with the list can catch this message and adds the item to it's list. 带有列表的ViewModel可以捕获此消息并将该项添加到其列表中。

One advantage is that both ViewModels don't have to know each other. 一个优点是两个ViewModel不必彼此了解。

Use a single parent viewmodel which has references to both the child viewmodels you describe. 使用单个父视图模型,该视图模型引用了您描述的子视图模型。 I assume that in your viewmodel that the user is selecting from you are binding the view to an ObservableCollection. 我假设在用户正在选择的viewmodel中将视图绑定到ObservableCollection。

In you parent viewmodel you can subscribe to the Change notification events on the ObservableCollection in the source viewmodel and then call a method on the second viewmodel to populate the changes. 在您的父视图模型中,您可以在源视图模型中订阅ObservableCollection上的更改通知事件,然后在第二个视图模型上调用方法以填充更改。

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

One other option, depending on which MVVM framework you are using is to use the Messenging pattern to pass messages between disconnected ViewModels - Use MVVM Light's Messenger to Pass Values Between View Model 另一个选项,取决于您使用的MVVM框架是使用Messenging模式在断开连接的ViewModel之间传递消息 - 使用MVVM Light的Messenger在视图模型之间传递值

Just use a mvvm library such as mvvm light, prism ... every mvvm library has structer for comminicate between viewmodels that you can use it. 只需使用mvvm库,例如mvvm light,prism ......每个mvvm库都可以构建用于在视图模型之间进行操作的程序。 - Dont write new one. - 不要写新的。 Do not reinvent the wheel. 不要重新发明轮子。 - Dont take parameter viewmodel instance in another viewmodel. - 不要在另一个viewmodel中使用参数viewmodel实例。 if you do that it will be pain in you 如果你这样做,那将是痛苦的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM