views:

40

answers:

4

I have these ViewModels: RecordViewModel, ComponentViewModel where RecordViewModel essentially is a container for several ComponentViewModels.

The display of these ViewModels is currently handled by DataTemplates that look something like this:

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

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

What I wanted to provide now is a way to change the order the ComponentViewModels are displayed and to remove a certain ComponentViewModel from the list. I started out doing that by manipulating the DataTemplate of the ComponentViewModel and adding buttons that provided these functions (the click would then trigger a method on the ComponentViewModel that would (through a reference "Parent" to the RecordViewModel) call a method on the RecordViewModel to perform the operation (like component.Parent.DeleteComponent(this)).

The problem with this in my oppinion is that it is really the Record that should manipulate the Components position/remove a Component and not the Component itself.

So I thought about using an adorner that attaches to the RecordViewModel and renders the buttons to provide the functionality (remove, move up, move down) for each of the ComponentViewModels.

The problem however is that these adorners need to take a reference to a Control-derivate which they adorn (which was ok I would just bind to the ItemsControl in the Record-DataTemplate) however the problem appears when I want to show the buttons in the right position for each ComponentViewModel. I only have a reference to the given ComponentViewModels and not to their visual representation (the thing defined in the DataTemplate) so I have no way of knowing where to place the 3 buttons.

Is there a way to work around this? Or is it possible that for these requirements using ViewModels/DataTemplates is just not a good idea and should I therefore use Control-derivates/ControlTemplates?

Thanks in advance!

A: 

If your goal is to have a Command on the parent ViewModel that acts on an element of the child ViewModel, you can do this by using a RelativeSource binding on Command and passing the item as Command Parameter:

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

The RelativeSource binding will find the ItemsControl, so the DataContext property will be your RecordViewModel. The CommandParameter will be the individual ComponentViewModel, so your ICommand implementation would be:

DeleteComponent((ComponentViewModel)parameter);
Quartermeister
+1  A: 

it is really the Record that should manipulate the Components position/remove a Component and not the Component itself

As far as your model objects go, that's probably true. However, the ViewModels are all about presentation, and the buttons are kind of part of a Component's presentation. So I think it could be acceptable for the ComponentViewModel to have a reference to its parent RecordViewModel, to enable this scenario, even if it wouldn't be appropriate for the Component to have a reference to its parent Record.

But consider that, in your scenario, maybe the ComponentViewModel has too many responsibilities. It belongs to the collection (because it's mutating the collection), and it belongs to the element in the collection (because it's showing the Component's name in a TextBox). It sounds like it's this dual responsibility that's bothering you. So break it up. Make RecordViewModel contain RecordElementViewModels, each of which knows how to remove itself from the Record; and each RecordElementViewModel contains a ComponentViewModel. On the view side, it sounds like your UI would be composed the same way: an outer panel with a Delete button, and then another control or panel inside that, presenting the Component's properties.

Now, for the example you posted, where Component's view is just a TextBox, I wouldn't bother splitting the ViewModel into two parts. But for a more complex example, it might make a lot of sense.

Joe White
A: 

To specifically answer your question about adorning:

You're getting into changing the way a DataTemplate-d element is laid out, which means you're not just layering an adorner on top of the element, you're actually wanting to insert a panel into the visual tree that imposes its own layout onto the DataTemplate (which becomes a child of the new panel). I'll admit that I haven't used adorners, but that doesn't seem to be what they're for.

The best way to do this, IMO, is to have your DataTemplate generate the parent panel, buttons and all -- which leads back to wanting the functionality on the ComponentViewModel, or perhaps splitting ComponentViewModel's responsibilities (see my other answer).

Joe White
+1  A: 

Coming up with wacky architectural hacks that you can employ to keep your view model elegant and simple is missing the point. The view model is the wacky architectural hack.

The only reason - seriously, the only reason - that the view model exists is to model the view. Does the view have buttons that trigger commands? The commands belong in the view model.

Thinking, "it's really the Record's responsibility to move Components" seems sensible on its face, but it's actually an indication that you're losing track of why you even created a view model in the first place. Does the Component view have a "Move Up" button? Then the Component view model needs a "Move Up" command that you can bind the button to. Because that's what the Component view model is for.

I'm being emphatic about this because this is the third or fourth question I've seen this week from WPF developers who seem to have gone down so deeply down the rabbit hole of the MVVM pattern that they've forgotten why it exists.

Robert Rossney
@Robert Rossney: +1 good one.
Veer