WPF HierarchicalDataTemplate ContextMenu not Opening - c#

I have a treeview that is loading based on datatype. If the HierarchicalDataTemplates are nested like below the cMenuBSTool ContextMenu will display on the first child InventoryBSVM but not on any InventoryBSVM children of the first child InventoryBSVM:
<HierarchicalDataTemplate DataType="{x:Type v:ServiceGWDVM}" ItemsSource="{Binding Inventory}" ItemTemplateSelector="{StaticResource ToolSelector}">
<TextBlock Text="{Binding Name}" Style="{StaticResource ServiceStyle}" ContextMenu="{StaticResource cMenuServiceGWD}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type v:InventoryBSVM}" ItemsSource="{Binding ChildItems}" ItemTemplateSelector="{StaticResource ToolSelector}">
<TextBlock Text="{Binding Name}" Style="{StaticResource ToolStyle}" ContextMenu="{StaticResource cMenuBSTool}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
If I place the InventoryBSVM HierarchicalDataTemplate outside of the ServiceGWDVM HierarchicalDataTemplate the cMenuBSTool ContextMenu will not display for any InventoryBSVM Nodes. The number of InventoryBSVM children of other InventoryBSVM is not known in advance. The ContextMenu is very simple as can be seen below:
<ContextMenu x:Key="cMenuBSTool">
<MenuItem Header="Delete Tool" Command="{Binding CmdDelete}">
<MenuItem.Triggers>
<EventTrigger RoutedEvent="MenuItem.Click"/>
</MenuItem.Triggers>
</MenuItem>
<MenuItem Header="Create Tool" Command="{Binding CmdCreate}" CommandParameter="{StaticResource wizCreateBS}">
<MenuItem.Triggers>
<EventTrigger RoutedEvent="MenuItem.Click"/>
</MenuItem.Triggers>
</MenuItem>
</ContextMenu>
I hope there is something simple that I am missing. I have spent two days on this now and am stuck. This is my first post on Stack Overflow and any help would be greatly appreciated.

Related

How to filter 3 level wpf treeview

Trying to filter level 2 and 3 nodes on a wpf treeview that is bound to a dataset.
I have tried using a value converter on the visibility property of the nodes but being a HierarchicalDataTemplate the converter isn't called.
<ObjectDataProvider x:Key="dataSetProvider" MethodName="GetDataSet" ObjectType="{x:Type local:DataSetCreator}"/>
<DataTemplate x:Key="SymbolTemplate">
<TextBlock Text="{Binding SymbolName}"/>
</DataTemplate>
<HierarchicalDataTemplate x:Key="FamilyTemplate" ItemsSource="{Binding Fam2Sym}" ItemTemplate="{StaticResource SymbolTemplate}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FamilyName}" />
<TextBlock><Run Text=" ("/><Run Text=")"/></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="Categorytemplate" ItemsSource="{Binding Cat2Fam}" ItemTemplate="{StaticResource FamilyTemplate}">
<TextBlock Text="{Binding LocalizedName}" />
</HierarchicalDataTemplate>
<TreeView x:Name="archTree" DataContext="{StaticResource dataSetProvider}" ItemsSource="{Binding RvtCat}"
ItemTemplate="{StaticResource Categorytemplate}"/>
I'm trying to filter a wpf treeview where if the level 2 OR 3 nodes match a string filter both levels are shown. I can't seem to find a way to do this. I've searched google but haven't found any similar questions. Suggestions?

How do I attached behaviors to a TreeViewItem?

I know how to attach behaviors to a TreeView, for example:
<TreeView
x:Name="TestCases"
ItemsSource="{Binding TestCases}"
Margin="0, 10, 0, 0">
<i:Interaction.Behaviors>
<behavior:TreeViewSelectedItemBlendBehavior />
</i:Interaction.Behaviors>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type testCases:TestCase}" ItemsSource="{Binding DataSets}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
But is it possible to add a behavior to every Item instead (to make something when "Selected" event is fired)? I don't know where I should put it as I use an ItemSource binding.

Binding ContextMenu to Datagrid Columns

