WPF MenuItem children not showing - c#

I am using an ObjectDataProvider and a DataTemplate to populate a MenuItem inside my Menu bar. (WPF, C#/XAML) See snipet below.
Result: The top menu item appears, when i click on it, the wrapping menu item (the one with the bound header text) appears along with the little arrow indicating the presence of children but hovering or clicking the arrow does not show the children, they cannot be accessed.
Expected result: The children are visible and behave properly.
Snippet:
<ObjectDataProvider x:Key="Brokers" ObjectInstance="{x:Static brokers:BrokerManager.Instance}" MethodName="GetBrokers" IsAsynchronous="True" />
<DataTemplate x:Key="BrokerMenuItem" DataType="IBroker">
<MenuItem Header="{Binding Path=Name}">
<MenuItem Header="Connect" />
<MenuItem Header="Disconnect" />
</MenuItem>
</DataTemplate>
<MenuItem Header="Brokers" ItemsSource="{Binding Source={StaticResource Brokers}}" ItemTemplate="{DynamicResource BrokerMenuItem}"/>

arsenmrkt: I have exactly the same problem, if I populate a MenuItem using a DataTemplate I cant seem to add children to any of those generated items. I don't understand your answer though, how should I use the ContentPresenter to get around this problem?
EDIT:
Actually, my problem was'nt exactly the same, since I'm trying to bind a collection of collections to a menu. I think I've gotten it to work though using the HierarchicalDataTemplate:
<Menu>
<MenuItem Header="{Binding Name}" ItemsSource="{Binding MenuOptions}">
<MenuItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Categories}">
<MenuItem Header="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
Does this help you NicholasF?

After searching for over a week, i finally found how to make this work properly. It turns out DataTemplates don't work too great for dynamic menus. The proper way to do this is to use the ItemContainerStyle property of MenuItem. (Or is that ItemStyleContainer?)
Simply create a style to override the header and set it to whatever you need. I them overrode the ItemsSource to include my children. However be careful here, as the children will inherit the style and each have the same children and generate a recursive menu. You'll need to override the ItemsSource of your children and set it to an empty x:Array or the likes.
There are several blogs out there describing how to use ItemContainerStyle, check them out.

ItemSource property of menuitem control is used for giving childes for that item, try to use <ContentPresenter /> with that datatemplate.

Related

How do you set the focus on a TextBox when ListViewItem is clicked?

I'm creating an UWP app using XAML and C#. Each ListViewItem in the ListView contains a TextBox. When I click/select a ListViewItem, I would set the Focus on the TextBox within that ListViewItem.
<ListView ItemsSource="{Binding Items, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
...
<TextBox Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Behaviors>
...
</i:Interaction.Behaviors>
</TextBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What's the best way to implement this in an MVVM style?
That sounds like a pure view issue, and should thus be solved purely in the view. It might need some code behind, however. Also one question: How likely is it that you will never need to select the ListView Item itself? If it is not 100% likely, consider a way to still select it directly (like doubleclick).
These answer seems similar enough:
https://stackoverflow.com/a/40054140/3346583 (event handlers and itterating over elements)
https://social.msdn.microsoft.com/Forums/en-US/1d52e0db-eef6-4022-9d27-ef456ad795a9/uwp-i-cant-set-focus-on-a-text-box-in-listviewitem?forum=wpdevelop (using the dispatcher)
I searched for "UWP ListView Focus Child". But looking for WPF solutions is about as likely to yield results.

Bind menu items to a list

I have a list of strings that holds the names of menu items:
List<string> namesStorage;
How do I bind this list as collection in XAML?
Right now I have wrote this is part, which already works:
<StackPanel Orientation="Vertical">
<MenuItem Header="MenuItem1" Command="{Binding Command1}" />
<MenuItem Header="MenuItem2" Command="{Binding Command1}" />
</StackPanel>
This items must have one command (different arguments will be set in the view model).
But I think this is not a good way to set every MenuItem in XAML - It is ok now for two items but I wonder how to make a universal decision for any number of this menu items. I have created a list that holds names but how to bind it (and keep a commands)?
After setting the correct DataContext, this should work:
<ItemsControl ItemsSource="{Binding namesStorage}" x:Name="ItemsControlName">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding ElementName=ItemsControlName, Path=DataContext.Command1}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Edit: Removed Parent-Stackpanel as suggested in the comments

Creating dynamic WPF context menu

I have an ObservableCollection in my ViewModel and I want to create a ContextMenu which is bindded to that collection where every item in the collection has a submenu and all submenus are the same.
For example the collection is {10,20,30} and the submenu is
- Param (MenuItem)
- Set (MenuItem)
- Reset (MenuItem)
- Clear (MenuItem)
so that the final context menu would look something like this\
- 10
- Param
- Set
- Reset
- Clear
- 20
- Param
- Set
- Reset
- Clear
- 30
....
I've tried creating a resource
<x:Array x:Key="MenuResource" Type=Control>
<MenuItem Header="Param"/>
<MenuItem Header="Set"/>
<MenuItem Header="Reset"/>
<MenuItem Header="Clear"/>
</x:Array>
and setting to ItemSource Property in MenuItem Style of the ItemContainerStyle of the ContextMenu.
Nothing seems to work.
Can someone please show me the XAML way to do this.
Thanks
You have to define HierarchicalDataTemplate to bind child collection and directly bind outer collection to ItemsSource of Context menu like this:
<TextBlock Text="Context menu test">
<TextBlock.ContextMenu>
<ContextMenu ItemsSource="{Binding YourCollection}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate
ItemsSource="{Binding ChildCollection}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
Assuming Name is a property in your underlying source object.

ContextMenu in Window Resources, bind to DataGrid property

I would like to reuse a ContextMenu on several DataGrid.
So I placed the context menu in the Resources of my Window.
I have trouble to bind to the SelectedItem property of the DataGrid on which the ContextMenu is placed.
In this example, I'm trying to have the Name property of the SelectedItem displayed in the context menu.
<Window.Resources>
<ContextMenu x:Key="DgContextMenu"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding SelectedItem.Name, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Window.Resources>
<DataGrid ItemsSource="{Binding CollectionView}"
ContextMenu="{StaticResource DgContextMenu}"
Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
Thanks in advance
The way you have written your example has binding error and that's why your context menu doesn't work. You have binded menu item header to SelectedItem.Name of ContextMenu object which doesn't have SelectedItem property (you can tell that from RelativeSource part of the menu item binding). One possible solution, among others, would be to bind DataContext of ContextMenu to DataGrid through PlacementTarget (not PlacementTarget.Tag). Since child controls „inherit“ DataContext of the parent you can just specify Path in the menu item binding. This is how it would look:
<Window.Resources>
<ContextMenu x:Key="DgContextMenu"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{Binding Path=SelectedItem.Name}" />
</ContextMenu>
</Window.Resources>
<DataGrid ItemsSource="{Binding CollectionView}"
ContextMenu="{StaticResource DgContextMenu}"
>
</DataGrid>
Basically you can find those errors if you run application in VS debugger and watch output in Output window (Debug -> Window -> Output). In output window you should look for System.Windows.Data Error line and in that line you will see the type of an object and property you are trying to bind and that will give you a clue what's wrong with your binding in XAML.

Changing content according to selected option in WPF

I'm interested in creating an app that displays some buttons and changes a viewport according to the selected button. The viewport in my app is a ContentControl and I thought of changing its content whenever a button is clicked. However, I believe there's a better approach, by perhaps injecting the ViewModels of each of the Views I want to present to the ContentControl and styling them using DataTemplates (Since I want to avoid having a grid with many controls and just setting their Visibility property whenever I want to show a particular view). Which of the approaches seems better to you? Do you have a different approach for this?
The view should be something similar to this:
Thanks!
Usually have a ViewModel behind the window which contains:
ObservableCollection<IViewModel> AvailableViewModels
IViewModel SelectedViewModel
ICommand SetCurrentViewModelCommand
I display the AvailableViewModels using an ItemsControl, which has its ItemTemplate set to a Button. The Button.Command is bound to the SetCurrentViewModelCommand, and it passes the current data item from the AvailableViewModels collection in through the CommandParameter
To display the content area, I use a ContentControl with ContentControl.Content bound to SelectedViewModel, and DataTemplates get used to tell WPF how to render each ViewModel.
The end result is my XAML looks something like this:
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding AvailableViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.SetCurrentViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<ContentControl Content="{Binding SelectedViewModel}" />
</DockPanel>
You can view an example of the full code used for such a setup on my blog

Categories

Resources