如何在视图中控制从视图模型中获取特定数据?

时间:2022-01-15 06:59:44

I have multiple of views (user controls), each with its own ViewModel. To navigate between them I am using buttons. Buttons display image and text from corresponding view model and also need column and row (because there are like 10 views: 10 columns with different number of rows each).

我有多个视图(用户控件),每个视图都有自己的ViewModel。要在它们之间导航,我正在使用按钮。按钮显示来自相应视图模型的图像和文本,还需要列和行(因为有10个视图:10列,每行有不同的行数)。

Right now buttons are created dynamically (I made a Navigator control for this) and for view models I have base class to hold text, image, column and row. Number of views available will be different (depends on user level and certain settings), that's why it's I need control here.

现在按钮是动态创建的(我为此创建了一个Navigator控件),对于视图模型,我有基类来保存文本,图像,列和行。可用的视图数量会有所不同(取决于用户级别和某些设置),这就是我需要控制的原因。

Question: how shall my control get data from view models?

问题:我的控件如何从视图模型中获取数据?

Right now I have interface INavigator, defined in (lol) control itself. And view models implement it. I could go opposite, let my control to know about view models. Both looks wrong.

现在我有接口INavigator,在(lol)控件本身中定义。视图模型实现它。我可以走对面,让我的控制来了解视图模型。两者都看错了。


There is a single Navigator control what has, lets say, Items bound to a list of view models. It can cast each view model to INavigator or ViewModelBase (common for all pages) to obtain specific view model image, text, column and row. So either view model knows about control (to implement INavigator) or control knows about ViewModelBase.. And this is a problem, both solution bind tight control and view models, which is bad in mvvm.

有一个Navigator控件,可以说是绑定到视图模型列表的Items。它可以将每个视图模型转换为INavigator或ViewModelBase(所有页面都通用)以获取特定的视图模型图像,文本,列和行。因此,任何一个视图模型都知道控件(实现INavigator)或控件知道ViewModelBase ..这是一个问题,两个解决方案绑定紧密控制和视图模型,这在mvvm中是不好的。


Schematically 如何在视图中控制从视图模型中获取特定数据?

4 个解决方案

#1


3  

The way you've drawn your diagram answers your own question as to how you should structure the code for this.

您绘制图表的方式回答了您自己的问题,即如何为此构建代码。

