简体   繁体   中英

ElementName Binding in ContextMenu

Suppose I have a Button named " myButton ", and inside its ContextMenu (named " myContextMenu "), I have a MenuItem named "myMenuItem" .

<Button Name="myButton">
    <Button.ContextMenu>
        <ContextMenu Name="myContextMenu">
            <MenuItem Name="myMenuItem" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

I know that the ContextMenu and MenuItem are not inside the same visual tree or namescope as its PlacementTarget myButton , so by using ElementName , the binding will fail. By using NameScope.SetNameScope() , I can make the ContextMenu inside the same namescope and then make the binding successful.

<!-- this will succeed only if setting the namescope in code-behind -->
<MenuItem Name="myMenuItem" Command="..." CommandParameter="{Binding ElementName=myButton}" />

However, without setting the namescope in code-behind, the Button can access both the ContextMenu and MenuItem .

<!-- this will succeed no matter the namescope is set in code-behind -->
<Button Name="myButton" Command="..." CommandParameter="{Binding ElementName=myContextMenu}">

Furthermore, I also noticed that the MenuItem cannot even access itself or its parent ContextMenu by using ElementName . But by using RelativeSource Self , the binding will succeed.

<!-- both will fail unless setting namescope in code behind -->
<MenuItem Name="myMenuItem" Command="..." CommandParameter="{Binding ElementName=myContextMenu}" />
<MenuItem Name="myMenuItem" Command="..." CommandParameter="{Binding ElementName=myMenuItem}" />
<!-- succeed even without setting namescope -->
<MenuItem Name="myMenuItem" Command="..." CommandParameter="{Binding RelativeSource={RelativeSource Self}}" />

So my questions are:

  1. Why Button can access "myContextMenu" by ElementName=myContextMenu even without setting the namescope of the ContextMenu ?
  2. Why MenuItem cannot access itself by ElementName=myMenuItem unless setting the namescope?
  3. Why RelativeSource Self works regardless of the namescope or visual tree?

1. Why Button can access myContextMenu by ElementName=myContextMenu even without setting the namescope of the MenuContext?

A ContextMenu element defines what the context menu looks like and how it behaves.

The properties of the ContextMenu class are used to define the position and behavior of the ContextMenu . [...]

If you assign a ContextMenu to the FrameworkElement.ContextMenu or FrameworkContentElement.ContextMenu property, the ContextMenuService class handles context menu operations in response to user interaction. [...]

A ContextMenu is automatically placed inside a Popup control.

The ContextMenu is assigned to the ContextMenu property of Button and it still exists there throughout the runtime with all the menu items that you created. It lives within the same namescope as the Button , because namescopes are created on load when XAML is parsed and at this point it is part of the Button .

In WPF, XAML namescopes are created on the root element for a XAML page when the page is loaded. Each name specified within the XAML page starting at the page root is added to a pertinent XAML namescope.

Consequently, a Binding with ElementName resolves successfully as it operates in a single namescope.

2. Why MenuItem cannot access itself by ElementName=myMenuItem unless setting the namescope?

As you can see, the ContextMenu is within the same namescope as Button . When opening it, the ContextMenu is still assinged to the ContextMenu property of Button , but a new Popup that hosts it is assigned as its parent. This also means that it resides in a different visual tree. Nevertheless, it is the same instance that the Button refers to. This is very important, since namescopes are not automatically reassigned after the initial assignment.

The moment that XAML is parsed represents the moment in time that a WPF XAML namescope is created and defined. If you add an object to an object tree at a point in time after the XAML that produced that tree was parsed, a Name or x:Name value on the new object does not automatically update the information in a XAML namescope.

In summary, the ContextMenu is only parented to a Popup , but it is still in the same namescope as the Button . This can only be changed manually, by adding it to the target namespace in code.

To add a name for an object into a WPF XAML namescope after XAML is loaded, you must call the appropriate implementation of RegisterName on the object that defines the XAML namescope, which is typically the XAML page root. If the name is not registered, the added object cannot be referenced by name through methods such as FindName, and you cannot use that name for animation targeting.

3. Why RelativeSource Self works regardless of the namescope or visual tree?

A RelativeSource does not use names to resolve elements, therefore it is independent of the actual namescope, but it depends on the visual tree.

Implements a markup extension that describes the location of the binding source relative to the position of the binding target.

In the concrete case of the ContextMenu , obviously the RelativeSource binding on MenuItem can resolve itself, as well as the ContextMenu , since they are both part of the same visual tree. However, it cannot resolve Button , as the root of the ContextMenu at runtime is the Popup that was assigned as its parent.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM