如何在 ViewModel 之间访问数据?
How to Access Data between ViewModels?
我目前正在做一个项目,为了简化解释,假设在一个 TabControl 中有两个选项卡...
在一个选项卡中,您将文件夹添加到列表框。
在另一个选项卡中,有一个列表框显示您添加的所有文件夹中的所有项目。
每个选项卡都是一个 ViewModel(这是为了简化代码,因为将所有代码都集中在一个 ViewModel 中会让人难以阅读和理解)。
为了让这个程序工作,两个 ViewModel 都需要访问项目列表:一个因为它必须显示它们,另一个因为它必须添加到它们。
我不知道该怎么做。我最初认为共享数据是不好的,一开始就不应该发生这种情况,但后来我意识到我想不出任何其他方法来做到这一点。
我是 MVVM 的新手(这是我使用 MVVM 的第一个真正的应用程序)并且最初开始使用它,因为我无法在 类 之间访问数据,并且认为 MVVM 会以某种方式解决这个问题,但是我又来了
如果有人能告诉我如何做到这一点,并可能用示例代码进行解释,我将不胜感激。我也乐于接受对我的方法的建议和建设性批评。
您可以从任何地方拥有一个单例对象和get/set它的属性。
看看这个例子;
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}}"
as dumping all the code in one ViewModel makes it hard to read and understand).
如果一个页面上要使用一组主要数据,则该页面通常有一个视图模型。除非每个选项卡都是可以在其他地方使用的可重复使用的单元,否则对我来说似乎没有必要为每个选项卡页设置一个虚拟机。
hard to read and understand
我不敢苟同,评论就是用来提供使用逻辑的。
我的建议是将它带到一个虚拟机上并解决这个问题。
both ViewModels need to access the list of items:
您可以将静态属性放在程序的应用程序上,并将 VM 实例分配给这些静态属性。然后每个 VM 都可以访问其他 VM 的数据。
我之前在需要访问 VM 的地方做过类似的事情,我使用通用应用程序作为访问位置。
I am new to MVVM
MVVM 只是一个 3 层数据系统,用于从数据视图中将业务逻辑(VM)与实体(模型)分开。不要执着于让 MVVM 成为教条。
另一种方法是使用消息系统。例如,在 PRISM 中,您可以使用 IEventAggregator
在您的应用程序中发送消息。我想 IEventAggregator
可以作为独立的 dll 使用。
它非常强大且易于使用。您定义一条消息,可以添加项目的 ViewModel 发送此消息的实例(以项目作为参数)。带有列表的 ViewModel 可以捕获此消息并将项目添加到它的列表中。
一个优点是两个 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
首先你应该明白MVVM中的View是什么。把它看成一个面板。可以嵌入 Window、TabControl 甚至列表框项中的面板。还可以包含子面板的面板。基本上,如果您的应用程序不仅仅是一个简单的输入表单,那么它很可能会有多个 View。不要试图将所有内容都放在一个 View/ViewModel 中,因为这会使以后的事情变得非常复杂。您想要所谓的 视图层次结构 及其对应的 ViewModels。会有很多 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).
这是正确的做法。下面是一些伪代码,描述了您的示例应用程序方案应该是什么样子:
// 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;
}
}
现在您可以在不同的视图中显示相同的数据并对其执行不同的操作。每个 View 都会自动更新,因为它引用相同的 Model.
您也可以使用 EventAggregator 或类似的东西在 ViewModels.
之间进行通信
尽量避免使用静态 classes/Singletons 可以从应用程序中的任何位置访问的数据,因为这会破坏 封装 原则。
只需使用 mvvm 库,例如 mvvm light、prism ...
每个 mvvm 库都有用于在您可以使用的视图模型之间进行通信的结构。
- 不要写新的。不要重新发明轮子。
- 不要在另一个视图模型中使用参数视图模型实例。如果你这样做,你会很痛苦
我目前正在做一个项目,为了简化解释,假设在一个 TabControl 中有两个选项卡...
在一个选项卡中,您将文件夹添加到列表框。
在另一个选项卡中,有一个列表框显示您添加的所有文件夹中的所有项目。
每个选项卡都是一个 ViewModel(这是为了简化代码,因为将所有代码都集中在一个 ViewModel 中会让人难以阅读和理解)。
为了让这个程序工作,两个 ViewModel 都需要访问项目列表:一个因为它必须显示它们,另一个因为它必须添加到它们。
我不知道该怎么做。我最初认为共享数据是不好的,一开始就不应该发生这种情况,但后来我意识到我想不出任何其他方法来做到这一点。
我是 MVVM 的新手(这是我使用 MVVM 的第一个真正的应用程序)并且最初开始使用它,因为我无法在 类 之间访问数据,并且认为 MVVM 会以某种方式解决这个问题,但是我又来了
如果有人能告诉我如何做到这一点,并可能用示例代码进行解释,我将不胜感激。我也乐于接受对我的方法的建议和建设性批评。
您可以从任何地方拥有一个单例对象和get/set它的属性。
看看这个例子;
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}}"
as dumping all the code in one ViewModel makes it hard to read and understand).
如果一个页面上要使用一组主要数据,则该页面通常有一个视图模型。除非每个选项卡都是可以在其他地方使用的可重复使用的单元,否则对我来说似乎没有必要为每个选项卡页设置一个虚拟机。
hard to read and understand
我不敢苟同,评论就是用来提供使用逻辑的。
我的建议是将它带到一个虚拟机上并解决这个问题。
both ViewModels need to access the list of items:
您可以将静态属性放在程序的应用程序上,并将 VM 实例分配给这些静态属性。然后每个 VM 都可以访问其他 VM 的数据。
我之前在需要访问 VM 的地方做过类似的事情,我使用通用应用程序作为访问位置。
I am new to MVVM
MVVM 只是一个 3 层数据系统,用于从数据视图中将业务逻辑(VM)与实体(模型)分开。不要执着于让 MVVM 成为教条。
另一种方法是使用消息系统。例如,在 PRISM 中,您可以使用 IEventAggregator
在您的应用程序中发送消息。我想 IEventAggregator
可以作为独立的 dll 使用。
它非常强大且易于使用。您定义一条消息,可以添加项目的 ViewModel 发送此消息的实例(以项目作为参数)。带有列表的 ViewModel 可以捕获此消息并将项目添加到它的列表中。
一个优点是两个 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
首先你应该明白MVVM中的View是什么。把它看成一个面板。可以嵌入 Window、TabControl 甚至列表框项中的面板。还可以包含子面板的面板。基本上,如果您的应用程序不仅仅是一个简单的输入表单,那么它很可能会有多个 View。不要试图将所有内容都放在一个 View/ViewModel 中,因为这会使以后的事情变得非常复杂。您想要所谓的 视图层次结构 及其对应的 ViewModels。会有很多 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).
这是正确的做法。下面是一些伪代码,描述了您的示例应用程序方案应该是什么样子:
// 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;
}
}
现在您可以在不同的视图中显示相同的数据并对其执行不同的操作。每个 View 都会自动更新,因为它引用相同的 Model.
您也可以使用 EventAggregator 或类似的东西在 ViewModels.
之间进行通信尽量避免使用静态 classes/Singletons 可以从应用程序中的任何位置访问的数据,因为这会破坏 封装 原则。
只需使用 mvvm 库,例如 mvvm light、prism ... 每个 mvvm 库都有用于在您可以使用的视图模型之间进行通信的结构。 - 不要写新的。不要重新发明轮子。 - 不要在另一个视图模型中使用参数视图模型实例。如果你这样做,你会很痛苦