Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / WPF
Tip/Trick

Passing Command Parameter for Buttons within an ItemTemplate using MVVM Pattern in a WPF Application

Rate me:
Please Sign up or sign in to vote.
4.88/5 (14 votes)
8 Oct 2013CPOL4 min read 185.6K   3K   22   13
Passing a Command Parameter for Buttons within an ItemTemplate using the MVVM Pattern in a WPF application.

Introduction

In this tip, we will talk about how to pass a CommandParameter for a Button control which is used within the ItemTemplate of the ListBox control. We are trying to load the list of Buttons inside the <code>ListBox. The list of Buttons is in English and Danish language. This tip will help you to identify which Button has been pressed and how to pass the CommandParameter within the Button control in MVVM.

Background

Suppose we have three buttons and each Button has its own command in the ViewModel. Then it can be done by adding three different ICommand properties in the ViewModel and bind those commands inside the XAML to the respective Button controls. This is the very straight forward approach in MVVM model.

Now the scenario is we want to display a list of Buttons at run time that is dynamically add Buttons into the List. We can add the Button control inside the DataTemplate and use this Template as a StaticResource of the ItemTemplate.

XML
<UserControl.Resources>
    <DataTemplate x:Key="UserTemplate1">
    <Button x:Name="button1"Command="{Binding RelativeSource=
    {RelativeSource AncestorType=UserControl},Path=DataContext.OnButtonClickCommand}"/>
    </DataTemplate>
</UserControl.Resources>  

In Grid - Use this DataTemplate:

XML
<ListBox ItemTemplate="{StaticResource UserTemplate1}"/> 

Note: OnButtonClickCommand is ICommand property inside the ViewModel.

This will help us to add multiple Buttons inside the ListBox by providing an ItemsSource. But here is one problem, what if there are three buttons named as Save, Open, and Close. And the requirement is that each Button click event will perform a different functionality. All these buttons have a single Command value OnButtonClickCommand. Then it is very difficult to identify in the ViewModel which Button is pressed from the Buttons list.

To solve the above problem, we need to pass a CommandParameter for the Button control to identify which Button is currently pressed.

XML
<DataTemplatex:Key="UserTemplate1">
     <Buttonx:Name="button1" Command="{Binding RelativeSource={RelativeSource
     AncestorType=UserControl},Path=DataContext.OnButtonClickCommand}" 
     CommandParameter ="{Binding ElementName=button1}"/>
</DataTemplate> 

Using the Code

Let's start with an example. First of all, create a new WPF project with an MVVM structure.

Image 1

We have created a Helper folder for some helper files. We want to create a List of Buttons with three buttons: Save, Open, and Close in two different languages. So we create a UserControl ButtonListUC in the View and add a ListBox control inside it. And add the DataTemplate as resource, because we want to use this DataTemplate as a StaticResource of the ItemTemplate in the ListBox. We already discussed this previously.

XML
<ListBox Margin="2" ScrollViewer.HorizontalScrollBarVisibility="Hidden" 
Cursor="Hand"         ItemsSource="{BindingCategoryButtonList}" 
ItemTemplate="{StaticResource UserTemplate1}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>  

Now add a ViewModel class ButtonListVM inside the ViewModel folder and use it as a DataContext of View XAML class.

XML
xmlns:local="clr-namespace:CommandBindingMVVMTest.ViewModel"
<UserControl.DataContext>
    <local:ButtonListVM/> 
</UserControl.DataContext>  

For ListBox ItemsSource, add property CategoryButtonList in ViewModel.

C#
Private List<CategoryItem> categorybuttonList = new List<CategoryItem>();
public List<CategoryItem> CategoryButtonList
{
    get {return categorybuttonList; }
    set {categorybuttonList = value; }
}      

CategoryButtonList is a collection of CategoryItem which has two Getter and Setter properties.

C#
public class CategoryItem
{
    public string ButtonContent { get; set; }
    public string ButtonTag { get; set; }
} 

Bind ButtonContent and ButtonTag property of CategoryItem to the Button's Content and Tag properties, respectively in XAML.

C#
<Button x:Name="button1" 
Content="{Binding ButtonContent}" Tag="{Binding ButtonTag}"
Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},
Path=DataContext.OnButtonClickCommand}" 
CommandParameter="{Binding ElementName=button1}"/> 

Here, we are using Tag property from Button class and this will help us to identify which Button is pressed and Content is use for display string on Button. Content will change as per Language selection. We added two buttons in XAML for language change. Passing the CommandParameter as language code (Abbreviations) because we used the same Command binding property OnCollectionChangeCommand to both controls.

XML
<Button Content="EnglishButtons" 
Command="{Binding OnCollectionChangeCommand }"
CommandParameter="en-US"/> 
<Button Content="DanishButtons" 
Command="{Binding OnCollectionChangeCommand}"
CommandParameter="da"/>  

Now add all the Command binding properties to ViewModel class.

C#
Icommand onButtonClickCommand;
public Icommand OnButtonClickCommand
{
    get { return onButtonClickCommand??
    (onButtonClickCommand = new RelayCommand(ButtonClick)); }
}
Icommand onCollectionChangeCommand;
public Icommand OnCollectionChangeCommand
{
    get { return onCollectionChangeCommand?? 
    (onCollectionChangeCommand = new RelayCommand(OnCollectionChange)); }
} 

