如何在此代码中将 contextMenu 置于按钮下方?
How do I place contextMenu under button in this code?
我想要像 AX 中那样的按钮,其中一些按钮只会打开一个菜单,下面有更多菜单项。我找到了下面的代码并且工作正常。但是当我左键单击按钮时,它会显示鼠标指针所在的上下文菜单。当我右键单击该按钮时,它会在按钮正下方很好地显示上下文菜单。我希望左键单击像右键单击一样工作。
问题是 ContextMenuService.Placement="Bottom"
只有在我右键单击时才有效。
<Button Name="MainButton" Content="Button with ContextMenu" Width="150" Height="30" ContextMenuService.Placement="Bottom">
<Button.ContextMenu>
<ContextMenu x:Name="MainContextMenu" PlacementRectangle="{Binding RelativeSource={RelativeSource self}}">
<MenuItem Header="Do A" />
<MenuItem Header="Do B" />
<MenuItem Header="Do C" />
</ContextMenu>
</Button.ContextMenu>
<Button.Triggers>
<EventTrigger SourceName="MainButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainContextMenu" 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>
</Button>
这应该可以解决问题,而不是使用 Button
<Menu>
<MenuItem Header="Options">
<MenuItem Header="Do A"/>
<MenuItem Header="Do B"/>
<MenuItem Header="Do C"/>
</MenuItem>
</Menu>
使用 Storyboard
设置单个 属性 值非常奇怪。这可行,但看起来有点难看。
但这也是您的 ContextMenuService.Placement="Bottom"
设置不起作用的原因:您只是不调用 ContextMenuService
通过 Storyboard
打开上下文菜单,所以PlacementTarget
不会设置为您的按钮。
我建议您创建一个简单的附件 属性 并在您的视图中重新使用它:
static class ContextMenuTools
{
public static readonly DependencyProperty OpenOnLeftClickProperty =
DependencyProperty.RegisterAttached(
"OpenOnLeftClick",
typeof(bool),
typeof(ContextMenuTools),
new PropertyMetadata(false, OpenOnLeftClickChanged));
public static void SetOpenOnLeftClick(UIElement element, bool value)
=> element.SetValue(OpenOnLeftClickProperty, value);
public static bool GetOpenOnLeftClick(UIElement element)
=> (bool)element.GetValue(OpenOnLeftClickProperty);
private static void OpenOnLeftClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is IInputElement element && (bool)e.NewValue)
{
element.PreviewMouseLeftButtonDown += ElementOnMouseLeftButtonDown;
}
}
private static void ElementOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is UIElement element
&& ContextMenuService.GetContextMenu(element) is ContextMenu contextMenu)
{
contextMenu.Placement = ContextMenuService.GetPlacement(element);
contextMenu.PlacementTarget = element;
contextMenu.IsOpen = true;
}
}
}
用法可能如下所示:
<Button Content="Button with ContextMenu" ContextMenuService.Placement="Bottom"
yourNS:ContextMenuTools.OpenOnLeftClick="True">
<Button.ContextMenu>
<ContextMenu x:Name="MainContextMenu">
<MenuItem Header="Do A" />
</ContextMenu>
</Button.ContextMenu>
</Button>
注意 yourNS
命名空间 - 这是一个 XAML 命名空间映射到附加的 属性 class ContextMenuTools
所在的 CLR 命名空间,例如:
xmlns:yourNS="clr-namespace:WpfApp1"
您还可以在实现 IInputElement
的任何 WPF 控件上使用附加的 属性,而不仅仅是按钮。
我想要像 AX 中那样的按钮,其中一些按钮只会打开一个菜单,下面有更多菜单项。我找到了下面的代码并且工作正常。但是当我左键单击按钮时,它会显示鼠标指针所在的上下文菜单。当我右键单击该按钮时,它会在按钮正下方很好地显示上下文菜单。我希望左键单击像右键单击一样工作。
问题是 ContextMenuService.Placement="Bottom"
只有在我右键单击时才有效。
<Button Name="MainButton" Content="Button with ContextMenu" Width="150" Height="30" ContextMenuService.Placement="Bottom">
<Button.ContextMenu>
<ContextMenu x:Name="MainContextMenu" PlacementRectangle="{Binding RelativeSource={RelativeSource self}}">
<MenuItem Header="Do A" />
<MenuItem Header="Do B" />
<MenuItem Header="Do C" />
</ContextMenu>
</Button.ContextMenu>
<Button.Triggers>
<EventTrigger SourceName="MainButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainContextMenu" 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>
</Button>
这应该可以解决问题,而不是使用 Button
<Menu>
<MenuItem Header="Options">
<MenuItem Header="Do A"/>
<MenuItem Header="Do B"/>
<MenuItem Header="Do C"/>
</MenuItem>
</Menu>
使用 Storyboard
设置单个 属性 值非常奇怪。这可行,但看起来有点难看。
但这也是您的 ContextMenuService.Placement="Bottom"
设置不起作用的原因:您只是不调用 ContextMenuService
通过 Storyboard
打开上下文菜单,所以PlacementTarget
不会设置为您的按钮。
我建议您创建一个简单的附件 属性 并在您的视图中重新使用它:
static class ContextMenuTools
{
public static readonly DependencyProperty OpenOnLeftClickProperty =
DependencyProperty.RegisterAttached(
"OpenOnLeftClick",
typeof(bool),
typeof(ContextMenuTools),
new PropertyMetadata(false, OpenOnLeftClickChanged));
public static void SetOpenOnLeftClick(UIElement element, bool value)
=> element.SetValue(OpenOnLeftClickProperty, value);
public static bool GetOpenOnLeftClick(UIElement element)
=> (bool)element.GetValue(OpenOnLeftClickProperty);
private static void OpenOnLeftClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is IInputElement element && (bool)e.NewValue)
{
element.PreviewMouseLeftButtonDown += ElementOnMouseLeftButtonDown;
}
}
private static void ElementOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is UIElement element
&& ContextMenuService.GetContextMenu(element) is ContextMenu contextMenu)
{
contextMenu.Placement = ContextMenuService.GetPlacement(element);
contextMenu.PlacementTarget = element;
contextMenu.IsOpen = true;
}
}
}
用法可能如下所示:
<Button Content="Button with ContextMenu" ContextMenuService.Placement="Bottom"
yourNS:ContextMenuTools.OpenOnLeftClick="True">
<Button.ContextMenu>
<ContextMenu x:Name="MainContextMenu">
<MenuItem Header="Do A" />
</ContextMenu>
</Button.ContextMenu>
</Button>
注意 yourNS
命名空间 - 这是一个 XAML 命名空间映射到附加的 属性 class ContextMenuTools
所在的 CLR 命名空间,例如:
xmlns:yourNS="clr-namespace:WpfApp1"
您还可以在实现 IInputElement
的任何 WPF 控件上使用附加的 属性,而不仅仅是按钮。