I'm trying to create a program which would allow me to choose a program to start a different program based on different requirements. Basically I have a JSON document specifying Name, Path, Icon etc and a button is created for each entry.
I have a ButtonDef
class that looks like this:
public class ButtonDef
{
public int Id { get; set; }
public string Caption { get; set; }
public string Cmd { get; set; }
public string Icon { get; set; }
}
I create an ObservableCollection<ButtonDef>
called Buttons
which is a public property in my ViewModel
and populate is with ButtonDef
s.
I have a RelayCommand
property and corresponding method that will start the program.
Everything works if I explicitly create a RelayCommand
for each button and call it in the Command directive in XAML but since I don't know how many buttons there will be, that is not an OK solution.
My XAML looks like this:
<ItemsControl ItemsSource="{Binding Buttons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Caption}" Height="30" Width="50" Margin="10" Command="{Binding DoSomething}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The buttons are created fine, with the correct caption but the Command doesn't fire.
How can I make this happen?
Edit:
public class MainViewModel : ViewModelBase
{
public ObservableCollection<ButtonDef> Buttons { get; set; }
public List<DataItem> Items { get; set; }
public RelayCommand DoSomething { get; set; }
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
Items = new List<DataItem> { new DataItem("Item 1"), new DataItem("Item 2") };
//Items.Add(new DataItem("Item 1"));
if (Buttons == null) Buttons = new ObservableCollection<ButtonDef>();
foreach (var item in Items)
{
Buttons.Add(new ButtonDef
{
Caption = item.Title,
Cmd = "Path to exe"
});
}
}
While the link provided by @dymanoid gave me som insights it took some more tweaking to make it work.
First define the RelayCommand in your ViewModel
like this:
public RelayCommand<object> DoSomething {get; set;}
Initialize the RelayCommand property:
DoSomething = new RelayCommand<object>(parameter => ExecuteSomething(parameter));
void ExecuteSomething(object parameter)
{
// Do your work here
}
XAML
The button(s) are declared in the following way:
<ItemsControl ItemsSource="{Binding Buttons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Caption}" Height="30" Width="50" Margin="10" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Path=DataContext.DoSomething}" CommandParameter="{Binding Cmd}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
where the RelativeSource and the "DataContext" part of the Path
enables access to the window's DataContext.
Two links which led to the solution:
WPF Command Parameter for button inside ItemsControl
and
Pass different commandparameters to same command using RelayCommand WPF