WPF:装饰ViewModel?

时间:2010-07-10 11:56:12

标签: wpf mvvm datatemplate adorner

我有这些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的正确位置的按钮。我只有对给定的ComponentViewModel的引用,而不是它们的可视化表示(在DataTemplate中定义的东西)所以我无法知道在哪里放置3个按钮。

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

提前致谢!

4 个答案:

答案 0 :(得分:5)

您可以使用古怪的建筑黑客来保持您的视图模型优雅和简单,这一点很难理解。视图模型古怪的建筑黑客。

唯一的原因 - 严重的是,唯一的原因 - 视图模型存在的是对视图进行建模。视图是否具有触发命令的按钮?这些命令属于视图模型。

思考,“它真的是记录驱动组件的责任”从表面看起来是明智的,但它实际上表明你正在失去为什么你甚至首先创建了一个视图模型。 Component视图是否有“Move Up”按钮?然后,Component视图模型需要一个“Move Up”命令,您可以将该按钮绑定到该命令。因为这是组件视图模型 for

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

答案 1 :(得分:1)

如果您的目标是在作为子ViewModel的元素的父ViewModel上使用Command,则可以通过在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);

答案 2 :(得分:1)

  

它实际上是应该操作组件位置/删除组件而不是组件本身的记录

就你的模型对象而言,这可能是真的。但是,ViewModel都是关于表示的,而按钮是Component的演示文稿的一部分。所以我认为ComponentViewModel可以接受对其父RecordViewModel的引用,以启用这种情况,即使它不适合于Component对其父Record的引用。

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

现在,对于您发布的示例,Component的视图只是一个TextBox,我不打算将ViewModel分成两部分。但是对于一个更复杂的例子,它可能很有意义。

答案 3 :(得分:0)

专门回答您关于装饰的问题:

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

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