【问题标题】:How can control in the view get specific data from view model?视图中的控件如何从视图模型中获取特定数据?
【发布时间】:2014-07-21 10:10:04
【问题描述】:

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

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

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

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


有一个Navigator 控制绑定到视图模型列表的Items。它可以将每个视图模型转换为INavigatorViewModelBase(所有页面通用)以获得特定的视图模型图像、文本、列和行。所以要么视图模型知道控制(实现INavigator),要么控制知道ViewModelBase..这是一个问题,解决方案都绑定了严格的控制和视图模型,这在mvvm中是不好的。


示意图

【问题讨论】:

  • 好吧,如果我明白了,你有某种容器,里面有很多用户控件。我现在的问题是您需要与那些 UserControl 进行什么样的交互?
  • @TzahMama,这与问题有什么关系?它们将是复杂的用户控件,充满了绑定到相应视图模型属性的不同控件(例如,图形、列表、按钮等)。我的问题是(尽量简单地说):视图模型列表和分别从 each 视图模型获取数据。当然,我可以列出图像,这样我就不必投射任何东西了。但这更加不专业,而不是让单个实体列表为实体的每个重要属性创建一堆列表。现在是 4 个,如果我需要 40 个呢?
  • 就是这样,我无法理解您的问题。我将建议Custom Routed Events 以一种优雅的方式检测用户控件中发生的某事。但我认为您只想从 ViewModel 中获取任意信息。如果是这种情况,我看不到任何其他方法,只是迭代它们以提取您想要的数据。是的,它并不优雅,但我没有看到任何其他方式(界面可能会有所帮助)
  • @TzahMama,那么您可能还想知道在这种情况下该怎么做 =D 点是 - 将控制和 ViewModel 耦合在一起是不好的。随意投票,也许一些 mvvm-sharks 会闻到它。
  • 这听起来对我来说很复杂,但让我试着解释一下:你有一堆Views(未定义的数字),你有相同数量的VM。现在要在它们之间导航,您有基本的PropertiesImageText。您使用哪种Control 来显示这种情况,即您使用什么来显示视图?

标签: c# wpf mvvm


【解决方案1】:

您绘制图表的方式回答了您自己的问题,即您应该如何构造代码。

您需要一个 VM(我们称之为 MainVM),其中包含其他 VM 的 ObservableCollection<VMBase>(使用您的基本类型,以便它们都可以愉快地生活在同一个集合中)。

您的视图需要一个ItemsControl(绑定到您的ObservableCollection<VMBase>),您只需使用VMBase 类型公开的属性为按钮指定DataTemplate。将Button中的Command属性设置为调用SwitchCommandCommandParameter设置为item本身(即{Binding .})。

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

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

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

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

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

很多人忘记了 MVVM 的重点是有目的的将 View 与 ViewModel 分开,这意味着您可以为单个 ViewModel 拥有多个 View,这就是所展示的。

【讨论】:

  • 非常实用(因此很有用)的答案。谢谢。
【解决方案2】:

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

我还发现视图优先的开发方法最容易使用,因为 XAML 中的 UI 开发是静态的(必须如此)。

我认为很多人都沉浸在“使每个组件(M、V、VM)独立且可替换”,包括我自己在内,但我慢慢得出结论:只会适得其反。

从技术上讲,您肯定会变得非常复杂并使用 IoC 容器,创建一些 ViewLocator 对象,将 View-type 绑定到 ViewModel-type,但是......除了更多的混乱之外,这究竟会给您带来什么?老实说,这让开发变得更加困难(因为我曾经这样做过),因为现在您首先失去了设计时支持,除此之外;而您仍然要么绑定到视图中的特定视图模型接口,要么在运行时创建绑定。为什么要复杂化?

This article is a good read,第一个 注意: 明确谈到了 View 与 ViewModel。希望它能帮助您得出自己的结论。

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

这就是为什么会有像Messengers and View Services 这样的东西,这是你在 ViewModel 上的导航服务可以很好地适应的地方。

