【问题标题】:WPF: Adorning a ViewModel?WPF:装饰 ViewModel?
【发布时间】:2011-03-14 05:49:47
【问题描述】:

我有这些 ViewModel:RecordViewModel、ComponentViewModel,其中 RecordViewModel 本质上是多个 ComponentViewModel 的容器。

这些 ViewModel 的显示当前由 DataTemplates 处理,看起来像这样:

<DataTemplate DataType="{x:Type vm:RecordViewModel}" >
    <ItemsControl ItemsSource={Binding Components} />
</DataTemplate>

<DataTemplate DataType="{x:Type vm:ComponentViewModel}" >
    <TextBox Text={Binding Name} />
</DataTemplate>

我现在想提供的是一种更改 ComponentViewModel 显示顺序并从列表中删除某个 ComponentViewModel 的方法。我开始这样做是通过操作 ComponentViewModel 的 DataTemplate 并添加提供这些功能的按钮(单击将触发 ComponentViewModel 上的一个方法,该方法将(通过对 RecordViewModel 的引用“Parent”)调用 RecordViewModel 上的方法来执行操作(如 component.Parent.DeleteComponent(this))。

在我看来,这个问题是真正应该操纵组件位置/删除组件的记录而不是组件本身。

所以我考虑使用附加到 RecordViewModel 并呈现按钮的装饰器来为每个 ComponentViewModel 提供功能(删除、上移、下移)。

然而,问题是这些装饰器需要引用他们装饰的 Control-derivate(这没关系,我只需绑定到 Record-DataTemplate 中的 ItemsControl)但是当我想显示每个 ComponentViewModel 的正确位置的按钮。我只有对给定 ComponentViewModels 的引用,而不是它们的视觉表示(在 DataTemplate 中定义的东西),所以我无法知道在哪里放置 3 个按钮。

有没有办法解决这个问题?或者,对于这些要求,使用 ViewModels/DataTemplates 可能不是一个好主意,因此我应该使用 Control-derivates/ControlTemplates 吗?

提前致谢!

【问题讨论】:

    标签: wpf mvvm datatemplate adorner


    【解决方案1】:

    想出一些古怪的架构技巧来让你的视图模型保持优雅和简单是没有意义的。视图模型古怪的架构技巧。

    视图模型存在的唯一原因——严肃地说,唯一原因——是对视图建模。视图是否有触发命令的按钮?命令属于视图模型。

    认为“移动组件确实是记录的责任”表面上看起来是明智的,但实际上这表明您甚至忘记了最初创建视图模型的原因。组件视图是否有“上移”按钮?然后组件视图模型需要一个“上移”命令,您可以将按钮绑定到该命令。因为这就是组件视图模型的作用

    我之所以强调这一点,是因为这是我本周从 WPF 开发人员那里看到的第三个或第四个问题,他们似乎已经深深陷入 MVVM 模式的兔子洞,以至于他们忘记了为什么会这样存在。

    【讨论】:

      【解决方案2】:

      如果您的目标是让父 ViewModel 上的 Command 作用于子 ViewModel 的元素,您可以通过在 Command 上使用 RelativeSource 绑定并将项目作为命令参数传递来实现:

      <DataTemplate DataType="{x:Type vm:ComponentViewModel}" >
          <Button
              Command="{Binding DataContext.RemoveCommand,
                  RelativeSource={RelativeSource AncestorType=ItemsControl}}"
              CommandParameter="{Binding}"
              Content="{Binding Name}"/>
      </DataTemplate>
      

      RelativeSource 绑定将找到 ItemsControl,因此 DataContext 属性将是您的 RecordViewModel。 CommandParameter 将是单独的 ComponentViewModel,因此您的 ICommand 实现将是:

      DeleteComponent((ComponentViewModel)parameter);
      

      【讨论】:

        【解决方案3】:

        真正应该操纵组件位置/删除组件的记录而不是组件本身

        就您的 model 对象而言,这可能是正确的。然而,ViewModel 都是关于展示的,而按钮是组件展示的一部分。因此,我认为 ComponentViewModel 引用其父 RecordViewModel 以启用此场景是可以接受的,即使 Component 引用其父 Record 是不合适的。

        但是考虑一下,在您的场景中,可能 ComponentViewModel 有太多职责。它属于集合(因为它正在改变集合),它属于集合中的元素 in(因为它在 TextBox 中显示组件的名称)。听起来好像是这种双重责任在困扰你。所以分手吧。使 RecordViewModel 包含 RecordElementViewModels,每个都知道如何从 Record 中删除自己;每个 RecordElementViewModel 都包含一个 ComponentViewModel。在视图方面,听起来您的 UI 将以相同的方式组成:带有删除按钮的外部面板,然后在其中显示组件属性的另一个控件或面板。

        现在,对于您发布的示例,组件的视图只是一个文本框,我不会费心将 ViewModel 分成两部分。但是对于一个更复杂的例子,它可能会很有意义。

        【讨论】:

          【解决方案4】:

          具体回答您关于装饰的问题:

          您正在改变 DataTemplate-d 元素的布局方式,这意味着您不只是在元素顶部分层装饰器,您实际上希望将面板插入到可视化树中将自己的布局强加到 DataTemplate(它成为新面板的子面板)上。我承认我没有使用过装饰器,但这似乎不是它们的用途。

          IMO,最好的方法是让您的 DataTemplate 生成父面板、按钮和所有内容——这导致想要 ComponentViewModel 上的功能,或者可能拆分 ComponentViewModel 的职责(请参阅我的其他答案)。

          【讨论】:

            猜你喜欢
            • 2016-11-24
            • 2013-02-06
            • 2010-11-25
            • 2010-11-24
            • 2015-11-14
            • 2015-03-04
            • 1970-01-01
            • 1970-01-01
            • 2010-10-04
            相关资源
            最近更新 更多