我有这些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吗?
提前致谢!
答案 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的职责(请参阅我的其他答案)。