【讨论】:

  • 是的,我也感觉到使用INavigator 并不是最糟糕的想法。如果它声明在一边(不在控件中,也不在视图模型中),那么感觉是这样的:视图模型实现了视图和控件可以使用的东西可以使用它,可以是替换 任何其他控件,谁知道INavigator format 并且能够处理它。而且这听起来不再像紧耦合了。不过,我确信有更好的方法来处理它。感谢您的文章,它很有帮助。
【解决方案3】:

我认为设计导致了一个没有出路的情况。

我相信创建一个自定义按钮控件,其中dependency properties 绑定图像,行和列实际上为它所在的页面提供了一种方法,可以将这些信息提供给他们;它们是否是动态创建的。

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

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

应该只有一个视图模型,它是控件所在的页面。控件只是绑定到最终驻留在该 VM 上的信息。它如何到达那里,这取决于程序员。如果按钮将包含状态数据,则它会从其依赖属性以两种方式绑定回它所绑定的 item

通过将虚拟机排除在按钮之外,并且只拥有一个虚拟机,这是隔离和维护数据的最佳方式。除非我真的在这里错过了什么......

【讨论】:

  • 那么,您建议提供该视图模型中的控件的信息,它在哪里使用?好的,问题是如何。我已经提到了图像列表。我应该以它理解的方式向导航控制提供特殊信息吗?这并不能消除问题。您不能使用简单的类型,List<Image>List<string> 很容易,但是对于这么多数据呢:图像列表、文本列表、列列表和 每个 的行列表视图模型(因此控制不必直接访问视图模型)......而不是视图模型列表,它以某种方式提供关于它们自己的数据的控制
  • @Sinatr 在我快速回答之前...您有刚才提到的数据示例吗?因为这听起来像是创建一个专门的对象,它包含数据(图像/行/列)的所有三个(例如 DTO 实体,因为缺少更好的术语),然后是绑定到 super 的这些项目的可观察集合 控件,它具有列表的依赖项,并动态管理绑定到上述类的每个单独实例的所有按钮。我相信一个数据示例可能有助于澄清,至少我的困惑并提供更真实的回应。
  • 我明白了,你的意思是 ObservableCollection<INavigator>ObservableCollection<ViewModelBase> 好(其中 ViewModelBase 实现 INavigator)。事实证明,从ViewModelBase 继承的视图模型不关心INaviagor(它们都不关心行或列,而文本和可能的图像可以很有用,它们可以是普通属性,在创建ObservableCollection<INavigator> 时,它们可以简单地复制到INavigator 事情是改变集合需要用INavigator 重新创建一个集合。 . 有什么想法吗?
  • @Sinatr ObservableCollection<INavigator> 具有包含 ViewModel 基础以及图像/行/列作为属性的实际实例对象,可以根据需要表示为其他接口是一种方法是的。至于创建一个测试项目所涉及的成本,将把想法表达为基本示例。利用它来了解它是如何工作的,以及设计将如何影响您当前的项目。至于 MVVM,坦率地说,我没有看到“违反规则”的情况。对我来说,如前所述,MVVM 只是一种将数据与视图分离的方法;你做得很好。
【解决方案4】:

和这里的其他人一样,我发现很难真正理解你在问什么,所以这是很笼统的。问题标题的答案很简单:Control 总是通过绑定从 ViewModel 获取数据。您将 Control 的 DataContext 设置为相应的 ViewModel,然后从那里保持 ViewModel 和 Control 同步:

如果您将包含按钮的ItemsControl 添加到视图,则将ObservableCollection<ButtonViewModel> 添加到视图模型并将ItemsControlItemsSource 绑定到此。

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

确实存在无法在 ViewModel 中专门编码的复杂案例,我发现 Behaviors 是那里缺少的链接,但是当您向我展示具体案例时,我会深入探讨。

如果您想获得一个工作示例,请提供尽可能多的代码,以及您对它应该做什么的确切期望。

【讨论】:

  • ItemsControl 很有用,甚至可能是Selector。现在按钮是在代码中生成的。使用ItemsControl 会使其更像wpf 控制(我无权访问Blend)。也许它甚至可以解决我最初的 ItemTemplate 模板绑定问题(由 XAMIMAX 提到)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多