Reputation: 14511
I have an observableCollection that I loop through using an itemSource inside my view. Each item in the observablecollection contains a button that sends a command (openSessionCommand) to the viewModel. My question is - how can I send and ID back to the viewModel of which button was clicked inside the itemSource?
<ItemsControl ItemsSource="{Binding AvailableSessions}" Margin="490,181,10.111,39.111">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Border BorderBrush="Black" Background="Gainsboro" BorderThickness="1" Margin="2">
<Grid Background="#FFECECEC">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}, Path=DataContext.OpenSessionCommand}"
HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" Width="243" Height="42">
<TextBlock TextWrapping="Wrap">
<Run Text="{Binding SessionName}"/><LineBreak/>
<Run Text="{Binding Genre}"/><Run Text=" - "/><Run Text="{Binding Tempo}"/>
</TextBlock>
</Button>
<Label Content="{Binding AdminUsername}" HorizontalAlignment="Left"
Margin="10,53,0,0" VerticalAlignment="Top" Width="243" Height="26"/>
<Label Content="{Binding Client1Username}" HorizontalAlignment="Left"
Margin="10,71,0,0" VerticalAlignment="Top" Width="243" Height="25"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
DelegateCommand:
public class DelegateCommand : ICommand
{
private readonly Action _command;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action command, Func<bool> canExecute = null)
{
if (command == null)
throw new ArgumentNullException();
_canExecute = canExecute;
_command = command;
}
public void Execute(object parameter)
{
_command();
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
}
ICommand:
public ICommand OpenSessionCommand { get
{ return new DelegateCommand(OpenSession); }
}
public void OpenSession()
{
ContinueReceiving = false;
dispatcherTimer.Stop();
Messenger.Default.Send<NavigateMessage>(
new NavigateMessage(SessionViewModel.ViewName, this));
}
Upvotes: 1
Views: 730
Reputation: 1775
Use CommandParameter. Using {Binding}
will pass the object in the observable collection that was clicked. More at MSDN
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.OpenSessionCommand}" CommandParemeter="{Binding}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="243" Height="42">
<TextBlock TextWrapping="Wrap">
<Run Text="{Binding SessionName}"/><LineBreak/>
<Run Text="{Binding Genre}"/><Run Text=" - "/>
<Run Text="{Binding Tempo}"/>
</TextBlock>
</Button>
You will also need to adjust the OpenSessionCommand method signature to accept your parameter but that will depend on what Command implementation you are using.
UPDATE
Below is a generic implementation of DelegateCommand that accepts a parameter.
public class DelegateCommand<T> : ICommand where T : class
{
private readonly Action<T> _command;
private readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action<T> command, Func<T, bool> canExecute = null)
{
if (command == null)
throw new ArgumentNullException();
_canExecute = canExecute;
_command = command;
}
public void Execute(object parameter)
{
_command(parameter as T);
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter as T);
}
}
Now when you define your ICommand it should be something like this:
public ICommand OpenSessionCommand { get { return new DelegateCommand<Button>(OpenSession); } }
public void OpenSession(Button button)
{
ContinueReceiving = false;
dispatcherTimer.Stop();
Messenger.Default.Send<NavigateMessage>(new NavigateMessage(SessionViewModel.ViewName, this));
}
Upvotes: 2
Reputation: 11763
I'm using something similar from within a listbox with a context menu.
<ContextMenu>
<MenuItem Header ="Edit Exercise"
Command="{Binding EditExercise_Command}"
CommandParameter="{Binding SelectedExercise}"
/>
So, the method being called receives the exercise I was right clicking on as a parameter.
You can pass the button :)
Upvotes: 1