Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
9 Oct 2013 1  
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 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.

<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:

<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.

<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.

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.

<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.

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

For ListBox ItemsSource, add property CategoryButtonList in ViewModel.

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.

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.

<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.

<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.

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.

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:

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.

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:

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.

<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:

Click on Danish buttons:

Click on Save button in English:

Click on Save button in Danish:

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here