wpf data-binding contextmenu elementname

wpf - Enlace de ElementName de MenuItem en ContextMenu



data-binding (6)

Aquí hay otra solución de solo xaml. (Esto también supone que quieres lo que está dentro del DataContext , por ejemplo, lo estás MVVMing )

Opción uno, donde el elemento padre de ContextMenu no está en una plantilla de datos :

Command="{Binding PlacementTarget.DataContext.MyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"

Esto funcionaría para la pregunta de OP. Esto no funcionará si estás dentro de un DataTemplate . En estos casos, el DataContext suele ser uno de muchos en una colección, y el ICommand al que desea vincularse es una propiedad hermana de la colección dentro del mismo ViewModel (el DataContext de la Ventana, por ejemplo).

En estos casos, puede aprovechar la Tag para mantener temporalmente el DataContext principal que contiene tanto la colección como su ICommand:

class ViewModel { public ObservableCollection<Derp> Derps { get;set;} public ICommand DeleteDerp {get; set;} }

y en el 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>

¿Alguien más ha notado que los enlaces con ElementName no se resuelven correctamente para los objetos de MenuItem que están contenidos dentro de los objetos ContextMenu ? Mira esta muestra:

<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>

Todas las vinculaciones funcionan de maravilla a excepción de las ataduras contenidas dentro del ContextMenu. Imprimen un error en la ventana de Salida durante el tiempo de ejecución.

¿Alguien sabe de cualquier problema? ¿Que está pasando aqui?


Como dijeron otros, el ''ContextMenu'' no está contenido en el árbol visual y un enlace ''ElementName'' no funcionará. Configurar el ''NameScope'' del menú contextual como lo sugiere la respuesta aceptada solo funciona si el menú contextual no está definido en una ''DataTemplate''. He resuelto esto usando la extensión de marcado {x: Reference} que es similar al enlace ''ElementName'' pero resuelve el enlace de manera diferente, pasando por alto el árbol visual. Considero que esto es mucho más legible que usar ''PlacementTarget''. Aquí hay un ejemplo:

<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>

De acuerdo con la documentación de MSDN

x: Reference es una construcción definida en XAML 2009. En WPF, puede usar las características de XAML 2009, pero solo para XAML que no está compilado con WPF. El XAML compilado en el marcado y el formulario BAML de XAML actualmente no son compatibles con las palabras clave y características del lenguaje XAML 2009.

lo que sea que eso signifique ... Funciona para mí, sin embargo.


Después de experimentar un poco, descubrí una alternativa:

Haga que Window / UserControl nivel superior implemente INameScope y establezca NameScope of ContextMenu en el control de nivel superior.

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 }

Esto permite que el menú de contexto encuentre elementos con nombre dentro de la Window . ¿Alguna otra opción?


Encontré una solución mucho más simple.

En el código detrás para UserControl:

NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this));


Los menús contextuales son difíciles de vincular. Existen fuera del árbol visual de tu control, por lo tanto, no pueden encontrar el nombre de tu elemento.

Intente configurar el contexto de datos de su menú contextual en su destino de ubicación. Tienes que usar RelativeSource.

<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ...


No estoy seguro de por qué recurrir a trucos de magia solo para evitar una línea de código dentro del controlador de eventos para el clic del mouse que ya maneja:

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; }