I have a view that is displaying a dynamic list of buttons (shown as text.) The users will already expect the text to be "selectable". But that is beside the point.
I am trying to bind the button(s) to a RelayCommand but when I test, clicking a line of text does not cause the bound command to be executed. I'm not sure what I am missing. This is the first time I've tried something like this, using an ItemsControl with a DataTemplate. What am I missing? Here is the view xaml:
<Window x:Class="MyCode.Correction.CorrectionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ls="clr-namespace:MyCode.Correction"
DataContext="{Binding Source={StaticResource Locator}, Path=CorrectionViewModel, UpdateSourceTrigger=PropertyChanged}"
mc:Ignorable="d"
Width="270"
Height="300"
ResizeMode="NoResize"
Title="Correction menu"
Topmost="True"
WindowStartupLocation="CenterOwner"
Icon="/MyApp;component/Images/cc.ico"
AutomationProperties.AutomationId="CorrectionWindow">
<Grid Margin="0,10,-6,9" RenderTransformOrigin="0.264,0.344">
<Grid.RowDefinitions>
<RowDefinition Height="114*" />
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding CorrectionOptions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"
Command="{Binding CorrectionCommand, Mode=OneTime}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="20,5,0,0"
FontSize="15">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
And here is the viewmodel:
namespace MyCode.Correction
{
public class CorrectionViewModel : DialogViewModelBase
{
private readonly IDialogService _dialogService;
private readonly ILogger Logger;
public CorrectionViewModel(IDialogService dialogService)
{
Logger = LoggerFactory.GetLoggerInstance(typeof(CorrectionViewModel));
CorrectionCommand = new RelayCommand<object>((s) => OnCorrectionClicked(s));
_dialogService = dialogService;
}
public RelayCommand<object> CorrectionCommand { get; set; }
private ObservableCollection<string> _correctionOptions;
public ObservableCollection<string> CorrectionOptions
{
get
{
return _correctionOptions;
}
set
{
Set(() => CorrectionOptions, ref _correctionOptions, value);
}
}
private void OnCorrectionClicked(object selectedCorrection)
{
UserDialogResult = (string)selectedCorrection;
CloseAction();
}
}
}
You command and parameter bindings are wrong.
The Command
binding has to point to the data context of the parent ItemsControl
, which is the CorrectionViewModel
that contains the CorrectionCommand
. This is done using RelativeSource
.
The CommandParameter
is the current data context itself (the clicked correction option string
). A TemplatedParent
as RelativeSource
is used in control templates only, not for data templates.
It should work like this.
<ItemsControl ItemsSource="{Binding CorrectionOptions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"
Command="{Binding DataContext.CorrectionCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding}"
Margin="20,5,0,0"
FontSize="15">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>