Click here to Skip to main content
Click here to Skip to main content

Command Binding inside DataTemplate in Parent-Child Scenario in an MVVM Application

By , 21 Feb 2011
Rate this:
Please Sign up or sign in to vote.

Introduction

This article will address one of the problems a developer might run into when binding commands inside DataTemplate in Parent-Child scenario in Model-View-ViewModel pattern. Basic knowledge of this pattern is expected. The demo application is created in Visual Studio 2010.

Example Scenario

The demo application discussed in this article is available for download at the top of this page. It contains a very simple application that allows a user to add, update and remove brand and add, remove product in a brand. If the user types a valid brand name and hits Add Brand button, the brand will be added and shown in the tabs. I have used Header Editable Tab Control (see my previous article on this, this time I edited the HeaderTemplate of TabItem to add a delete button). Inside a tab, the user can add and remove product under specific brand.

Sample application to add brand and products

Now, to develop the application, let's identify the Views and ViewModels first. Initially, one can say there is only one View and Viewmodel (i.e. BrandsView and BrandsViewModel). Well, let's have a deeper look.

ViewModels identified

It's clear now, right? The red border shows BrandsView and BrandsViewModel, the green and blue border identify (SingleBrandView, SingleBrandViewModel) and (ProductView, ProductViewModel) respectively. Let's concentrate on the class diagram now:

Class Diagram of ViewModels

The parent-child relation is clear as BrandsViewModel contains a collection of SingleBrandViewModel which has a collection of ProductViewModel. So, we have three Views corresponding to these three ViewModels. Let's code the BrandsView.

<UserControl x:Class="DemoApp.Views.BrandsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:tab="clr-namespace:FormattedTabControl;
		assembly=FormattedTabControl"
             xmlns:vm="clr-namespace:DemoApp.ViewModels"
             xmlns:vw="clr-namespace:DemoApp.Views">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:ProductViewModel}">
            <vw:ProductView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:SingleBrandViewModel}">
            <vw:SingleBrandView />
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="15*" />
            <ColumnDefinition Width="70*" />
            <ColumnDefinition Width="15*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock Text="Brand Name" Grid.Row="0" Grid.Column="0" Margin="5" />
        <TextBox x:Name="txtBrandName"
            Grid.Row="0" 
            Grid.Column="1"
            Margin="5" />
        <Button Grid.Row="0" 
                Grid.Column="2" 
                Margin="5" 
                Content="Add Brand"
                Command="{Binding AddBrand}"
                CommandParameter="{Binding ElementName=txtBrandName, Path=Text}"/>
        <tab:FormattedTab x:Name="tab"
                          Grid.ColumnSpan="3"
                          Grid.Row="1"
                          ItemSource="{Binding Brands}"/>
    </Grid>
</UserControl>

So, binding of AddBrand Command is trivial, but what about DeleteBrand? If we see the picture where we bordered the Viewmodels, the close button is actually inside the FormattedTabControl (FormattedTab is the custom TabControl used here). So, it seems that we should put DeleteBrand inside SingleBrandViewModel. Ok, I agree, but remember our SingleBrandViewModel collection is in BrandsViewModel. So, how do we will perform the delete operation? A simple answer is, we will put an event in SingleBrandViewModel, fire that when the Command executes and hook the event in BrandsViewModel when we add a Brand. This will certainly work, but a Brand should not raise an event to delete itself. This makes the ViewModel class coupled and hard to test. We can actually bind the command with parent ViewModel, BrandViewModel here using FindAncestor or ElementName in Binding expression. Let's see how we can do that inside the ItemContainerStyle of FormattedTabControl.

<Style x:Key="TabItemHeaderContainerStyle" TargetType="TabItem">
            <Setter Property="HeaderTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                            <local:EditableTabHeaderControl Grid.Column="0" 
				Style="{StaticResource EditableTabHeaderControl}">
                                <local:EditableTabHeaderControl.Content>
                                    <Binding Path="Header" Mode="TwoWay"/>
                                </local:EditableTabHeaderControl.Content>
                            </local:EditableTabHeaderControl>
                            <Button x:Name="cmdTabItemCloseButton" 
                                    Style="{StaticResource TabItemCloseButtonStyle}"
                                    Grid.Column="1" Margin="15,0,0,0"
                                    Command="{Binding RelativeSource=
					{RelativeSource FindAncestor, 
					AncestorType={x:Type TabControl}}, 
					Path=DataContext.DeleteBrand}"
                                    CommandParameter="{Binding}"/>
                        </Grid>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>

