Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

wpf - ContextMenu on button click not firing command

Here is a problem.

I am displaying context menu on a button click and the menu command is bind to ICommand in the view model. Menu is displaying on the button click as well as on the right click. The problem is menu click is not firing when I click button and then click context menu, but I can confirm that menu is working when I right click on button and then click on menu.

 <Button Grid.Row="3" Width="500" Height="30" Name="cmButton"  >
    Button with Context Menu
    <Button.ContextMenu>
        <ContextMenu DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Mode=Self}}"  >
            <MenuItem  DataContext="{Binding}" Header="New Layout Element..." Command="{Binding Path=SubmitBtn}" />                  
        </ContextMenu>
    </Button.ContextMenu>
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <EventTrigger RoutedEvent="Click">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
                                    <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
                                </BooleanAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>                    
        </Style>
    </Button.Style>
 </Button>

I can confirm there is nothing wrong in my view model because command is firing when I do right click on button then click on context menu.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

PlacementTarget is null when you manually set ContextMenu.IsOpen property because it is set to actual value only once it's open by right clicking on target control. (PopUpService class is responsible for setting this value to actual target).

Since PlacementTarget is null in case when you open it via Storyboard, binding is not able to resolve actual command it's binded to.

So, issue is you need to pass on the DataContext of Button to the MenuItem so that binding can be resolved. (MenuItem are not is same visual tree as that of button). That can be achieved via two ways:


Using x:Reference (available in WPF 4.0 and higher) but you need to declare dummy control so that it can be referenced to get DataContext with Visibility set to Collapsed.

<FrameworkElement x:Name="dummyControl" Visibility="Collapsed"/>
   <Button Width="100" Height="30" Name="cmButton">
      <Button.ContextMenu>
         <ContextMenu>
           <MenuItem Header="New Layout Element..."
                     Command="{Binding Path=DataContext.SubmitBtn,
                                       Source={x:Reference dummyControl}}" />
         </ContextMenu>
      </Button.ContextMenu>
   </Button>

Another interesting thing is Freezable objects inherit DataContext even if they don't lie in VisualTree so we can use this feature to overcome situations where we need to inherit DataContext.

First we need to create class inheriting from Freezable and exposing DP which can be bind to:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
     DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy));
}

Now we can use it in XAML like this:

<Button Width="100" Height="30" Name="cmButton">
    <Button.Resources>
        <local:BindingProxy x:Key="proxy" Data="{Binding}"/>
    </Button.Resources>
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="New Layout Element..."
                      Command="{Binding Path=Data.SubmitBtn,
                                        Source={StaticResource proxy}}" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...