2

I can't figure out where I'm going wrong here. Hopefully one of you can help.

I have a Window that contains a TabControl. The TabControl has a ContextMenu with a checkable item for "always-on-top" behavior. I want to bind this checkable item to the containing Window's boolean Topmost property.

No matter what I do, the binding fails and I get a "Cannot find source for binding" error in my debug output.

(These excerpts are greatly simplified from my actual code. I hope I didn't accidentally cut out anything relevant.)

First I tried this:

<Window x:Name="myWindow" (blah blah other properties)>
    <TabControl x:Name="tabControl">
        <TabControl.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Always on Top" IsCheckable="True"
                          IsChecked="{Binding ElementName=myWindow,
                                              Path=Topmost,
                                              Mode=TwoWay}"/>
            </ContextMenu>
        ...

That didn't work. Is it because the MenuItem is inside of the Window "myWindow"? Do I need to use a RelativeSource Ancestor binding?

So I tried this:

<Window x:Name="myWindow" (blah blah other properties)>
    <TabControl x:Name="tabControl">
        <TabControl.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Always on Top" IsCheckable="True"
                          IsChecked="{Binding RelativeSource={
                                                  RelativeSource FindAncestor,
                                                  AncestorType={x:Type Window}
                                              },
                                              Path=Topmost,
                                              Mode=TwoWay}"/>
            </ContextMenu>
        ...

That didn't work either.

So now I'm stuck. How do I make this binding work?

Note: My code-behind is not doing anything with these elements. Do I need to set Window.DataContext or something? That could possibly break other parts of this window.

H.B.
  • 166,899
  • 29
  • 327
  • 400
Grant Birchmeier
  • 17,809
  • 11
  • 63
  • 98

2 Answers2

2

Yes, whatever object your "TopMost" property is on will have to be set as the DataContext of your window. If its set as the DataContext of your Window then your control should be able to pickup the property from the ElementName binding you tried in your first example.

Your view will look at its DataContext for a property named "TopMost"

KodeKreachor
  • 8,852
  • 10
  • 47
  • 64
  • What is the relationship between DataContext and ElementName? Seems like they both do the same thing, no? – Grant Birchmeier Apr 05 '12 at 22:12
  • DataContext is the object that represents the backing properties of a view. Any simple binding expressions will look on its DataContext for the property its bound to. ElementName binding means that you're binding the attribute of an element to an attribute of another element (like binding the text attribute of a textbox to the content attribute of a button). – KodeKreachor Apr 05 '12 at 22:20
  • I still don't follow. I **am** "binding the attribute of an element to the attribute of another element" (that is, I'm binding MenuItem.IsChecked to myWindow.Topmost). What purpose does DataContext serve? Why is it looking anywhere other than the explicit ElementName I've given it? – Grant Birchmeier Apr 05 '12 at 22:29
  • Ok, think about the relation that the TopMost property has to the Window. If the DataContext of your Window is not set, then it has no relation to the TopMost property. Trust me, if you instantiate an object that has your TopMost property on it, then set the DataContext of your Window to that object, your ElementName binding will work because then you'll have created the relationship between the TopMost property and the Window view. – KodeKreachor Apr 05 '12 at 22:36
  • I deleted ElementName from the binding completely, and set DataContext=this in codebehind... and it worked! I guess what you were trying to say is that *all binding is relative to the DataContext* (unless you do RelativeSource or something). Is that about right? – Grant Birchmeier Apr 05 '12 at 22:51
  • All binding expressions will look for either a property on the control's DataContext or for a matching dependency property attribute on the target element if you're using ElementName binding. If you're using ElementName binding then the binding's source element effectively becomes the binding expression's DataContext. Does that make sense? :) – KodeKreachor Apr 05 '12 at 22:57
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/9751/discussion-between-grant-birchmeier-and-kodekreachor) – Grant Birchmeier Apr 05 '12 at 23:00
1

As far as I remember Menu is drawn in a popup which is not actually a part of the visual tree of the Window. So it's better to try to use MVVM here and set IsChecked through the view model.

Sergei B.
  • 3,227
  • 19
  • 18