Search code examples
c#wpfxamlwcf-binding

WPF MenuItem.IsChecked binding not working


I have a simple menu and would like the IsChecked property of the MenuItems to be bound so that I could later manipulate them in code behind. I have a break point set in the getter and setter of the property but they are never hit.

I've searched other questions and none have shed any light on my issue. I created a skeleton project to demonstrate what I'm seeing.

MainWindow.xaml:

<Window x:Class="POC_BindingMenuItemIsChecked.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:POC_BindingMenuItemIsChecked"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:MainWindowViewModel x:Key="mainWindowViewModel"/>
    </Window.Resources>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Choice">
                <MenuItem Header="Alpha"
                          IsCheckable="True"
                          Checked="MenuItemAlpha_Checked"
                          IsChecked="{Binding AlphaIsRecording, Mode=TwoWay}"/>
                <MenuItem Header="Bravo"
                          IsCheckable="True"
                          Checked="MenuItemBravo_Checked"
                          IsChecked="{Binding
                            Source={StaticResource mainWindowViewModel},
                            Path=BravoIsRecording,
                            Mode=TwoWay}"/>
                <MenuItem Header="Charlie"
                          IsCheckable="True"
                          Checked="MenuItemCharlie_Checked"
                          IsChecked="{Binding CharlieIsRecording, Mode=TwoWay}"/>
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    private MainWindowViewModel _MainWindowViewModel = null;

    public MainWindow()
    {
        InitializeComponent();

        // get a reference to the binding sources so we can set the properties
        _MainWindowViewModel = new MainWindowViewModel();
        this.DataContext = _MainWindowViewModel;
    }

    private void MenuItemAlpha_Checked(object sender, RoutedEventArgs e)
    {
        //no-op yet
    }

    private void MenuItemBravo_Checked(object sender, RoutedEventArgs e)
    {
        //no-op yet
    }

    private void MenuItemCharlie_Checked(object sender, RoutedEventArgs e)
    {
        //no-op yet
    }
}

MainWindowViewModel.cs:

class MainWindowViewModel : DependencyObject
{

    // Alpha

    public static PropertyMetadata AlphaIsRecordingPropertyMetadata
        = new PropertyMetadata(null);
    public static DependencyProperty AlphaIsRecordingProperty
        = DependencyProperty.Register(
            "AlphaIsRecording",
            typeof(bool),
            typeof(MainWindowViewModel),
            AlphaIsRecordingPropertyMetadata);
    public bool AlphaIsRecording
    {
        get { return (bool)GetValue(AlphaIsRecordingProperty); }
        set { SetValue(AlphaIsRecordingProperty, value); }
    }

    // Bravo

    public static PropertyMetadata BravoIsRecordingPropertyMetadata
        = new PropertyMetadata(null);
    public static DependencyProperty BravoIsRecordingProperty
        = DependencyProperty.Register(
            "BravoIsRecording",
            typeof(bool),
            typeof(MainWindowViewModel),
            BravoIsRecordingPropertyMetadata);
    public bool BravoIsRecording
    {
        get { return (bool)GetValue(BravoIsRecordingProperty); }
        set { SetValue(BravoIsRecordingProperty, value); }
    }

    // Charlie

    public static PropertyMetadata CharlieIsRecordingPropertyMetadata
        = new PropertyMetadata(null);
    public static DependencyProperty CharlieIsRecordingProperty
        = DependencyProperty.Register(
            "CharlieIsRecording",
            typeof(bool),
            typeof(MainWindowViewModel),
            CharlieIsRecordingPropertyMetadata);
    public bool CharlieIsRecording
    {
        get { return (bool)GetValue(CharlieIsRecordingProperty); }
        set { SetValue(CharlieIsRecordingProperty, value); }
    }
}

EDIT:

Because of the solution that mm8 provided (POCO and not Dependency) I've modified the solution. It now functions as a set of three MenuItems that behave as radio buttons. The code follows:

MainWindow.xaml:

<Window x:Class="POC_BindingMenuItemIsChecked.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:POC_BindingMenuItemIsChecked"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:MainWindowViewModel x:Key="mainWindowViewModel"/>
    </Window.Resources>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Choice">
                <MenuItem Header="Alpha"
                          IsCheckable="True"
                          Checked="MenuItemAlpha_Checked"
                          IsChecked="{Binding AlphaIsRecording, Mode=TwoWay}"/>
                <MenuItem Header="Bravo"
                          IsCheckable="True"
                          Checked="MenuItemBravo_Checked"
                          IsChecked="{Binding BravoIsRecording, Mode=TwoWay}"/>
                <MenuItem Header="Charlie"
                          IsCheckable="True"
                          Checked="MenuItemCharlie_Checked"
                          IsChecked="{Binding CharlieIsRecording, Mode=TwoWay}"/>
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    private MainWindowViewModel _mainWindowViewModel = null;

    public MainWindow()
    {
        InitializeComponent();

        // get a reference to the binding sources so we can set the properties
        _mainWindowViewModel = new MainWindowViewModel();
        this.DataContext = _mainWindowViewModel;
    }

    private void MenuItemAlpha_Checked(object sender, RoutedEventArgs e)
    {
        _mainWindowViewModel.BravoIsRecording = false;
        _mainWindowViewModel.CharlieIsRecording = false;
    }

    private void MenuItemBravo_Checked(object sender, RoutedEventArgs e)
    {
        _mainWindowViewModel.AlphaIsRecording = false;
        _mainWindowViewModel.CharlieIsRecording = false;
    }

    private void MenuItemCharlie_Checked(object sender, RoutedEventArgs e)
    {
        _mainWindowViewModel.AlphaIsRecording = false;
        _mainWindowViewModel.BravoIsRecording = false;
    }
}

ViewModelBase.cs:

class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // Create the OnPropertyChanged method to raise the event
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

MainWindowViewModel.cs:

class MainWindowViewModel : ViewModelBase
{
    private bool _alphaIsRecording = false;
    private bool _bravoIsRecording = false;
    private bool _charlieIsRecording = false;

    // Alpha

    public bool AlphaIsRecording
    {
        get { return _alphaIsRecording; }
        set
        {
            _alphaIsRecording = value;
            OnPropertyChanged("AlphaIsRecording");
        }
    }

    // Bravo

    public bool BravoIsRecording
    {
        get { return _bravoIsRecording; }
        set
        {
            _bravoIsRecording = value;
            OnPropertyChanged("BravoIsRecording");
        }
    }

    // Charlie

    public bool CharlieIsRecording
    {
        get { return _charlieIsRecording; }
        set
        {
            _charlieIsRecording = value;
            OnPropertyChanged("CharlieIsRecording");
        }
    }
}

Solution

  • You are binding to AlphaIsChecked and BravoIsChecked but I don't see any such properties? Try to bind to BravoIsRecording:

    <MenuItem Header="Bravo"
            IsCheckable="True"
            Checked="MenuItemBravo_Checked"
            IsChecked="{Binding
            Source={StaticResource mainWindowViewModel},
            Path=BravoIsRecording,
            Mode=TwoWay}"/>
    

    Also, a view model doesn't typically inherit from DependencyObject and define dependency properties. Turn your properties into ordinary CLR properties and put a breakpoint in the setter of the BravoIsRecording and it should get hit when you check the CheckBox.