WPF上下文菜单放置调整事件

3

有人知道如何确定右键菜单(ContextMenu)因过于靠近屏幕边缘而自动调整位置的时间吗?

我的情况是,我有一个右键菜单,有两个圆角和两个直角。当菜单向下打开时,我会把底部两个角变成圆弧,如果菜单向上打开,则将顶部两个角变成圆弧。问题是,我没有找到可以绑定到的事件或属性,告诉我当菜单自动更改方向时。

以下是一些简化的示例代码。如果您在屏幕顶部单击,则菜单向下弹出。如果您将窗口移动到屏幕底部,则菜单将向上弹出。

<Window x:Class="menuRedirection.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="100" Width="200">
  <DockPanel Name="panel" ContextMenuOpening="DockPanel_ContextMenuOpening">
    <DockPanel.ContextMenu>
      <ContextMenu>
        <MenuItem Header="item"/>
        <MenuItem Header="item"/>
        <MenuItem Header="item"/>
        <MenuItem Header="item"/>
      </ContextMenu>
    </DockPanel.ContextMenu>
    <Rectangle DockPanel.Dock="Bottom" Name="menuTarget" Fill="Red" Height="10"/>
    <TextBlock DockPanel.Dock="Top" Text="right click for context menu"/>
  </DockPanel>
</Window>

private void DockPanel_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
  ContextMenuService.SetPlacement(panel, PlacementMode.Bottom);
  ContextMenuService.SetPlacementTarget(panel, menuTarget);
}

这是真实应用程序的样子,你可以看到我的问题是需要知道如何调整圆角。

enter image description here

2个回答

2
据我所知,这是不可能的。
使用 JustDecompile 工具,我追踪到该功能在 Popup 类的 UpdatePosition 方法中。最终位置似乎是在这里设定的:
    this._positionInfo.X = num4;
    this._positionInfo.Y = num5;
    this._secHelper.SetPopupPos(true, num4, num5, false, 0, 0);

_secHelper是一个类型为PopupSecurityHelper的辅助类,似乎只是一个内部帮助程序...而且,这些都不会导致事件甚至公共属性的更改。

这是一篇MSDN文章,解释了弹出窗口如何确定位置(“当弹出窗口遇到屏幕边缘”描述了您的情况)。

然而,这篇文章解释了如何使用CustomPopupPlacementCallback在某种程度上覆盖这些行为。但是,这仍然使用PopupPrimaryAxis,应该根据需要翻转菜单,并导致相同的问题。

我能想到的唯一其他事情是,您可以查看PlacementRectangle,并可能类似于UpdatePosition进行大小和位置轮询...或者像UpdatePosition一样检查弹出窗口本身。

不过,这是一个私有方法。因此,您尝试模拟的任何逻辑都可能在将来的框架版本中更改。

更新

另外,您可能可以尝试滥用PointToScreenPointFromScreen,但如果工作的话,这将是非常复杂的代码...


感谢提供的信息。它对找出解决方案有很大帮助。 - Bill Tarbell

2

我找不到一个真正的WPF解决方案,但Justin的评论让我尝试比较菜单位置和PlacementTarget的位置。

第一步是订阅contextMenu.Loaded事件(这会在布局处理完毕后但在完全显示在屏幕上之前触发)。

<ContextMenu ContextMenu.Loaded="ContextMenu_Loaded">

然后当触发事件时,我可以确定菜单是否内部切换到我的请求放置模式的备用位置。如果反转了,那么我会相应地调整我的圆角。

注意:最初我使用了getWindowRect并将菜单矩形与目标矩形进行比较,但发现菜单矩形始终返回先前实例的位置。为避免这个问题,我现在获取相关屏幕的工作区并手动检查菜单是否适合。

注意2:请确保您的菜单模板在倒置和正常显示时具有相同的窗口高度。否则,由于getWindowRect返回上一个菜单的大小,您的计算可能会出错。

void ContextMenu_Loaded(object sender, RoutedEventArgs e)
{
  bool reversed = isMenuDirectionReversed(this.ContextMenu);

  //existing styles are read-only so we have to make a clone to change a property
  if (reversed)
  {//round the top corners if the menu is travelling upward
    Style newStyle = new Style(typeof(ContextMenu), this.ContextMenu.Style);
    newStyle.Setters.Add(new Setter { Property = Border.CornerRadiusProperty, Value = new CornerRadius(10, 10, 0, 0) });
    this.ContextMenu.Style = newStyle;
  }
  else
  { //since we may have overwritten the style in a previous evaluation, 
    //we also need to set the downward corners again    
    Style newStyle = new Style(typeof(ContextMenu), this.ContextMenu.Style);
    newStyle.Setters.Add(new Setter { Property = Border.CornerRadiusProperty, Value = new CornerRadius(0, 0, 10, 10) });
    this.ContextMenu.Style = newStyle;
  }
}

评估方法:
private bool isMenuDirectionReversed(ContextMenu menu)
{
  //get the window handles for the popup' placement target
  IntPtr targetHwnd = (HwndSource.FromVisual(menu.PlacementTarget) as HwndSource).Handle;

  //get the relevant screen
  winFormsScreen screen = winFormsScreen.FromHandle(targetHwnd);

  //get the actual point on screen (workingarea not taken into account)
  FrameworkElement targetCtrl = menu.PlacementTarget as FrameworkElement;
  Point targetLoc = targetCtrl.PointToScreen(new Point(0, 0));

  //compute the location for the bottom of the target control
  double targetBottom = targetLoc.Y + targetCtrl.ActualHeight;

  if (menu.Placement != PlacementMode.Bottom)
    throw new NotImplementedException("you need to implement your own logic for other modes");

  return screen.WorkingArea.Bottom < targetBottom + menu.ActualHeight;
}

最终结果为:

这里输入图片描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接