What you need is one VM (let's call it MainVM) which contains an ObservableCollection<VMBase> of the other VMs (using your base type so that they can all happily live in the same collection).

你需要的是一个虚拟机(我们称之为MainVM),它包含其他虚拟机的ObservableCollection (使用你的基本类型,这样他们都可以幸福地生活在同一个集合中)。

Your View needs an ItemsControl (bound to your ObservableCollection<VMBase>) where you specify a DataTemplate for the Button using the properties exposed by the VMBase type only. Set the Command property in the Button to call SwitchCommand, CommandParameter is set to the item itself (i.e. {Binding .}).

您的View需要一个ItemsControl(绑定到您的ObservableCollection ),您可以使用VMBase类型公开的属性为Button指定DataTemplate。在Button中设置Command属性以调用SwitchCommand,CommandParameter设置为项本身(即{Binding。})。

Your View also needs a ContentControl bound to a SelectedVM property on MainVM which you can populate.

您的View还需要一个ContentControl绑定到您可以填充的MainVM上的SelectedVM属性。

Implement SwitchCommand to set the SelectedVM property based on the value from the CommandParameter.

实现SwitchCommand以根据CommandParameter中的值设置SelectedVM属性。

public void ExecuteSwitchCommand(object parameter)
{
   var vmBase = parameter as VMBase;
   if (vmBase != null)
      SelectedVM = vmBase;
}

All properties mentioned here should be INotifyPropertyChanged enabled so that the View registers when they change and updates the UI.

此处提到的所有属性都应启用INotifyPropertyChanged,以便View在更改和更新UI时进行注册。

To get the different UIs for the ContentControl, add type-specific DataTemplates for each of your specific VM types to the Resources file of your View (or if you're smart and are building a custom plug-in framework, merge the Resource Dictionaries).

要获取ContentControl的不同UI,请将每个特定VM类型的特定于类型的DataTemplates添加到View的Resources文件中(或者如果您很聪明并且正在构建自定义插件框架,请合并资源字典) 。

A lot of people forget with MVVM that the whole point is that there is a purposeful separation of View from ViewModel, thus meaning you can potentially have many Views for a single ViewModel, which is what this demonstrates.

很多人忘了MVVM,重点是View与ViewModel有目的地分离,这意味着你可以为一个ViewModel提供很多视图,这就是这个。

#2


2  

I find it's easiest to think of MVVM as a top-down approach... View knows about it's ViewModel, ViewModel knows about it's Model, but Model does not know about it's ViewModel and ViewModel does not know about it's View.

我发现将MVVM视为一种自上而下的方法是最简单的... View知道它的ViewModel,ViewModel知道它的模型,但Model不知道它的ViewModel和ViewModel不知道它的View。

I also find a View-first approach to development the easiest to work with, as UI development in XAML is static (has to be).

我还发现了一种最容易使用的View-first开发方法,因为XAML中的UI开发是静态的(必须是)。

I think a lot of people get to wrapped up in 'making every component (M, V, VM) standalone and replaceable', myself included, but I've slowly come to the conclusion that is just counter-productive.

我认为很多人都会把'自己的每个组件(M,V,VM)独立和可替换'包括在内,包括我自己,但我慢慢得出结论,这只会适得其反。

Technically, sure you could get very complicated and using IoC containers, create some ViewLocator object which binds a View-type to a ViewModel-type, but... what exactly does that gain you besides more confusion? It makes it honestly harder (because I've done this at one point) to develop because now you've lost design-time support first and foremost, among other things; and you're still either binding to a specific view model interface in your view or creating the binding at run-time. Why complicate it?

从技术上讲,确定你可以变得非常复杂并使用IoC容器,创建一些将View类型绑定到ViewModel类型的ViewLocator对象,但是......除了更多的混淆之外,究竟是什么让你获益?这使得它真的变得更难(因为我已经在某一点上完成了这一点),因为现在你已经失去了设计时支持,其中包括:并且您仍然要么绑定到视图中的特定视图模型接口,要么在运行时创建绑定。为什么复杂化呢?

This article is a good read, and the first Note: explicitly talks about View vs. ViewModel. Hopefully it will help you draw your own conclusions.

本文是一篇很好的阅读,第一篇注释:明确地讨论了View与ViewModel。希望它能帮助您得出自己的结论。

To directly answer your question, I think having your ViewModels implement an INavigator interface of some sort is probably ideal. Remember your VM is 'glue' between your view and model/business logic, it's job is to transform business data into data that is consumable by your views, so it exists somewhere between both your UI and business layers.

要直接回答您的问题,我认为让您的ViewModel实现某种类型的INavigator接口可能是理想的。请记住,您的VM是视图和模型/业务逻辑之间的“粘合剂”,它的工作是将业务数据转换为您的视图可以使用的数据,因此它存在于UI和业务层之间。

This is why there are things like Messengers and View Services, which is where your navigator service on the ViewModels can fit in nicely.

这就是为什么有像Messenger和View Services这样的东西,这是ViewModel上的导航器服务可以很好地适应的原因。

#3


1  

I think the design has led to a no way out situation.

我认为这种设计导致了一种无路可走的局面。

I believe that creating a custom button control where the dependency properties tie the image, the row and column actually provide a way for the page, which it resides on ,to get that information to them; whether they are dynamically created or not.

我相信创建一个自定义按钮控件,其中依赖属性绑定图像,行和列实际上为它所驻留的页面提供了一种方式,以获取它们的信息;它们是否是动态创建的。

Continuing on with that thought. There is no MVVM logic applied to a custom control, the control contains what it needs to do its job and that is through the dependency properties as mentioned. Any functionality of the button should be done by commanding; all this makes the button data driven and robust enough to use in a MVVM methodology or not.

继续这个想法。没有MVVM逻辑应用于自定义控件,该控件包含完成其工作所需的内容,即通过所述的依赖项属性。按钮的任何功能都应该通过命令来完成;所有这些使按钮数据驱动和足够强大,可以在MVVM方法中使用。

Question: how shall my control get data from view models?

问题:我的控件如何从视图模型中获取数据?

There should only one viewmodel which is the page the control resides on. The control is simply bound to information which ultimately resides on that VM. How it gets there, that is up to the programmer. If the button is going to contain state data, that is bound from its dependency property in a two way fashion back to the item it is bound to.

应该只有一个viewmodel是控件所在的页面。控件简单地绑定到最终驻留在该VM上的信息。如何到达那里,这取决于程序员。如果该按钮将包含状态数据,则该状态数据以其双向方式从其依赖属性绑定到它所绑定的项目。

By keeping VMs out of the buttons and only having one VM that is the best way to segregate and maintain the data. Unless I am really missing something here....

通过将VM保持在按钮之外,并且只有一个VM是分离和维护数据的最佳方式。除非我真的在这里遗漏了什么....

#4


1  

Same as others here I find it a bit hard to actually understand what you are asking, so this is quite general. The answer to the question header is simply: the Control gets the data from the ViewModel through bindings, always. You set the DataContext of your Control to the corresponding ViewModel, and from there you keep the ViewModel and the Control synchronized:

和其他人一样,我觉得实际上要理解你的要求有点难,所以这很普遍。问题标题的答案很简单:Control始终通过绑定从ViewModel获取数据。您将Control的DataContext设置为相应的ViewModel,然后从那里保持ViewModel和Control同步:

If you add an ItemsControl containing buttons to the View, you add an ObservableCollection<ButtonViewModel> to the ViewModel and bind the ItemsSource of the ItemsControl to this.

如果向View添加包含按钮的ItemsControl,则将ObservableCollection 添加到ViewModel,并将ItemsControl的ItemsSource绑定到此。

If you allow the user to dynamically add content to the View, the actual code that does it resides in the ViewModel, e.g. when the user clicks on a button "Add Button", you use the Command property to call a ViewModel method that adds a ButtonViewModel to the collection and the View will automatically reflect your changes.

如果您允许用户向View动态添加内容,则执行该操作的实际代码将驻留在ViewModel中,例如当用户单击“添加按钮”按钮时,使用Command属性调用ViewModel方法,该方法将ButtonViewModel添加到集合中,View将自动反映您的更改。

There do exist complicated cases that are impossible to code exclusively in the ViewModel, I have found Behaviors to be the missing link there, but I'll get into that when you show me the specific case.

确实存在不可能在ViewModel中专门编码的复杂情况,我发现行为是那里缺少的链接,但是当你向我展示具体情况时我会深入研究。

If you'd like to get a working example, please provide as much code as you can, with your exact expectations of what it should do.

如果您想获得一个有效的示例,请提供尽可能多的代码,并准确了解它应该做什么。

#1


3  

The way you've drawn your diagram answers your own question as to how you should structure the code for this.

您绘制图表的方式回答了您自己的问题,即如何为此构建代码。

What you need is one VM (let's call it MainVM) which contains an ObservableCollection<VMBase> of the other VMs (using your base type so that they can all happily live in the same collection).

你需要的是一个虚拟机(我们称之为MainVM),它包含其他虚拟机的ObservableCollection (使用你的基本类型,这样他们都可以幸福地生活在同一个集合中)。

Your View needs an ItemsControl (bound to your ObservableCollection<VMBase>) where you specify a DataTemplate for the Button using the properties exposed by the VMBase type only. Set the Command property in the Button to call SwitchCommand, CommandParameter is set to the item itself (i.e. {Binding .}).

您的View需要一个ItemsControl(绑定到您的ObservableCollection ),您可以使用VMBase类型公开的属性为Button指定DataTemplate。在Button中设置Command属性以调用SwitchCommand,CommandParameter设置为项本身(即{Binding。})。

Your View also needs a ContentControl bound to a SelectedVM property on MainVM which you can populate.

您的View还需要一个ContentControl绑定到您可以填充的MainVM上的SelectedVM属性。

Implement SwitchCommand to set the SelectedVM property based on the value from the CommandParameter.

实现SwitchCommand以根据CommandParameter中的值设置SelectedVM属性。

public void ExecuteSwitchCommand(object parameter)
{
   var vmBase = parameter as VMBase;
   if (vmBase != null)
      SelectedVM = vmBase;
}

All properties mentioned here should be INotifyPropertyChanged enabled so that the View registers when they change and updates the UI.

此处提到的所有属性都应启用INotifyPropertyChanged,以便View在更改和更新UI时进行注册。

To get the different UIs for the ContentControl, add type-specific DataTemplates for each of your specific VM types to the Resources file of your View (or if you're smart and are building a custom plug-in framework, merge the Resource Dictionaries).

要获取ContentControl的不同UI,请将每个特定VM类型的特定于类型的DataTemplates添加到View的Resources文件中(或者如果您很聪明并且正在构建自定义插件框架,请合并资源字典) 。

A lot of people forget with MVVM that the whole point is that there is a purposeful separation of View from ViewModel, thus meaning you can potentially have many Views for a single ViewModel, which is what this demonstrates.

很多人忘了MVVM,重点是View与ViewModel有目的地分离,这意味着你可以为一个ViewModel提供很多视图,这就是这个。

#2


2  

I find it's easiest to think of MVVM as a top-down approach... View knows about it's ViewModel, ViewModel knows about it's Model, but Model does not know about it's ViewModel and ViewModel does not know about it's View.

我发现将MVVM视为一种自上而下的方法是最简单的... View知道它的ViewModel,ViewModel知道它的模型,但Model不知道它的ViewModel和ViewModel不知道它的View。

I also find a View-first approach to development the easiest to work with, as UI development in XAML is static (has to be).

我还发现了一种最容易使用的View-first开发方法,因为XAML中的UI开发是静态的(必须是)。

I think a lot of people get to wrapped up in 'making every component (M, V, VM) standalone and replaceable', myself included, but I've slowly come to the conclusion that is just counter-productive.

我认为很多人都会把'自己的每个组件(M,V,VM)独立和可替换'包括在内,包括我自己,但我慢慢得出结论,这只会适得其反。

Technically, sure you could get very complicated and using IoC containers, create some ViewLocator object which binds a View-type to a ViewModel-type, but... what exactly does that gain you besides more confusion? It makes it honestly harder (because I've done this at one point) to develop because now you've lost design-time support first and foremost, among other things; and you're still either binding to a specific view model interface in your view or creating the binding at run-time. Why complicate it?

从技术上讲,确定你可以变得非常复杂并使用IoC容器,创建一些将View类型绑定到ViewModel类型的ViewLocator对象,但是......除了更多的混淆之外,究竟是什么让你获益?这使得它真的变得更难(因为我已经在某一点上完成了这一点),因为现在你已经失去了设计时支持,其中包括:并且您仍然要么绑定到视图中的特定视图模型接口,要么在运行时创建绑定。为什么复杂化呢?

This article is a good read, and the first Note: explicitly talks about View vs. ViewModel. Hopefully it will help you draw your own conclusions.

本文是一篇很好的阅读,第一篇注释:明确地讨论了View与ViewModel。希望它能帮助您得出自己的结论。

To directly answer your question, I think having your ViewModels implement an INavigator interface of some sort is probably ideal. Remember your VM is 'glue' between your view and model/business logic, it's job is to transform business data into data that is consumable by your views, so it exists somewhere between both your UI and business layers.

要直接回答您的问题,我认为让您的ViewModel实现某种类型的INavigator接口可能是理想的。请记住,您的VM是视图和模型/业务逻辑之间的“粘合剂”,它的工作是将业务数据转换为您的视图可以使用的数据,因此它存在于UI和业务层之间。

This is why there are things like Messengers and View Services, which is where your navigator service on the ViewModels can fit in nicely.

这就是为什么有像Messenger和View Services这样的东西,这是ViewModel上的导航器服务可以很好地适应的原因。

#3


1  

I think the design has led to a no way out situation.

我认为这种设计导致了一种无路可走的局面。

I believe that creating a custom button control where the dependency properties tie the image, the row and column actually provide a way for the page, which it resides on ,to get that information to them; whether they are dynamically created or not.

我相信创建一个自定义按钮控件,其中依赖属性绑定图像,行和列实际上为它所驻留的页面提供了一种方式,以获取它们的信息;它们是否是动态创建的。

Continuing on with that thought. There is no MVVM logic applied to a custom control, the control contains what it needs to do its job and that is through the dependency properties as mentioned. Any functionality of the button should be done by commanding; all this makes the button data driven and robust enough to use in a MVVM methodology or not.

继续这个想法。没有MVVM逻辑应用于自定义控件,该控件包含完成其工作所需的内容,即通过所述的依赖项属性。按钮的任何功能都应该通过命令来完成;所有这些使按钮数据驱动和足够强大,可以在MVVM方法中使用。

Question: how shall my control get data from view models?

问题:我的控件如何从视图模型中获取数据?

There should only one viewmodel which is the page the control resides on. The control is simply bound to information which ultimately resides on that VM. How it gets there, that is up to the programmer. If the button is going to contain state data, that is bound from its dependency property in a two way fashion back to the item it is bound to.

应该只有一个viewmodel是控件所在的页面。控件简单地绑定到最终驻留在该VM上的信息。如何到达那里,这取决于程序员。如果该按钮将包含状态数据,则该状态数据以其双向方式从其依赖属性绑定到它所绑定的项目。

By keeping VMs out of the buttons and only having one VM that is the best way to segregate and maintain the data. Unless I am really missing something here....

通过将VM保持在按钮之外,并且只有一个VM是分离和维护数据的最佳方式。除非我真的在这里遗漏了什么....

#4


1  

Same as others here I find it a bit hard to actually understand what you are asking, so this is quite general. The answer to the question header is simply: the Control gets the data from the ViewModel through bindings, always. You set the DataContext of your Control to the corresponding ViewModel, and from there you keep the ViewModel and the Control synchronized:

和其他人一样,我觉得实际上要理解你的要求有点难,所以这很普遍。问题标题的答案很简单:Control始终通过绑定从ViewModel获取数据。您将Control的DataContext设置为相应的ViewModel,然后从那里保持ViewModel和Control同步:

If you add an ItemsControl containing buttons to the View, you add an ObservableCollection<ButtonViewModel> to the ViewModel and bind the ItemsSource of the ItemsControl to this.

如果向View添加包含按钮的ItemsControl,则将ObservableCollection 添加到ViewModel,并将ItemsControl的ItemsSource绑定到此。

If you allow the user to dynamically add content to the View, the actual code that does it resides in the ViewModel, e.g. when the user clicks on a button "Add Button", you use the Command property to call a ViewModel method that adds a ButtonViewModel to the collection and the View will automatically reflect your changes.

如果您允许用户向View动态添加内容,则执行该操作的实际代码将驻留在ViewModel中,例如当用户单击“添加按钮”按钮时,使用Command属性调用ViewModel方法,该方法将ButtonViewModel添加到集合中,View将自动反映您的更改。

There do exist complicated cases that are impossible to code exclusively in the ViewModel, I have found Behaviors to be the missing link there, but I'll get into that when you show me the specific case.

确实存在不可能在ViewModel中专门编码的复杂情况,我发现行为是那里缺少的链接,但是当你向我展示具体情况时我会深入研究。

If you'd like to get a working example, please provide as much code as you can, with your exact expectations of what it should do.

如果您想获得一个有效的示例,请提供尽可能多的代码,并准确了解它应该做什么。