0

After looking around on StackOverflow and other websites, I can see that people have a lot of problems with binding properties and commands to MenuItems and ContextMenus because ContextMenu is not part of the WPF visual tree. Anyway, I've tried a few different solutions and am not having any luck.

I have a MenuItem that is part of a ContextMenu. The ContextMenu is part of a window that is bound to a ViewModel in its code behind like so:

public partial class Window1 : Window
{
    public MainWindowViewModel ViewModel { get { return DataContext as MainWindowViewModel; } } 

    public Window1()
    {
        InitializeComponent();

        //There is a property in the App.xaml.cs file that refers to MainWindowViewModel
        DataContext = App.MainWindowViewModel = new MainWindowViewModel();
    }
}

The property that I am trying to bind to in MainWindowViewModel:

private bool _askBeforeDownloading_Checked = true;

public bool AskBeforeDownloading_Checked
{
    get { return _askBeforeDownloading_Checked; }
    set
    {
        _askBeforeDownloading_Checked = value;
        NotifyPropertyChange(() => AskBeforeDownloading_Checked);
    }
}

My current attempt in XAML:

<Button Name="Button_1" >
    <Button.ContextMenu>
        <ContextMenu x:Name="MainContextMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" >
            <MenuItem >
                <MenuItem IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.AskBeforeDownloading_Checked}" />
            </MenuItem>
        </ContextMenu>
    </Button.ContextMenu>
</Button>

I came up with my current XAML based on the accepted answer on this question, along with this guide. What am I missing? I'm not getting any output errors, but the MenuItem is not getting checked. Is there something that I am not doing with PlacementTarget?

Update: I thought it would be important to note that my ContextMenu is a child control of a Button. I've added it to my XAML.

Update 2: After using Snoop on my application I found that my Button was automatically inheriting from MainWindowViewModel as it should. However, I overlooked a parent MenuItem that may affect my code. I've updated my XAML and apologize for missing that the first time.

Community
  • 1
  • 1
Eric after dark
  • 1,768
  • 4
  • 31
  • 79

2 Answers2

3

The DataContext is getting inherited from the <ContextMenu>, so you don't need to write anything special in the binding there.

<Button Name="Button_1" >
    <Button.ContextMenu>
        <ContextMenu x:Name="MainContextMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" >
            <MenuItem IsCheckable="True" IsChecked="{Binding AskBeforeDownloading_Checked}" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

To explain in more detail, the DataContext binding in the <ContextMenu> is saying :

  • "RelativeSource Self" means "This item", which is <ContextMenu>
  • "PlacementTarget" means "The item this item is placed on", which in this case is the <Button> object

So you're saying bind the ContextMenu.DataContext property to the Button.DataContext property.

That property should be your MainWindowViewModel, so you can just use a normal binding to bind to the .AskBeforeDownloading_Checked property on the DataContext.

Alternatively, you could remove the .DataContext binding from the <ContextMenu>, and just keep your <MenuItem> binding similar to how you have it, but you need to reference the Button.DataContext.AskBeforeDownloading_Checked, rather than the Button.AskBeforeDownloading_Checked that you have now.

<Button Name="Button_1" >
    <Button.ContextMenu>
        <ContextMenu x:Name="MainContextMenu>
            <MenuItem IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.AskBeforeDownloading_Checked}" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

Of the two methods, I prefer the first one as it allows for less code if you are binding more items/properties to the Button.DataContext properties.

Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Shouldn't my `MenuItem` be checked after doing this? Because it's not, which leads me to believe there is still something else going on here. – Eric after dark Apr 02 '15 at 15:27
  • @Ericafterdark `MenuItem.IsChecked` should match whatever `Button.DataContext.AskBeforeDownloading_Checked` is. If it's not staying in sync, then I would double-check that `Button.DataContext` is equal to your `MainWindowViewModel`. I often recommend a 3rd party tool like [Snoop](https://snoopwpf.codeplex.com/) if you're having trouble debugging data bindings – Rachel Apr 02 '15 at 15:33
  • I prefer your first example as well and I agree that Snoop would be useful for this issue. However, I think it might be important to note that I don't set a `DataContext` for my `Button` in the XAML. Is this okay, or would that be the problem? In addition, would it also be a problem if my `MenuItem` was a child of another `MenuItem`? – Eric after dark Apr 02 '15 at 15:36
  • Checkout Updated 2 in my post. It looks like I overlooked something that might be affecting my program. – Eric after dark Apr 02 '15 at 15:43
  • @Ericafterdark Yes, the `DataContext` property gets inherited by child controls if it is not specifically set, so you don't need to explicitly set `Button.DataContext` if it's already set to `MainWindowViewModel` further up in the hierarchy. Also, the existence of a second `` in the tree should not affect either code example posted here :) – Rachel Apr 02 '15 at 16:11
  • In that case, it's strange that this doesn't seem to be working. What else can I look for? – Eric after dark Apr 02 '15 at 17:05
  • @Ericafterdark Are you able to see the MenuItem.DataContext in Snoop, and verify that the `AskBeforeDownloading_Checked` is set to `true`? – Rachel Apr 02 '15 at 17:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/74365/discussion-between-eric-after-dark-and-rachel). – Eric after dark Apr 02 '15 at 18:08
1

You need to add PlacementTarget.DataContext. Refer below code.

<Button Name="Button_1" >
        <Button.ContextMenu>
            <ContextMenu x:Name="MainContextMenu">
                <MenuItem IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.AskBeforeDownloading_Checked}" />
            </ContextMenu>
        </Button.ContextMenu>
    </Button>
Ayyappan Subramanian
  • 5,348
  • 1
  • 22
  • 44