2012-10-04 3 views
3

contextMenu가 화면 가장자리에 너무 가깝게 배치되어 배치를 자동으로 조정할 때를 어떻게 알 수 있습니까?WPF ContextMenu 배치 조정 이벤트

제 시나리오는 2 개의 둥근 모서리와 2 개의 사각형 모서리가있는 contextMenu입니다. 메뉴가 열릴 때 아래쪽 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 메뉴는 항상 이전 인스턴스의 위치를 ​​반환한다는 것을 알았습니다. 이 문제를 피하기 위해 이제는 관련 화면의 workingArea를 얻고 메뉴가 적합한 지 수동으로 확인합니다.

주 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 articleCustomPopupPlacementCallback을 사용하여 이러한 동작을 다소 재정의하는 방법을 설명합니다. 그러나 이것은 여전히 ​​PopupPrimaryAxis을 사용하며, 필요에 따라 메뉴를 뒤집어야하므로 동일한 문제가 발생합니다.

내가 생각할 수있는 유일한 다른 점은 PlacementRectangle을 조사하고 크기와 위치를 조사하여 UpdatePosition과 비슷한 방식으로 조사하거나 UpdatePosition처럼 팝업 자체를 확인하는 것입니다.

이것은 개인적인 방법입니다. 따라서 향후 버전의 프레임 워크에서 모방하려고하는 모든 논리가 바뀔 수 있습니다. 또한 UPDATE

, 당신이 가능하게 PointToScreen 또는 PointFromScreen을 bastardizing 시도 할 수 있지만,이 일을하는 경우가 매우 복잡한 코드를 것 ...

+0

감사합니다. 그것은 분명히 해결책을 찾는 데 도움이되었습니다. –