binding error to hide menu item - c#

I want to hide a menuItem regarding user's rights. The menu item is placed into a context menu (displayed with a right click) into a userControl. Rights are passed to the user control trough the main window. I have an error 40 : Binding error. VS can't find my property declared in the xaml document.
MainWindow.xaml
<MyUC:myUC
...
MainOptionsVisibility="False" />
myUc.xaml
<GMap_NET_WindowsPresentation:GMapControl.ContextMenu>
<ContextMenu Opened="ContextMenu_Opened">
<MenuItem
Header="{x:Static Internationalization:Resources.VIEWPORT_ADDOBJECT}"
Command="{x:Static local:Viewport.CreateGraphicObjectRequestCommand}"
CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource BooleanToVisibilityCollapsedConverter}}">
<MenuItem.Icon>
<Image Source="{DynamicResource EditIcon}" Width="32" Height="32" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static Internationalization:Resources.OPTIONS}"
Visibility="{Binding Path=MainOptionsVisibility, RelativeSource={RelativeSource Self}, Converter={StaticResource BooleanToVisibilityCollapsedConverter}}" >
...
/>
</GMap_NET_WindowsPresentation:GMapControl.ContextMenu>
</GMap_NET_WindowsPresentation:GMapControl>
And MainOptionsVisibility is well declared as dependency property in code-behind. I have checked, it is well initialized. The visibility of the other item is OK (I didn't do it).
EDIT : new XAML code after Flo's answer :
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Main.Client.MyProject.Implementation.UIs.StandardViewports.Viewports"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:GMap_NET_WindowsPresentation="clr-namespace:GMap.NET.WindowsPresentation;assembly=GMap.NET.WindowsPresentation"
xmlns:Internationalization="clr-namespace:Main.Client.MyProject.Library.Resources;assembly=MyProjectLibrary"
xmlns:Main_Client_MyProject_Library_Converters="clr-namespace:Main.Client.MyProject.Library.Converters;assembly=MyProjectLibrary"
x:Name="baseViewport"
x:Class="Main.Client.MyProject.Implementation.UIs.StandardViewports.Viewports.Viewport"
MouseEnter="baseViewport_MouseEnter"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Loaded="BaseViewport_Loaded">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyLibs;component/ResourceDictionnary/ResourceDictionnary.xaml" />
</ResourceDictionary.MergedDictionaries>
<Main_Client_MyProject_Library_Converters:BooleanToVisibilityCollapsedConverter x:Key="BooleanToVisibilityCollapsedConverter" />
<Main_Client_MyProject_Library_Converters:BooleanToVisibilityCollapsedConverter x:Key="BooleanToVisibilityTestConverter" />
</ResourceDictionary>
</UserControl.Resources>
<UserControl.CommandBindings>
...
</UserControl.CommandBindings>
<Grid>
<GMap_NET_WindowsPresentation:GMapControl
x:Name="gMapControl"
MaxZoom="18"
MinZoom="1"
MouseDown="gMapControl_MouseDown"
OnMapZoomChanged="gMapControl_OnMapZoomChanged"
OnCurrentPositionChanged="gMapControl_OnCurrentPositionChanged"
MouseMove="gMapControl_MouseMove"
Loaded="gMapControl_Loaded"
Drop="gMapControl_Drop"
AllowDrop="True"
IsEnabled="{Binding IsEnabled, ElementName=baseViewport}" MapType="OpenStreetMap">
<GMap_NET_WindowsPresentation:GMapControl.ContextMenu>
<ContextMenu Opened="ContextMenu_Opened">
<MenuItem
Header="{x:Static Internationalization:Resources.VIEWPORT_ADDOBJECT}"
Command="{x:Static local:Viewport.CreateGraphicObjectRequestCommand}"
CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource BooleanToVisibilityTestConverter}}">
<MenuItem.Icon>
<Image Source="{DynamicResource EditIcon}" Width="32" Height="32" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static Internationalization:Resources.VIEWPORT_OPTIONS}"
Visibility="{Binding Path=PlacementTarget.DataContext.MainOptionsVisibility, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Converter={StaticResource BooleanToVisibilityTestConverter}}" >
<MenuItem Header="{x:Static Internationalization:Resources.VIEWPORT_LOCKSUPERIORLEFTCORNER}" Command="{x:Static local:Viewport.LockSuperiorLeftCornerRequestCommand}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}">
</MenuItem>
<MenuItem Header="{x:Static Internationalization:Resources.VIEWPORT_LOCKINFERIORRIGHTCORNER}" Command="{x:Static local:Viewport.LockInferiorRightCornerRequestCommand}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}">
</MenuItem>
<MenuItem Header="{x:Static Internationalization:Resources.VIEWPORT_LOCKZOOMMAXONMAP}" Command="{x:Static local:Viewport.LockMaxZoomLevelRequestCommand}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" >
</MenuItem>
<MenuItem Header="{x:Static Internationalization:Resources.VIEWPORT_LOCKZOOMMINONMAP}" Command="{x:Static local:Viewport.LockMinZoomLevelRequestCommand}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" >
</MenuItem>
</MenuItem>
</ContextMenu>
</GMap_NET_WindowsPresentation:GMapControl.ContextMenu>
</GMap_NET_WindowsPresentation:GMapControl>
</Grid>
The converter was changed for both menuItems for tests. It is never called.

The problem is your MenuItem has no property which is called MainOptionsVisibility, only your Window has. Through RelativeSource={RelativeSource Self} your Binding to the MenuItem.
RelativeSource={RelativeSource AncestorType={x:Type Window}} wont work either, because your ContextMenu isnt part of the Windows logical or visual Tree.
What you could do is set the DataContext of the ContextMenus PlacementTarget (GMap_NET_WindowsPresentation:GMapControl) or one of its ancestors to your Window (e.g. through setting the Windows DataContext to itself (<Window ...DataContext={Binding RelativeSource={RelativeSource Self}}.../>) and the do something like:
<MenuItem Header="{x:Static Internationalization:Resources.OPTIONS}" Visibility="{Binding Path=PlacementTarget.DataContext.MainOptionsVisibility, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Converter={StaticResource BooleanToVisibilityCollapsedConverter}}" >

Related

WPF : Remove context menu items based on condition

I have a context menu in WPF with following items:
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="{x:Static localization:Resources.MyContext_Command1}" Command="{Binding Command1}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command2}" Command="{Binding Command2}" />
<Separator />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command3}" Command="{Binding Command3}" Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command4}" Command="{Binding Command4}" Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}" />
<Separator Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}"/>
</ContextMenu>
With the above code these menu items(Command3 and Command4) are appearing grey(Disabled) when IsItemActive = false and appear black(Enabled) when IsItemActive = true. But i want my Menuitems(Command3 and Command4) and also Seperator to Disappear / Appear from context menu based on "IsItemActive".How can i achieve this ?
This should do the trick. But I cannot get my bindings to work now so it's not tested. You will have to give a name to the element that you are applying this in order to make the binding work. For my case, it's a window.
Create a dependency property for IsItemActive only then you can bind.
Tip : type propdp and double tab in Visual Studio to use code snippet.
<Window x:Class.....
x:Name="mainwindow"
...>
Then in the <Window.Resources> define the styles
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=mainwindow,Path=IsItemActive}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=mainwindow,Path=IsItemActive}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
Duplicate the style and change TargetType="Separator" Then apply the style on your context menu items and separator.
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="{x:Static localization:Resources.MyContext_Command1}" Command="{Binding Command1}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command2}" Command="{Binding Command2}" />
<Separator />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command3}" Command="{Binding Command3}" Style="{StaticResource MenuItemStyle}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command4}" Command="{Binding Command4}" Style="{StaticResource MenuItemStyle}" />
<Separator Style="{StaticResource SeparatorStyle}"/>
</ContextMenu>

How to get PlacementTarget.Content in ContextMenu?

I have a situation, where I have to get Content from ContextMenu's Button. Something like this:
<Button Content="Test" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Content}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
But.. that doesn't work. The problem can be easily solved with the button's Tag, but the Tag is in use already:
<Button Content="Test" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"
Tag="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}, AncestorLevel=2}}">
<Button.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Content}"/>
<MenuItem Header="Option 2" />
</ContextMenu>
</Button.ContextMenu>
</Button>
I'm using Tag to get main DataContext. Yet I still need the content of the button.
Why getting "Tag" from Placement target works, but "Content" does not?
How can I get it?
MenuItem doesn't have "PlacementTarget" property, bidning to Self doesn't work. There should be "System.Windows.Data Error: 40 : BindingExpression path error: 'PlacementTarget' property not found on 'object' 'MenuItem'" warning in Output.
Change the path:
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding Path=Parent.PlacementTarget.Content, RelativeSource={RelativeSource Self}}"/>
<MenuItem Header="Option 2" />
</ContextMenu>
or RelativeSource:
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding Path=PlacementTarget.Content, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
<MenuItem Header="Option 2" />
</ContextMenu>

C# wpf Databinding command not working in contextmenu

I am learning wpf and mvvm and I decided to create a Soundboard for myself to practice and so far it's going pretty well.
Now I have made a datatemplate where for every file that the program finds in the specified directory it will create a button with the name of the file in it and I can click it to play. So far so good.
However I now tried to make a ContextMenu so that when I want to remove a file from the list I can right click and select remove, but this command doesn't work even though I have the exact same command structure for the regular button.
I am really quite confused with the whole RelativeSource thing and was already happy my regular 'play' command worked in the button.
If someone could point me in the right direction that would be great. I really could use an explanation on my specific problem as that always seems to help me more then a generic example somehow. I have tried to read on all the related questions but just don't seem to figure it out from there.
My ItemsControl:
<ItemsControl x:Name="MySounds" ItemsSource="{Binding Sounds}">
ItemTemplate:
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Style="{StaticResource mainButton}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.PlaySound}"
CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Self}}"
Tag="{Binding Path=Name}">
<TextBlock Text="{Binding Path=NormalizedName}" TextWrapping="Wrap" Height="auto" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Path=Name}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.RemoveSound}"
CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Self}}">
<MenuItem.Icon>
<Image Source="\WpfPractice;component\Images\CoffeeArt.png" Width="20" VerticalAlignment="Center"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
I have a generic RelayCommand in my viewmodel and that all works, the problem really is just with the binding.
If you bind the Tag property of the Button to the ItemsControl, you could bind to the command using the PlacementTarget property of the ContextMenu:
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Style="{StaticResource mainButton}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.PlaySound}"
CommandParameter="{Binding Path=Name}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}}">
<TextBlock Text="{Binding Path=NormalizedName}" TextWrapping="Wrap" Height="auto" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Path=Name}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.DataContext.RemoveSound}"
CommandParameter="{Binding Path=Name}">
<MenuItem.Icon>
<Image Source="\WpfPractice;component\Images\CoffeeArt.png" Width="20" VerticalAlignment="Center"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
You can try to replace your command string in your MenuItem by this :
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.RemoveSound}"

How to access dynamic control WPF in DataTemplate

I created a tabcontrol with TabItem dynamic, and each TabItem with a button to close it, but just want that button visible when the TabItem is selected.
But I can not access the control inside the DataTemplate
<TabControl Name="dynamicTab" ItemsSource="{Binding}" Margin="0,85,0,0">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<Button
Focusable="False"
BorderThickness="0"
Background="Transparent"
BorderBrush="Transparent"
Padding="-4"
Height="10"
Width="10"
Name="btnDelete" Visibility="Hidden" DockPanel.Dock="Right" Margin="5,0,0,0" Click="btnDelete_Click"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Name="imgButtonClose" Source="/Recursos;component/Imagens/close16x16.png" Height="10" Width="10"/>
</Button>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
</DockPanel>
</DataTemplate>
</TabControl.Resources>
</TabControl>
Just use the binding on the IsSelected property of ancestoral TabItem:
<BooleanToVisibilityConverter x:Key="boolToVisibilityConverter"/>
...
<Button ...
Name="btnDelete"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=IsSelected, Converter={StaticResource boolToVisibilityConverter}">
...
</Button>
If you have no problems with this binding:
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}"
then the proposed code should work.

How to Set DataContext for TreeViewItem for ContextMenu

I have the following code. I am trying to bind the Command property of the MenuItem to the relevant command which is implemented in the DataContext of the user control. Can anyone help me? The command does not get executed.
<UserControl x:Class="MIB2.App.Presentations.Views.CategoryManView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:model="clr-namespace:MIB2.Models;assembly=MIB2"
xmlns:local="clr-namespace:MIB2.App.Presentations.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="335">
<UserControl.Resources>
<local:DatabindingDebugConverter x:Key="debugConverter" />
</UserControl.Resources>
<StackPanel Width="417">
<TreeView x:Name="tree"
ItemsSource="{Binding RootCategories}" SelectedItemChanged="TreeView_SelectedItemChanged"
MouseRightButtonDown="OnPreviewMouseRightButtonDown">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Root Category" Command="{Binding AddRootCategoryCommand}"/>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="model:Item"
ItemsSource="{Binding Children}">
<Grid>
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType=TextBox}}">
<MenuItem Header="Add Category" Command="{Binding Path=AddEntityCommand}" />
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" Tag="{Binding DataContext, ElementName=tree}"/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Orientation="Horizontal">
<Button Content="Add New Category" Command="{Binding AddEntityCommand}" />
<Button Content="Add New Root Category" Command="{Binding AddRootCategoryCommand}" />
<Button Content="Delete Category" Command="{Binding DeleteEntityCommand}" />
</StackPanel>
<ContentControl Content="{Binding EntityViewModel.View}" />
</StackPanel>
</UserControl>
Please try this:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="model:Item"
ItemsSource="{Binding Children}">
<Grid Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Category"
Command="{Binding Path=PlacementTarget.Tag.AddEntityCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
You can use RelativeSource in binding to lookup the parent object
e.g
<MenuItem Header="Add Root Category" Command="{Binding DataContext.CommandName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}/>

Categories