0

我有这些 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 吗?

提前致谢!

4

4 回答 4

5

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

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

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

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

于 2010-07-10T18:11:32.220 回答
1

如果您的目标是让父 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);
于 2010-07-10T12:18:46.807 回答
1

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

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

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

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

于 2010-07-10T12:29:01.190 回答
0

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

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

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

于 2010-07-10T12:32:12.663 回答