I am trying to bind DataGrid column header to its own ContextMenu like this:
<DataGrid x:Name="AllLogs">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Show/Hide Columns"
ItemsSource="{Binding ElementName=AllLogs, Path=Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
Its always sends the following error in output:
Cannot find source for binding with reference
'ElementName=AllLogs'. BindingExpression:Path=Columns;
DataItem=null; target element is 'MenuItem' (Name=''); target property
is 'ItemsSource' (type 'IEnumerable')
EDIT: Binding with a ComboBox works as expected
<ComboBox ItemsSource="{Binding ElementName=AllLogs, Path=Columns}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Header}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You should set first the DataContext of ContextMenu so that ItemsSource bind to Menu Item can inherit the same DataContext.
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show/Hide Columns"
ItemsSource="{Binding Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
The reason that ContextMenu didn't work but ComboBox did is that ContextMenu is a Popup, that means it's not part of DataGrid's visual tree, so ElementName would not work as ComboBox did. In fact, #user1672994 was vary close to the answer.
<DataGrid x:Name="AllLogs">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Show/Hide Columns"
ItemsSource="{Binding PlacementTarget.Columns, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
The problem/misunderstanding is that ContextMenu isn't a part of a visual tree.
So ContextMenu.PlacementTarget is your "connection" to the UIElement, which is in the visual tree, therefore you have to go via PlacementTarget to access the elements from visual tree.
MSDN about PlacementTarget:
When the ContextMenu is assigned to the FrameworkElement.ContextMenu
or FrameworkContentElement.ContextMenu property, the
ContextMenuService changes this value of this property to the owning
FrameworkElement or FrameworkContentElement when the ContextMenu opens
In answer below you don't have to traverse searching an ancestor type, but do use an UIElement as DataContext for ContextMenu:
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show/Hide Columns" ItemsSource="{Binding Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
I don't know the RadGrivView control per say, but the error means that it cannot find an IEnumerable property called "Columns" on your element. Are you sure it is a publicly accessible collection for the control?
ElementName uses VisualTree to find out desired element, it its not part of the current visual tree - up or down as it is with context menu you will get exception.
You could use Binding Source={x:Reference AllLogs} that does not use VisualTree, but unfortunately in your use case you would get exception of circular reference if you use it directly without style.
What you need to use is RelativeSource binding.
<Window.Resources>
<ContextMenu x:Key="headerMenu">
<ContextMenu.Items>
<MenuItem
Header="Show/Hide Columns"
ItemsSource="{Binding Columns, RelativeSource={RelativeSource AncestorType=DataGrid}}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu.Items>
</ContextMenu>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ContextMenu" Value="{StaticResource headerMenu}" />
</Style>
</Window.Resources>
<Grid>
<DataGrid x:Name="AllLogs">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"></DataGridTextColumn>
<DataGridTextColumn Header="Name"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
This would work as well - with Reference:
<Window.Resources>
<ContextMenu x:Key="headerMenu">
<ContextMenu.Items>
<MenuItem
Header="Show/Hide Columns"
ItemsSource="{Binding Source={x:Reference AllLogs}, Path=Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu.Items>
</ContextMenu>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ContextMenu" Value="{StaticResource headerMenu}" />
</Style>
</Window.Resources>
<Grid>
<DataGrid x:Name="AllLogs">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"></DataGridTextColumn>
<DataGridTextColumn Header="Name"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>

add command on ItemTemplate selection

This is a pretty easy question I think , but I can't find the answer. I have an itemtemplate defined into a datatemplate. When an item is selected, I wanna trigger a command to select the name of my element and apply it somewhere else. For the moment the MouseDown event doesn't accept my command.
<ListView Margin="4" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Path=ExistingStateInfos, ElementName=Window}"
SelectedItem="{Binding Path=SelectedStateInfo, ElementName=Window}" x:Name="statinfoListview">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type States:StateInfo}">
<TextBlock Text="{Binding Path=Name}" MouseDown="{x:Static MyWindow.ApplyStateInfoNameToStateCommand}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You can't set a command directly to an event handler.
Use EventToCommand from the MVVM LightToolkit
<DataTemplate DataType="{x:Type States:StateInfo}">
<TextBlock Text="{Binding Path=Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<Command:EventToCommand Command="{Binding YourCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
In fact ot was bulshit from me, I just add to do
<TextBlock Text="{Binding Path=Name}" MouseDown="{x:Static ApplyStateInfoNameToState_Click}" />
Sorry it's friday. Have a nice weekend :)

Tree View Context Menu - Pass selected item to command?

I'm wondering how to pass the selected item to a command from a treeview / HierarchicalDataTemplate ?
Here is the code that I have so far, it displays the context menu but I haven't bound the commands to it yet. The command binding is the easy part, but how do I tell which node it came from ?
<HierarchicalDataTemplate
DataType="{x:Type viewModel:UsersViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Images\Region.png" />
<TextBlock Text="{Binding UserName}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" />
<MenuItem Header="Delete"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
Just {Binding} should be the whole item.
(To pass it to the Command bind the CommandParameter, in Execute and CanExecute it will become the method parameter (which you then need to cast to your item-type))

Categories

Resources