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 Button
s at run time that is dynamically add Button
s 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 Button
s 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 Button
s 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"))
{
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 {
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;
}
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 Button
s within
the ItemTemplate
.
History
- First version: 9 October, 2013.