WPF ContextMenu loses datacontext if it is displayed using a left-click event
我有一个按钮,我想在用户左键单击它时显示一个弹出菜单。我已将弹出窗口实现为按钮的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <Button Name="AddRangeBtn" Height="50" Width="50" Margin="5" **Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}">** <Button.Content> <Image Source="{StaticResource DmxPlusIconImage}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="50" MaxWidth="50" Margin="-10" /> </Button.Content> <Button.ToolTip> <ToolTip Style="{DynamicResource DMXToolTipStyle}" Content="{x:Static Localization:StringResources.AddFrequencyRangeTooltip}"/> </Button.ToolTip> <Button.ContextMenu> **<ContextMenu Name="AddRngContextMenu" DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">** <MenuItem Style="{StaticResource localMenuItem}" IsEnabled="{Binding CanAddLinearRange}"> <MenuItem.ToolTip> <ToolTip Style="{DynamicResource DMXToolTipStyle}" Content="{x:Static Localization:StringResources.ToolTip_AddLinearFrequencyRange}"/> |
设置datacontext的技巧是将contextmenu的datacontext绑定到contextmenu中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <Button.Triggers> <EventTrigger SourceName="AddRangeBtn" RoutedEvent="Button.Click"> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="AddRngContextMenu" Storyboard.TargetProperty="(ContextMenu.IsOpen)"> <DiscreteObjectKeyFrame KeyTime="0:0:0"> <DiscreteObjectKeyFrame.Value> <system:Boolean>True</system:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers> |
这样,右键单击按钮会显示上下文菜单,但该上下文菜单的实例似乎不会获得与右键单击显示的实例相同的数据上下文!! !在左键版本中,菜单中的所有项目都已启用,因此很明显,上下文菜单没有从视图模型中看到依赖项属性。我不知道这是怎么可能的,似乎有两个不同版本的上下文菜单,一个具有正确的数据上下文,一个具有默认行为。
右键单击启动时的上下文菜单:
右键单击启动时的上下文菜单:
我正在使用 XAML 来创建左键单击行为,并且在我见过的一些示例中,人们使用代码隐藏来实现左键单击上下文菜单,他们将上下文菜单的数据上下文设置为button.click 事件处理程序中的 button.datacontext。我猜这也是我需要做的,但我不确定如何在 xaml 中做到这一点。
任何想法都将受到高度赞赏。
感谢@KeithStein 让我走上了正确的道路。我在项目中添加了以下附加行为类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | public class OpenContextMenuOnLeftClickBehavior : DependencyObject { public static bool GetIsEnabled(DependencyObject obj) { return (bool)obj.GetValue(IsEnabledProperty); } public static void SetIsEnabled(DependencyObject obj, bool value) { obj.SetValue(IsEnabledProperty, value); } /// <summary> /// The DependencyProperty that backs the IsEnabled property. /// </summary> public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached( "IsEnabled", typeof(bool), typeof(OpenContextMenuOnLeftClickBehavior), new FrameworkPropertyMetadata(false, IsEnabledProperty_Changed)); /// <summary> /// The property changed callback for the IsEnabled dependency property. /// </summary> /// <param name="dpobj"> /// The dependency object whose property changed. /// </param> /// <param name="args"> /// The event arguments. /// </param> private static void IsEnabledProperty_Changed(DependencyObject dpobj, DependencyPropertyChangedEventArgs args) { FrameworkElement f = dpobj as FrameworkElement; if (f != null) { bool newValue = (bool)args.NewValue; if (newValue) f.PreviewMouseLeftButtonUp += Target_PreviewMouseLeftButtonUpEvent; else f.PreviewMouseLeftButtonUp -= Target_PreviewMouseLeftButtonUpEvent; } } protected static void Target_PreviewMouseLeftButtonUpEvent(object sender, RoutedEventArgs e) { FrameworkElement f = sender as FrameworkElement; if (f?.ContextMenu != null) { f.ContextMenu.PlacementTarget = f; f.ContextMenu.IsOpen = true; } e.Handled = true; } } |
然后我在 xaml 中为具有
的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <Button Name="AddRangeBtn" Height="50" Width="50" Margin="5" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}" controls:OpenContextMenuOnLeftClickBehavior.IsEnabled="True"> <Button.Content> <Image Source="{StaticResource DmxPlusIconImage}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="50" MaxWidth="50" Margin="-10" /> </Button.Content> <Button.ToolTip> <ToolTip Style="{DynamicResource DMXToolTipStyle}" Content="{x:Static Localization:StringResources.AddFrequencyRangeTooltip}"/> </Button.ToolTip> <Button.ContextMenu> <ContextMenu Name="AddRngContextMenu" DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"> <MenuItem Style="{StaticResource localMenuItem}" IsEnabled="{Binding CanAddLinearRange}"> <MenuItem.ToolTip> <ToolTip Style="{DynamicResource DMXToolTipStyle}" Content="{x:Static Localization:StringResources.ToolTip_AddLinearFrequencyRange}"/> </MenuItem.ToolTip> <MenuItem.Header> <Button> <Image Source="{StaticResource DMXAddLinFreqRngTypeIconImage}" MinHeight="37" MinWidth="37"/> </Button> </MenuItem.Header> </MenuItem> |
这使得左键单击"上下文菜单"的行为与右键单击版本一样。
我还没有找到只使用 XAML 的方法,但我使用了附加的行为来帮助保持干净。
问题在于,很明显,
我使用的解决方案涉及一个带有附加依赖属性
1 2 3 4 5 6 7 8 9 10 | protected static void Target_PreviewMouseLeftButtonUpEvent(object sender, RoutedEventArgs e) { FrameworkElement f = sender; if (f.ContextMenu != null) { f.ContextMenu.PlacementTarget = f; f.ContextMenu.IsOpen = true; } e.Handled = true; } |