This is the style of FormattedTab's HeaderTemplate. It contains two columns, one for EditableTabHeaderControl and the other for a Close Button. Now, we have to bind the DeleteBrand command with this button. The FormattedTab's ItemSource is binded with Brands collection of BrandsViewModel. As each TabItem is binded with SingleBrandViewModel, so from this button, we have to find its ancestor TabControl and bind to the ancestor's DataContext. Similar case arises for the Remove button, it's datacontext is ProductViewModel but we put and bind the DeleteProduct in SingleBrandViewModel. So, at Remove button of ProductView, out target ancestor will be the ListBox which is in SingleBrandView.

<Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="{Binding ProductName}" Margin="5"/>
            <Button Grid.Column="1" Margin="5"
                    Content="Remove"
                    Command="{Binding RelativeSource=
			{RelativeSource FindAncestor, 
			AncestorType={x:Type ListBox}}, 
			Path= DataContext.DeleteProduct}"
                    CommandParameter="{Binding}" />
</Grid>

Have a close look at CommandParameter in both cases. The whole binded object is passed as parameter, which makes the Command's execution extremely easy. Have a look at Execute method for DeleteProduct Command:

private DelegateCommand<productviewmodel> deleteProduct;

public DelegateCommand<productviewmodel> DeleteProduct
        {
            get
            {
                return this.deleteProduct ?? (this.deleteProduct = 
				new DelegateCommand<productviewmodel>(
                                   this.ExecuteDeleteProduct,
                                   (arg) => true));
            }
        }

private void ExecuteDeleteProduct(ProductViewModel obj)
{
	if (this.Products.Contains(obj))
	{
		this.Products.Remove(obj);
	}
}

This is the way we can bind commands in parent-child scenario in MVVM pattern keeping the ViewModels more testable.

Code

The solution contains two projects, one for the demo application and the other for the FormattedTabControl.

Notes

I have used Prism's DelegateCommand class here. Also, for the style of close button in the custom TabControl, I looked at this four part article on WPF TabControl. Thanks to Olaf Rabbachin for writing such a wonderful article.

What Now?

I would like to hear your thoughts about this implementation. Share your ideas, just leave a comment...

History

  • 21st February, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Amit Kumar Dutta
Student University of Alabama at Birmingham
United States United States

A very simple guy, who codes with his heart (and with his brain, of course).

Homepage: http://www.amitdutta.net
View my profile on Linkedin: http://www.linkedin.com/in/am1tdutta/
Follow on   Twitter

Comments and Discussions

 
GeneralThanks [modified] PinmemberNaserAsadi4-Jan-13 19:30 
GeneralRe: Thanks PinmemberAmit Kumar Dutta11-Aug-13 23:23 
GeneralMulti Event Command Binder PinmemberMember 432946918-Dec-11 7:27 
GeneralMy vote of 5 PinmemberCodeShoveler7-Mar-11 11:34 
GeneralRe: My vote of 5 PinmemberAmit Kumar Dutta3-Jun-11 5:15 
GeneralMy vote of 5 PinmemberRaviRanjankr21-Feb-11 20:23 
GeneralRe: My vote of 5 PinmemberAmit Kumar Dutta22-Feb-11 3:50 
GeneralMy vote of 5 PinmemberMahmudul Haque Azad21-Feb-11 17:28 
GeneralRe: My vote of 5 PinmemberAmit Kumar Dutta22-Feb-11 3:50 
Thanks bhaia Smile | :)
GeneralMy vote of 5 PinmemberSlacker00721-Feb-11 8:26 
GeneralRe: My vote of 5 PinmemberAmit Kumar Dutta22-Feb-11 3:49 
GeneralMy vote of 5 Pinmembersayem_bd21-Feb-11 6:23 
GeneralRe: My vote of 5 PinmemberAmit Kumar Dutta21-Feb-11 6:25 
GeneralMy vote of 5 Pinmemberkdgupta8721-Feb-11 5:27 
GeneralRe: My vote of 5 PinmemberAmit Kumar Dutta21-Feb-11 5:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 21 Feb 2011
Article Copyright 2011 by Amit Kumar Dutta
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid