Xaml Namescope and template question about WPF ContextMenu

Everything in the code below works, except for binding to ContextMenu. This is obviously due to the fact that ContextMenu is inside a style that puts it in a different name pointer from the rest of xaml. I am looking for a solution where I do not need to create an instance of ContextMenu in the code, because the application in which I have to apply the solution contains a very large ContextMenu with a lot of bindings. There must be a way to do this in haml, otherwise it would look like serious oversight. Also note that I already tried traversing the element tree using VisualTreeHelper and LogicalTreeHelper, but I could not find the ContextMenu from the root Window element (these classes obviously missed the interesting elements). In any case, all of the code is below. This can be inserted into a new WPF application in Visual Studio, and nothing is missing.

Here is the code for App.xaml.cs (xaml remained unchanged):

using System.Windows; namespace WpfApplication1 { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); WindowV windowV = new WindowV(); WindowVM windowVM = new WindowVM(); windowV.DataContext = windowVM; windowV.Show(); } } } 

Here's the haml for what was originally Window1:

 <Window x:Class="WpfApplication1.WindowV" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:src="clr-namespace:WpfApplication1" Name="MainWindow" Title="WindowV" Height="300" Width="300"> <Window.Resources> <Style TargetType="{x:Type ItemsControl}"> <Style.Triggers> <DataTrigger Binding="{Binding IsLocked}" Value="true"> <Setter Property="ItemsSource" Value="{Binding LockedList}" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <TextBlock Text="{Binding}" /> </DataTemplate> </Setter.Value> </Setter> </DataTrigger> <DataTrigger Binding="{Binding IsLocked}" Value="false"> <Setter Property="ItemsSource" Value="{Binding RegularList}" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <TextBlock Text="{Binding}"> <TextBlock.ContextMenu> <ContextMenu> <MenuItem Header="{Binding MenuItem1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> <MenuItem Header="{Binding MenuItem2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> <MenuItem Header="{Binding MenuItem3, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> </ContextMenu> </TextBlock.ContextMenu> </TextBlock> </DataTemplate> </Setter.Value> </Setter> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="4*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <ItemsControl Grid.Row="0" /> <Button Name="ToggleButton" Grid.Row="1" Content="Toggle Lock" Click="OnToggleLock" /> </Grid> </Window> 

Here is the codebehind for what was originally Window1:

 using System.Windows; using System.Windows.Markup; namespace WpfApplication1 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class WindowV : Window { public WindowV() { InitializeComponent(); } private void OnToggleLock(object sender, RoutedEventArgs e) { if (((WindowVM)(DataContext)).IsLocked == true) ((WindowVM)(DataContext)).IsLocked = false; else ((WindowVM)(DataContext)).IsLocked = true; } } } 

A new class has been added to a project called WindowVM. Here is his code:

 using System.Collections.Generic; using System.ComponentModel; namespace WpfApplication1 { public class WindowVM : INotifyPropertyChanged { public string MenuItem1 { get { string str = "Menu item 1"; return str; } } public string MenuItem2 { get { string str = "Menu item 2"; return str; } } public string MenuItem3 { get { string str = "Menu item 3"; return str; } } public List<string> LockedList { get { List<string> list = new List<string>(); list.Add("This items control is currently locked."); return list; } } public List<string> RegularList { get { List<string> list = new List<string>(); list.Add("Item number 1."); list.Add("Item number 2."); list.Add("Item number 3."); return list; } } private bool _isLocked; public bool IsLocked { get { return _isLocked; } set { if (_isLocked != value) { _isLocked = value; OnPropertyChanged("IsLocked"); } } } public WindowVM() { IsLocked = false; } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string PropertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(PropertyName)); } } } 

Any insight would be greatly appreciated. Many thanks!

Andrew

0
source share
2 answers

Ok, this solution works: I modified the WindowV.xaml and WindowV.xaml.cs files as follows. The following fixes fix the problem with names in xaml.

Here's the new WindowV.xaml file:

 <Window x:Class="WpfApplication1.WindowV" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:src="clr-namespace:WpfApplication1" Name="RootElement" Title="WindowV" Height="300" Width="300"> <Window.Resources> <ContextMenu x:Key="myContextMenu" DataContext="{Binding Path=DataContext, ElementName=RootElement}"> <MenuItem Header="{Binding MenuItem1}" /> <MenuItem Header="{Binding MenuItem2}" /> <MenuItem Header="{Binding MenuItem3}" /> </ContextMenu> <Style TargetType="{x:Type ItemsControl}"> <Style.Triggers> <DataTrigger Binding="{Binding IsLocked}" Value="true"> <Setter Property="ItemsSource" Value="{Binding LockedList}" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <TextBlock Text="{Binding}" /> </DataTemplate> </Setter.Value> </Setter> </DataTrigger> <DataTrigger Binding="{Binding IsLocked}" Value="false"> <Setter Property="ItemsSource" Value="{Binding RegularList}" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <TextBlock Text="{Binding}" ContextMenu="{StaticResource myContextMenu}" /> </DataTemplate> </Setter.Value> </Setter> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="4*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <ItemsControl Grid.Row="0" /> <Button Name="ToggleButton" Grid.Row="1" Content="Toggle Lock" Click="OnToggleLock" /> </Grid> </Window> 

Here's the corresponding code:

 using System.Windows; using System.Windows.Markup; using System; namespace WpfApplication1 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class WindowV : Window { public WindowV() { InitializeComponent(); System.Windows.Controls.ContextMenu contextMenu = FindResource("myContextMenu") as System.Windows.Controls.ContextMenu; NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this as DependencyObject)); contextMenu.RegisterName("RootElement", this); } private void OnToggleLock(object sender, RoutedEventArgs e) { if (((WindowVM)(DataContext)).IsLocked == true) ((WindowVM)(DataContext)).IsLocked = false; else ((WindowVM)(DataContext)).IsLocked = true; } } } 

Andrew

0
source

Good. I have a little problem after what you are trying to execute, but try the following:

 <ContextMenu> <MenuItem Header="{Binding DataContext.MenuItem1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" /> ... 

And to make your code easier, you can try:

 <ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"> <MenuItem Header="{Binding MenuItem1}"/> ... 

The problem was that you were tied to the window of a user interface element that does not have a property called MenuItem1, etc. The DataContext property has the data you are interested in.

0
source

All Articles