Search code examples
c#wpfxamlmvvmmvvm-light

Checked event command binding on RadioButton doesn't work when the control is dynamically created


I have 3 RadioButton controls that I add to my WPF program dynamically, with the help of a custom class I've defined and an ItemsControl. My custom class is simple; it has two properties, one an identifier and the other a bool to represent the Checked/Unchecked status.

public class RadioButtonItem
{
    public string ItemName { get; set; }
    public bool IsChecked { get; set; }
}

I'm using MVVMLight, and a ViewModel, and DataContext etc are all properly setup.

In the MainWindow I have the RadioButton controls added like so:

<Grid x:Name="LayoutRoot">
    <ItemsControl ItemsSource="{Binding Path=RadioButtons}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"></StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <RadioButton Content="{Binding ItemName}" 
                             IsChecked="{Binding IsChecked}"
                             Margin="12" GroupName="ABC">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Checked">
                            <i:InvokeCommandAction Command="{Binding RadioButtonCheckedCommand}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </RadioButton>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Note that i is the Interactivity namespace: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

Now in my ViewModel, I create a list of RadioButtonItem instances, and at loading time it creates 3 RadioButton controls in the XAML end just fine, as I expect it to. Then using MVVMLight command RelayCommand, I hook up the Checked event to the RadioButtonCheckedCommand command, which in turn is supposed to call the RadioButtonChecked() private method.

public class MainViewModel : ViewModelBase
{
    private List<RadioButtonItem> _radioButtons;
    public List<RadioButtonItem> RadioButtons
    {
        get => _radioButtons;
        set { _radioButtons = value; RaisePropertyChanged(); }
    }

    public RelayCommand RadioButtonCheckedCommand { get; private set; }

    public MainViewModel()
    {
        RadioButtons = new List<RadioButtonItem>
        {
            new RadioButtonItem() { ItemName = "A", IsChecked = false },
            new RadioButtonItem() { ItemName = "B", IsChecked = false },
            new RadioButtonItem() { ItemName = "C", IsChecked = false },
        };

        RadioButtonCheckedCommand = new RelayCommand(RadioButtonChecked);
    }

    private void RadioButtonChecked()
    {

    }
}

But, when I run the program it doesn't call the said event. Neither does it give me any errors. How do I get an event to work in a dynamically created control like this?

To verify my command bindings etc. work, I also created three static RadioButton controls on my window, as shown below, and attached them to the same command and they work fine on Checked event.

<StackPanel Grid.Row="1" HorizontalAlignment="Center">
    <RadioButton Content="X" Margin="12">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Checked">
                <i:InvokeCommandAction Command="{Binding RadioButtonCheckedCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </RadioButton>
    <RadioButton Content="Y" Margin="12">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Checked">
                <i:InvokeCommandAction Command="{Binding RadioButtonCheckedCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </RadioButton>
    <RadioButton Content="Z" Margin="12">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Checked">
                <i:InvokeCommandAction Command="{Binding RadioButtonCheckedCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </RadioButton>
</StackPanel>

Solution

  • inside ItemsControl, binding to RadioButtonCheckedCommand is trying to resolve it against an item - RadioButtonItem instance

    but the command is defined in MainViewModel. it works in the StackPanel because there are no bindings to RadioButtonItems.

    the solution is to bind to ItemsControl.DataContext (same as Grid.DataContext):

    <i:InvokeCommandAction Command="{Binding Path=DataContext.RadioButtonCheckedCommand, ElementName=LayoutRoot}"/>