2012-10-04 31 views
3

有谁知道我如何确定contextMenu何时自动调整位置,因为它靠近屏幕边缘?WPF ContextMenu放置调整事件

我的情景是,我有一个contextMenu有2个圆角和2个方角。当菜单打开时,我绕着底部2 ...如果菜单向上打开,那么我围绕顶部2.问题是,我没有找到绑定的事件或属性,告诉我菜单何时获取其方向自动改变。

这里有一些简单的示例代码来尝试。如果在窗口位于屏幕顶部时单击,则菜单关闭。如果您将窗口移动到屏幕底部,则菜单将会上升。

<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

我无法找到一个真正的WPF解决方案,但贾斯汀的评论带领我尝试将菜单的位置与PlacementTarget的位置进行比较。

第一步是订阅contextMenu.Loaded事件(在布局已处理但在屏幕上完全可见之前触发)。

<ContextMenu ContextMenu.Loaded="ContextMenu_Loaded"> 

然后,当这种情况下,我可以弄清楚菜单是否内部切换到我请求的placementMode的替代位置。如果它被颠倒过来,我会继续并相应地调整我的圆角。

注:我最初使用getWindowRect并将菜单Rect与目标的Rect进行比较,但发现Rect菜单总是返回先前实例的位置。为了避免这个问题,我现在得到相关屏幕的工作区并手动查看菜单是否合适。注意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; 
} 

最终结果:

enter image description here

2

据我所知,这是不可能的。

使用JustDecompile,我将此功能追溯到Popup类中的UpdatePosition方法。最后的位置,似乎在这里设置:

this._positionInfo.X = num4; 
    this._positionInfo.Y = num5; 
    this._secHelper.SetPopupPos(true, num4, num5, false, 0, 0); 

_secHelper是一个辅助类PopupSecurityHelper类型,似乎只是一个内部帮手......而且,所有这些结果的事件,甚至是公共财产正在改变。

Here is an MSDN article explaining how popup positioning is determined in general('当弹出窗口遇到屏幕边缘'描述你的场景)。

但是,this article解释了如何使用CustomPopupPlacementCallback来稍微覆盖这些行为。但是,这仍然使用PopupPrimaryAxis,它会根据需要翻转菜单,并且会导致相同的问题。

我能想到的唯一的另一件事是,你可以看看PlacementRectangle,也许轮询的大小和位置类似于UpdatePosition如何做或者只是检查弹出窗口本身就像UpdatePosition一样。

虽然这是一种私人方法。所以,任何你试图模仿的逻辑都会在未来的框架版本中发生变化。

UPDATE

而且,你可能尝试bastardizing PointToScreenPointFromScreen,但是这将是非常令人费解的代码,如果它的工作...

+0

感谢您的信息。它绝对有助于找出解决方案。 –