문제
상황에 맞는 메뉴 항목에서 명령을 실행하는 것과 관련된 흥미로운 문제입니다...
내 컨트롤에 행을 삽입하는 명령인 InsertRowCmd를 실행하고 싶습니다.이 명령은 행을 삽입할 위치를 알아야 합니다.
Mouse.GetPosition()을 사용할 수 있지만 이렇게 하면 현재 메뉴 항목 위에 있는 마우스의 위치를 알 수 있습니다.대신 컨텍스트 메뉴의 원점을 얻고 싶습니다.
상황에 맞는 메뉴의 원본을 명령에 대한 매개 변수로 전달하는 방법에 대한 제안 사항이 있는 사람이 있습니까?
샘플 코드:
<UserControl x:Name="MyControl">
<!--...-->
<ContextMenu x:Name="menu">
<MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/>
</ContextMenu>
</UserControl>
나의 현재 아이디어는 다음과 같습니다.
- 코드에서 원점을 찾을 수 있도록 대신 클릭 핸들러를 사용하세요.문제는 활성화/비활성화를 처리해야 한다는 것입니다.
-클릭 이벤트를 처리하고 컨텍스트 메뉴의 출처를 저장합니다.이 저장된 정보를 명령에 전달하십시오.명령이 실행되기 전에 클릭 이벤트가 실행되는 것을 확인했습니다.
어떤 아이디어가 있나요?
편집하다:
저는 Josh Smith의 제품을 사용하고 있습니다. 명령SinkBinding 명령 처리를 내 ViewModel 클래스로 라우팅합니다.따라서 명령 실행을 처리하는 코드는 뷰에 대해 아무것도 모릅니다.
해결책
당신은 사용해야합니다 TranslatePoint
왼쪽 상단(0, 0)을 변환하려면 ContextMenu
포함하는 그리드의 좌표로.바인딩하면 그렇게 할 수 있습니다. CommandParameter
~로 ContextMenu
변환기를 사용하십시오.
CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}"
또 다른 접근 방식은 연결된 읽기 전용 속성 유형을 자동으로 업데이트하는 연결된 동작입니다. Point
언제든지 ContextMenu
열립니다.사용법은 다음과 같습니다.
<ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True">
<MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/>
</ContextMenu>
그래서 TrackOpenLocation
부착된 재산은 다음에 부착하는 작업을 수행합니다. ContextMenu
두 번째 연결된 속성을 업데이트합니다(OpenLocation
) 그럴 때마다 ContextMenu
열립니다.그런 다음 MenuItem
그냥 바인딩 할 수 있습니다 OpenLocation
그 위치를 알아내기 위해 ContextMenu
마지막으로 열렸습니다.
다른 팁
Kent의 답변에 이어 나는 그가 첨부한 부동산 제안을 사용하여 다음과 같이 결론을 내렸습니다(Josh Smith의 연결된 동작의 예):
public static class TrackBehavior
{
public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged));
public static bool GetTrackOpenLocation(ContextMenu item)
{
return (bool)item.GetValue(TrackOpenLocationProperty);
}
public static void SetTrackOpenLocation(ContextMenu item, bool value)
{
item.SetValue(TrackOpenLocationProperty, value);
}
public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point()));
public static Point GetOpenLocation(ContextMenu item)
{
return (Point)item.GetValue(OpenLocationProperty);
}
public static void SetOpenLocation(ContextMenu item, Point value)
{
item.SetValue(OpenLocationProperty, value);
}
static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var menu = dependencyObject as ContextMenu;
if (menu == null)
{
return;
}
if (!(e.NewValue is bool))
{
return;
}
if ((bool)e.NewValue)
{
menu.Opened += menu_Opened;
}
else
{
menu.Opened -= menu_Opened;
}
}
static void menu_Opened(object sender, RoutedEventArgs e)
{
if (!ReferenceEquals(sender, e.OriginalSource))
{
return;
}
var menu = e.OriginalSource as ContextMenu;
if (menu != null)
{
SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget));
}
}
}
그런 다음 Xaml에서 사용하려면 다음이 필요합니다.
<ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True">
<MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/>
</ContextMenu>
하지만 다음도 추가해야 했습니다.
NameScope.SetNameScope(menu, NameScope.GetNameScope(this));
내 뷰의 생성자에 연결하고, 그렇지 않으면 CommandParameter
조회할 수 없습니다 ElementName=menu
.
Kent의 답변 외에도 "표준 방식"을 생각해보십시오.F.e.ListBox에 ContextMenu가 있으면 메뉴 위치가 필요하지 않습니다. 선택한 항목은 메뉴가 나타나기 전에 설정되기 때문입니다.따라서 컨트롤에 마우스 오른쪽 버튼을 클릭하면 "선택"되는 항목이 있는 경우...