1
<Button Content="{Binding Type}" Name="Ellipsis" Tag="{Binding ElementName=Ellipsis, Path=DataContext}">
    <Button.ContextMenu>
          <ContextMenu x:Name="MainContextMenu" DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
              <MenuItem Header="{Binding Type}"/>
          </ContextMenu>
    </Button.ContextMenu>
    <Button.Triggers>
          <EventTrigger SourceName="Ellipsis" RoutedEvent="Button.Click">
              <BeginStoryboard>
                  <Storyboard>
                      <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainContextMenu" Storyboard.TargetProperty="(ContextMenu.IsOpen)">
                         <DiscreteObjectKeyFrame KeyTime="0:0:0">
                             <DiscreteObjectKeyFrame.Value>
                                 <sys:Boolean>True</sys:Boolean>
                             </DiscreteObjectKeyFrame.Value>
                         </DiscreteObjectKeyFrame>
                      </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
              </BeginStoryboard>
          </EventTrigger>
     </Button.Triggers>
</Button>

As shown above, Button.Content="{Binding Type}" works perfectly but when it goes in its ContextMenu, the DataContext changes which causing MenuItem Header="{Binding Type}" to not work. I researched online and someone said saving the outer DataContext in a tag and use it as the inner DataContext. I tried that in my code but ContextMenu still not reading the correct DataContext. The MenuItem Header should be the same as the Button.Content in this case but it is not. What should I do?

Jtastic
  • 7
  • 5
  • Shouldn't it be `Tag="{Binding ElementName=Ellipsis, Path=DataContext}"`? To match the `Name` of your button? – Emperor Eto Jan 26 '23 at 23:52
  • @PeterMoore Thanks for your reply! It was a typo on the ElementName though I already tried it with the correct name and it still didn't do what I expect it to do. I just tried using ``` – Jtastic Jan 27 '23 at 00:26
  • Ah, well the ContextMenu is technically not a descendant of the Button because it's a Popup and thus a totally new Window technically. And you're right the suggestion in my second comment won't work. I tried this out, and if you RIGHT click it seems to work. But if you LEFT click it doesn't. So when it opens with your trigger it doesn't get the data context, but when you open it with a right click like a normal context menu it does. Hopefully that sets you on the right track. I'm scratching my own head right now. – Emperor Eto Jan 27 '23 at 01:15
  • Does this answer your question? [Show ContextMenu on Left Click using only XAML](https://stackoverflow.com/questions/555252/show-contextmenu-on-left-click-using-only-xaml) – Emperor Eto Jan 27 '23 at 13:15
  • Also if I'm not mistaken this is the exact same issue: https://stackoverflow.com/questions/59584206/wpf-contextmenu-loses-datacontext-if-it-is-displayed-using-a-left-click-event – Emperor Eto Jan 27 '23 at 16:19

2 Answers2

0
<Button Content="{Binding Type}" x:Name="EllipsisButton">
    <Button.ContextMenu>
          <ContextMenu x:Name="MainContextMenu">
              <MenuItem Header="{Binding DataContext.Type,ElementName=EllipsisButton}"/>
          </ContextMenu>
    </Button.ContextMenu>
</Button>

One solution I have is to use ElementName. You can refer to an upper level with its x:Name. In this case, I am binding to Type from whichever component that has x:Name="EllipsisButton"

Jtastic
  • 7
  • 5
-1

To answer the datacontext part of the question.

One way would be to ensure your contextmenu would have the same datacontext as a button would be to define the contextmenu as a resource.

<Grid.Resources>
     <ContextMenu x:Key="MainContextMenu">
          <MenuItem Header="{Binding Type}"/>
      </ContextMenu>
</Grid.Resources>
<Button ContextMenu="{StaticResource MainContextMenu}"

Because the contextmenu is instantiated in the grid, it inherits the grid's datacontext.

The left click will not work without code though. Not with a button.

Not sure why you'd want left click to show a context menu but maybe you could consider a popup instead of contextmenu.

You could animate isopen using a booleananimation

            <BooleanAnimationUsingKeyFrames Storyboard.TargetName="MyPopup" Storyboard.TargetProperty="(Popup.IsOpen)">
              <DiscreteBooleanKeyFrame Value="True" />
            </BooleanAnimationUsingKeyFrames>

Or you could instead use a togglebutton which has an ischecked boolean:

        <ToggleButton Content="{Binding Type}" 
                      x:Name="Ellipsis" />
        <Popup IsOpen="{Binding IsChecked, ElementName=Ellipsis, Mode=TwoWay}" 
               PlacementTarget="{Binding ElementName=Ellipsis}"
               DataContext="{Binding DataContext, ElementName=Ellipsis}" 
               StaysOpen="False">
            <ListBox>
                <MenuItem Header="{Binding Type}"/>
            </ListBox>
        </Popup>

OK, probably needs a bit more work but my popup shows up and has the correct datacontext:

enter image description here

Andy
  • 11,864
  • 2
  • 17
  • 20
  • The context menu DOES already get the DataContext of the button when it's opened through right click. He actually doesn't even need the Key or the RelativeSource binding to get that. Meanwhile putting the ContextMenu it in a resource makes it inaccessible to his trigger. – Emperor Eto Jan 27 '23 at 13:14
  • I think the second half of this might be a good answer to https://stackoverflow.com/questions/555252/show-contextmenu-on-left-click-using-only-xaml/29123964#29123964 though. – Emperor Eto Jan 27 '23 at 13:18
  • You can set the contextmenu to a {StaticResourse MainContextMenu} @Peter Moore Putting it a resource does not make it inaccessible to a trigger. – Andy Jan 27 '23 at 13:21
  • Did you try it? I get "System.InvalidOperationException: ''MainContextMenu' name cannot be found in the name scope of 'System.Windows.Controls.Button'.'" The problem is `Storyboard.TargetName` can no longer find `MainContextMenu`. – Emperor Eto Jan 27 '23 at 13:23
  • Note also, the question isn't really what his problem is, but someone searching will find based on that. – Andy Jan 27 '23 at 13:23
  • Yes I did try it. – Andy Jan 27 '23 at 13:23
  • Perhaps you can post the complete code, because I cannot get the Storyboard to successfully reference a ContextMenu in the resources. – Emperor Eto Jan 27 '23 at 13:27
  • Ah sorry. I think I was not clear. The button references the resource. The storyboard just sets isopen on whatever is there. – Andy Jan 27 '23 at 13:28
  • Oh.... The code doing that is already there though @Peter Moore – Andy Jan 27 '23 at 13:29
  • `Storyboard.TargetName="MainContextMenu"` throws an exception. How would the StoryBoard reference the ContextMenu if it's in a resource? – Emperor Eto Jan 27 '23 at 13:30
  • Remember that this does not actually work 100% with a contextmenu. But not because of any referencing of isopen or resources. The animation can set a property on it's owner - the button. Doesn't actually need a name. Similar to the markup for the popup. It kind of works if you right click first. – Andy Jan 27 '23 at 13:36