如何在视图模型之间访问数据?

3
我正在开发一个项目,为了简化说明,假设TabControl中有两个选项卡...
在一个选项卡中,您可以将文件夹添加到ListBox中。
在另一个选项卡中,有一个ListBox显示您添加的所有文件夹中的所有项目。
每个选项卡都是一个ViewModel(这样可以简化代码,因为将所有代码倾泻到一个ViewModel中会使其难以阅读和理解)。
为了使这个程序正常工作,两个ViewModel都需要访问项目列表:一个需要显示它们,另一个需要添加它们。
我遇到了困难,不知道如何做到这一点。起初,我认为共享数据是不好的,本来不应该出现这种情况,但后来我意识到我想不到其他方法来解决这个问题。
我对MVVM很陌生(这是我第一个真正使用MVVM的应用程序),最初开始使用它,因为我无法在类之间访问数据,认为MVVM会以某种方式解决这个问题,但我又遇到了同样的问题。
如果有人能告诉我如何做到这一点,并用示例代码解释一下,我将不胜感激。我也乐于接受对我的方法的建议和建设性批评。
6个回答

2
首先,你应该明白 MVVM 中的 View 是什么。将其视为一个面板。该面板可以嵌入到窗口、TabControl 或者 ListBox 项中。该面板也可以包含子面板。基本上,如果你的应用程序不只是一个简单的输入表单,那么它很有可能会有多个 View。不要试图把所有东西都放在一个 View/ViewModel 中,因为这样会让事情变得非常复杂。你希望拥有所谓的 Views 层次结构和相应的 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;
    }
}

现在,您可以在不同的视图中显示相同的数据并执行不同的操作。每个视图都会自动更新,因为它引用了同一个模型。
您还可以使用EventAggregator或类似的东西在ViewModel之间进行通信。
尽量避免使用可以从应用程序任何地方访问的静态类/单例模式来存储数据,因为这会破坏封装原则。

1
你可以拥有一个单例对象,并从任何地方获取/设置其属性。
看这个例子;
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)

你可以查看这个链接了解更多关于单例的知识。
你甚至可以将你的ApplicationState单例设置为ObservableObject,并从你的视图中绑定它的属性,例如:
Value="{Binding SharedString, Source={x:Static ApplicationState.Instance}}"

好主意,当使用 DI 容器时,您可以不必自己实现单例模式来实现它。 - Mighty Badaboom

0

只需使用诸如mvvm light、prism等mvvm库... 每个mvvm库都有用于在视图模型之间通信的结构,您可以使用它。 - 不要编写新的。不要重复造轮子。 - 不要在另一个视图模型中使用参数视图模型实例。如果这样做,你会很痛苦。


0
如果把所有的代码都放在一个ViewModel中,那么就会变得难以阅读和理解。
如果页面有一个主要的数据集,那么通常只需要一个视图模型。除非每个选项卡是可重用的单元,需要在其他地方使用,否则我觉得为每个选项卡页面都建立一个VM是不必要的。
我持不同意见,因为这就是注释的用途,它提供了使用逻辑。
我的建议是将其合并为一个VM,来解决这个问题。
如果两个ViewModel都需要访问项目列表:
你可以将静态属性放在程序的应用上,并将VM实例分配给这些静态属性。然后每个VM都可以访问其他VM的数据。
我之前也做过类似的事情,当我需要访问VM时,我使用公共应用作为访问点。
我是MVVM的新手。
MVVM只是一种三层数据系统,将业务逻辑(VM)与实体(Models)与数据视图分离。不要过于追求将MVVM作为教条。

0
另一种方法是使用消息系统。例如,在PRISM中,您可以使用在应用程序内部发送消息。我猜可作为独立的dll使用。
它非常强大且易于使用。您定义一个消息和ViewModel,可以添加项目并发送此消息的实例(带有项目作为参数)。具有列表的ViewModel可以捕获此消息并将项目添加到其列表中。
一个优点是两个ViewModel不必彼此知道。

0

使用一个单一的父视图模型,其中包含对您描述的两个子视图模型的引用。我假设在您的视图模型中,用户正在从您绑定到ObservableCollection的视图中进行选择。

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

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

另一个选项,取决于您使用的MVVM框架,是使用Messenging模式在断开的ViewModel之间传递消息 - 使用MVVM Light的Messenger传递值之间的视图模型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接