Here ButtonClick and OnCollectionChange are Actions to be executed. Collect the parameter as object type.

C#
private void ButtonClick(object button){}
private void OnCollectionChange(object lang){} 

We add another Helper classes ButtonNames, EnglishCategory and DanishCategory. All these classes contain three static string properties. ButtonNames class is used for Button Tag property and this will be the same for any language whereas EnglishCategory and DanishCategory classes are used for Button Content property.

For example, the ButtonNames class:

C#
public class ButtonNames
{
    public static string SaveButton = "Save";
    public static string OpenButton = "Open";
    public static string CloseButton = "Close";
} 

Now adding the CategoryItem in CategoryButtonList, create three objects of CategoryItem item1, item2, item3 and assign the values to the ButtonContent and ButtonTag properties to each element as per language.

C#
private void OnCollectionChange(object lang)
{
    CategoryItem item1 = new CategoryItem();
    CategoryItem item2 = new CategoryItem();
    CategoryItem item3 = new CategoryItem();
    if(lang.ToString().Equals("en-US"))
    //IfEnglishbuttonispressed
    {
        item1.ButtonContent = EnglishCategory.SaveButton;
        item1.ButtonTag = ButtonNames.SaveButton;
        item2.ButtonContent = EnglishCategory.OpenButton;
        item2.ButtonTag = ButtonNames.OpenButton;
        item3.ButtonContent = EnglishCategory.CloseButton;
        item3.ButtonTag = ButtonNames.CloseButton;
    }
    else                    //IfDanishbuttonispressed
    {
        item1.ButtonContent = DanishCategory.SaveButton;
        item1.ButtonTag = ButtonNames.SaveButton;
        item2.ButtonContent = DanishCategory.OpenButton;
        item2.ButtonTag = ButtonNames.OpenButton;
I        tem3.ButtonContent = DanishCategory.CloseButton;
        item3.ButtonTag = ButtonNames.CloseButton;
    }
    //Intializethebuttonlist
    CategoryButtonList = new List<CategoryItem>();
    CategoryButtonList.Add(item1);
    CategoryButtonList.Add(item2);
    CategoryButtonList.Add(item3);
} 

For the Button click event, we only read the Tag property of the Button which is passed as a CommandParameter from the View:

C#
private void ButtonClick(object button)
{
    Button clickedbutton = button as Button;
    if(clickedbutton != null)
    {
        string msg = string.Format("YouPressed:{0}button",clickedbutton.Tag);
        MessageBox.Show(msg);
    }
}     

In this ButtonClick function, we can check the (clickedbutton.Tag) value with static string properties of the ButtonNames class and as per the value, we can define the functionality for that Button.

Lastly, to run the application, add the ButtonListUC UserControl instance in the MainPage window.

XML
<Window x:Class="CommandBindingMVVMTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CommandBindingMVVMTest.Views"
    Title="MVVMCommandBinding" Height="200" 
    Width="500" WindowStyle="ToolWindow">
    <Grid>
        <local:ButtonListUC HorizontalAlignment="Center"/>
    </Grid>
</Window>  

Let us try to run the application and perform various operations to see the solution in action.

Click on English buttons:

Image 2

Click on Danish buttons:

Image 3

Click on Save button in English:

Image 4

Click on Save button in Danish:

Image 5

Points of Interest

In this way, we have talked about how to pass the CommandParameter from the View to the ViewModel and how to use the CommandParameter for the Button which is a ListBox element. We have also talked about how to use the Tag and Content properties of the Button, if we are using a ListBox to bind the multiple Buttons within the ItemTemplate.

History

  • First version: 9 October, 2013.

License

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


Written By
Software Developer
India India
I am front-End UI developer. I love to work in web site development specially in SPA. I love to accept challenges. I am passionate developer of Angular, React, React-native.

I Started my career with C# - Silverlight. After working in silverlight I developed Windows Phone and WPF applications. I also worked on ASP.NET MVC with EntiyFramework and NHibernate as ORM.

My Certifications are
MCTS: Microsoft Silverlight 4, Development
MCTS: Microsoft .NET Framework 4, Data Access

Comments and Discussions

 
Questionupon attempting to load the solution to visual studio 2013 community edition I get a an error : The procedure entry point_Atomic_fetch_sub_4 could not be located in the dynamic link library MSVCR110.dll Pin
Member 1092489523-Mar-15 11:42
Member 1092489523-Mar-15 11:42 
GeneralMy vote of 5 Pin
TheRealSteveJudge14-Jan-15 4:06
TheRealSteveJudge14-Jan-15 4:06 
This article helped me a lot today. Thank you!!!
QuestionWhy only one command? Pin
Richard Deeming14-Oct-13 6:56
mveRichard Deeming14-Oct-13 6:56 
AnswerRe: Why only one command? Pin
Punamchand Dhuppad14-Oct-13 19:50
professionalPunamchand Dhuppad14-Oct-13 19:50 
SuggestionRe: Why only one Language? Pin
ppfepf18-Sep-14 0:16
ppfepf18-Sep-14 0:16 
GeneralRe: Why only one Language? Pin
Punamchand Dhuppad18-Sep-14 3:49
professionalPunamchand Dhuppad18-Sep-14 3:49 

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

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