2

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");
        }
    }
}
dtaylor
  • 997
  • 3
  • 10
  • 26

1 Answers1

2

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.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • mm8, thanks, you were right. Unfortunately, it had no effect when I corrected it. Also, I'm saddened that Microsoft did not flag that as neither a compile time exception nor a run time exception. – dtaylor May 24 '17 at 14:00
  • No effect on what? What are you expecting to happen here? Please see my edit. – mm8 May 24 '17 at 14:00
  • mm8, I have break points set in the setter and getter of each of the properties "set { SetValue(AlphaIsRecordingProperty, value); }" but these are not being caught. Let me try something more definitive... – dtaylor May 24 '17 at 14:09
  • mm8, I can change the value of the property from code behind, but the value is not reflected in the view. If I change the view, the new value is not reflected in the property. – dtaylor May 24 '17 at 14:15
  • I'm surprised to hear your comment about dependency objects. I had read that this was the best practice. – dtaylor May 24 '17 at 14:17
  • It's not: https://stackoverflow.com/questions/291518/inotifypropertychanged-vs-dependencyproperty-in-viewmodel. By the way, could you please update your question to indicate how you set the properties? – mm8 May 24 '17 at 14:25
  • mm8, Thanks for the link, it was enlightening. However, there doesn't seem to be a consensus. Still, the POCO seems to work with MenuItems where Dependency does not. I will post changes to my example that work with POCO. – dtaylor May 24 '17 at 15:03
  • The setter of a dependency property is effectively bypassed: https://stackoverflow.com/questions/4225373/setters-not-run-on-dependency-properties. That's why I suggested that you should change the dependency properties into CLR properties if you expect the breakpoint to get hit. – mm8 May 24 '17 at 15:11
  • mm8, thanks and please see my edit. Thanks again. – dtaylor May 24 '17 at 16:53