ContextMenu의 MenuItem에서 ElementName 바인딩
다른 사람이 ElementName을 사용하는 바인딩이 MenuItem
개체 내에 포함 된 개체에 대해 올바르게 확인되지 않는다는 사실을 알고 ContextMenu
있습니까? 이 샘플을 확인하십시오.
<Window x:Class="EmptyWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
x:Name="window">
<Grid x:Name="grid" Background="Wheat">
<Grid.ContextMenu>
<ContextMenu x:Name="menu">
<MenuItem x:Name="menuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/>
<MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/>
<MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/>
<MenuItem Header="Menu Item" Tag="{Binding ElementName=menuItem}" Click="MenuItem_Click"/>
</ContextMenu>
</Grid.ContextMenu>
<Button Content="Menu"
HorizontalAlignment="Center" VerticalAlignment="Center"
Click="MenuItem_Click" Tag="{Binding ElementName=menu}"/>
<Menu HorizontalAlignment="Center" VerticalAlignment="Bottom">
<MenuItem x:Name="anotherMenuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/>
<MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/>
<MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/>
<MenuItem Header="Menu Item" Tag="{Binding ElementName=anotherMenuItem}" Click="MenuItem_Click"/>
</Menu>
</Grid>
</Window>
모든 바인딩은 ContextMenu에 포함 된 바인딩을 제외하고 훌륭하게 작동합니다. 런타임 중에 출력 창에 오류를 인쇄합니다.
해결 방법을 아는 사람이 있습니까? 여기서 무슨 일이 일어나고 있습니까?
훨씬 더 간단한 해결책을 찾았습니다.
UserControl에 대한 코드 숨김 :
NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this));
다른 사람들이 말했듯이 'ContextMenu'는 시각적 트리에 포함되어 있지 않으며 'ElementName'바인딩이 작동하지 않습니다. 허용 된 답변에서 제안한대로 컨텍스트 메뉴의 'NameScope'설정은 컨텍스트 메뉴가 'DataTemplate'에 정의되지 않은 경우에만 작동합니다. 'ElementName'바인딩과 비슷하지만 시각적 트리를 우회하여 바인딩을 다르게 해결하는 {x : Reference} Markup-Extension 을 사용하여이 문제를 해결했습니다 . 나는 이것이 'PlacementTarget'을 사용하는 것보다 훨씬 더 읽기 쉽다고 생각합니다. 다음은 그 예입니다.
<Image Source="{Binding Image}">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding Source={x:Reference Name=Root}, Path=DataContext.RemoveImage}"
CommandParameter="{Binding}" />
</ContextMenu>
</Image.ContextMenu>
</Image>
MSDN 문서에 따르면
x : Reference는 XAML 2009에 정의 된 구문입니다. WPF에서는 XAML 2009 기능을 사용할 수 있지만 WPF 태그 컴파일되지 않은 XAML에만 사용할 수 있습니다. 마크 업 컴파일 된 XAML 및 BAML 형식의 XAML은 현재 XAML 2009 언어 키워드 및 기능을 지원하지 않습니다.
그게 무슨 뜻이든간에 .. 그래도 괜찮아요.
여기에 또 다른 xaml 전용 해결 방법이 있습니다. (이것은 또한 DataContext 내부에있는 것을 원한다고 가정합니다 ( 예 : MVVMing ).)
옵션 1, ContextMenu 의 부모 요소가 DataTemplate에 없습니다 .
Command="{Binding PlacementTarget.DataContext.MyCommand,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"
이것은 OP의 질문에 효과적입니다. DataTemplate 내부에 있으면 작동하지 않습니다 . 이 경우 DataContext 는 종종 컬렉션의 많은 것 중 하나이며 바인딩하려는 ICommand 는 동일한 ViewModel ( 예 : Window 의 DataContext) 내 컬렉션의 형제 속성입니다 .
이러한 경우 태그 를 활용 하여 컬렉션과 ICommand를 모두 포함 하는 부모 DataContext 를 일시적으로 보유 할 수 있습니다 .
class ViewModel
{
public ObservableCollection<Derp> Derps { get;set;}
public ICommand DeleteDerp {get; set;}
}
그리고 xaml에서
<!-- ItemsSource binds to Derps in the DataContext -->
<StackPanel
Tag="{Binding DataContext, ElementName=root}">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem
Header="Derp"
Command="{Binding PlacementTarget.Tag.DeleteDerp,
RelativeSource={RelativeSource
AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource AncestorType=ContextMenu}}">
</MenuItem>
컨텍스트 메뉴는 바인딩하기가 까다 롭습니다. 컨트롤의 시각적 트리 외부에 존재하므로 요소 이름을 찾을 수 없습니다.
Try setting the datacontext of your context menu to its placement target. You have to use RelativeSource.
<ContextMenu
DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ...
After experimenting a bit, I discovered one work around:
Make top level Window
/UserControl
implement INameScope
and set NameScope
of ContextMenu
to the top level control.
public class Window1 : Window, INameScope
{
public Window1()
{
InitializeComponent();
NameScope.SetNameScope(contextMenu, this);
}
// Event handlers and etc...
// Implement INameScope similar to this:
#region INameScope Members
Dictionary<string, object> items = new Dictionary<string, object>();
object INameScope.FindName(string name)
{
return items[name];
}
void INameScope.RegisterName(string name, object scopedElement)
{
items.Add(name, scopedElement);
}
void INameScope.UnregisterName(string name)
{
items.Remove(name);
}
#endregion
}
This allows the context menu to find named items inside of the Window
. Any other options?
I'm not sure why resort to magic tricks just to avoid a one line of code inside the eventhandler for the mouse click you already handle:
private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e)
{
// this would be your tag - whatever control can be put as string intot he tag
UIElement elm = Window.GetWindow(sender as MenuItem).FindName("whatever control") as UIElement;
}
참고URL : https://stackoverflow.com/questions/1013558/elementname-binding-from-menuitem-in-contextmenu
'developer tip' 카테고리의 다른 글
registerServiceWorker는 React JS에서 무엇을합니까? (0) | 2020.11.18 |
---|---|
Hibernate Criteria API에서 SQL을 얻는 방법 (로깅을위한 * 아님 *) (0) | 2020.11.18 |
Git이 이전에 이동 한 파일을 확인하도록하기 (0) | 2020.11.18 |
float가 정수인지 확인 (0) | 2020.11.18 |
벡터 / 행렬 끝까지 우아한 인덱싱 (0) | 2020.11.18 |