WPF command bindings to a DataItemTemplate with MVVM

391 views Asked by At

I'm working on a WPF project, and I'm trying to follow the MVVM pattern. In the past I've only used DataTemplates to display information, but now I want to put a button on my template that performs an action related to the item containing the clicked button. I'm trying to figure out the best way to bind my code to my button in a way that the code knows which button was clicked.

My WindowViewModel contains relay commands which are exposed through command properties, as well as an ObservableCollection of `Items'.

public class WindowViewModel
{
    public ICommand ChangeItemCommand { get; private set; }

    public ObservableCollection<Item> Items {get;private set;}

    public WindowViewModel()
    {
        ChangeItemCommand = new RelayCommand(new Action<object>(this.ChangeItem));
        Items = new ObservableCollection<Item>();
    }

    public void ChangeItem(object o)
    {
        string key = (string)o;
        //do something to the item with provided key
    }
}

My ItemViewModel contains an ItemKey property to identify the item.

public class ItemViewModel
{
    public string ItemName { get; private set; }
    public string ItemKey { get; private set; }
}

My list box DataTemplate looks something like this.

<DataTemplate DataType="local:ItemViewModel">
    <StackPanel Orientation="Horizontal">
        <Label Content="{Binding ItemName}"/>
            <Button Command="???" CommandParameter="{Binding ItemKey}"/>
         </StackPanel>
</DataTemplate>

So I'm trying to figure out the best way to bind the button command to WindowViewModel.ChangeItemCommand of the WindowViewModel.

One option I'm considering would be to add a command property to the ItemViewModel which is set when instances are created by WindowViewModel

public class ItemViewModel
{
    public string ItemName { get; private set; }
    public string ItemKey { get; private set; }
    public ICommand ChangeItemCommand{ get; private set; }
}

<Button Command="{Binding ChangeItemCommand}" CommandParameter="{Binding ItemKey}"/>

Alternatively I could bind directly to the WindowViewModel.ChangeItemCommand property by using a RelativeSource.

<Button 
   Command="{Binding Path=ChangeItemCommand, 
                     RelativeSource={RelativeSource AncestorType={x:Type MyAppAWindow}}}" 
   CommandParameter="{Binding ItemKey}"/>

Note: I'm not entirely sure I did that right

So which would be recommended, or is there another better way?

2

There are 2 answers

0
123 456 789 0 On

The latter is the preferred one.

There are several ways and it boils down to preferences. There are a lot of MVVM Frameworks out there like MVVM-Light and Caliburn Micro that makes binding to commands way way easier compared to pure WPF CommandBindings.

0
akjoshi On

Both options are fine but the real decision to make is that who should be the owner of this action, In MVVM view models should be designed first and then views should use them properly.

For e.g.

  • if this action is say RemoveItem then I would say it belongs to WindowViewModel (as this is about changing the collection which WindowViewModel exposes).

  • but say this action is RefreshItemData or ChangeItemDetails(e.g.ItemName or ItemStatus) then it belongs to ItemViewModel (as this ItemViewModel can be used in other windows supporting this action)

So, I would advice you to first design the ViewModels considering functionality, re-usability etc. and then use suitable binding feature (i.e. RelativeSource, ElementName, direct etc.).