SelectionChanged command not triggered when changing ListBox's SelectedItem programmatically - c#

I have a simple WPF page with a custom control (BrowsingPanel) containing a ListBox, and another control (ItemDataSheet) which displays data related to the element which is selected in the ListBox. When I click on an item in the ListBox, a command is sent to the BrowsingPanelViewModel, which sends a message. The message is received by the ItemDataSheetViewModel, which updates the ItemDataSheet view.
This is my BrowsingPanel.xaml:
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
Background="DarkGray">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"
CommandParameter="{Binding ElementName=itemsList, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</Grid>
It works well, except that I would like the first ListBox item to be selected by default. To do so, I've tried two things:
First, I've tried to select the first item in the BrowsingPanelViewModel's constructor as shown below.
public RelayCommand<MyItem> SelectedItemChangedCommand { get; private set; }
public BrowsingPanelViewModel()
{
SelectedItemChangedCommand = new RelayCommand<MyItem>(SelectedItemChanged);
MyItems = new ObservableCollection<MyItem>();
MyItems.Add(ParsetemFromResourceName("Resources/toto.txt"));
MyItems.Add(ParseItemFromResourceName("Resources/tata.txt"));
MyItems.Add(ParseItemFromResourceName("Resources/titi.txt"));
//Select the first item if there's one
if (MyItems.Any())
SelectedItemChanged(MyItems.First());
}
void SelectedItemChanged(MyItem selectedItem)
{
Messenger.Default.Send(new NotificationMessage<MyItem>(selectedItem, Notification.SelectedMyChanged));
}
This works fine, the ItemDataSheetViewModel displays the data corresponding to this item, but the item is not (visually) selected in the ListBox.
Then, I've tried to select the first item from the BrowsingPanel view. In the code behind, I have a handler for itemsList_Loaded which looks like this:
private void itemsList_Loaded(object sender, RoutedEventArgs e)
{
//Select the first item by default
itemsList.Focus();
if (itemsList.Items.Count > 0)
itemsList.SelectedItem = itemsList.Items[0];
}
And this is where I get a weird behavior. This selects the item correctly in the ListBox, but the SelectedItemChanged command is not triggered. And I don't understand why.
The funny part is that if I replace my EventTrigger with a SelectionChanged event that I put in the code behind as shown below, then the callback function is called.
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
Background="DarkGray"
Loaded="itemsList_Loaded"
SelectionChanged="itemsList_SelectionChanged"> <!-- This is called when changing SelectedItem in the Loaded -->
</ListBox>
</Grid>
Obviously, by combining the 2 solutions I have mentioned, it works: the bit in the view model constructor displays the appropriate data in the ItemDataSheet view, while the bit in the itemsList_Loaded visually selects the item in the List. But I don't find this very elegant...
It seems to me that programmatically changing the ListBox's SelectedIndex should trigger the SelectionChanged command, but it doesn't.
Any help will be appreciated!

Bare in mind this is a solution for a single select listbox.
You really don't need most of that code.
It can be as simple as only needing the SelectedValue property on the listbox:
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
SelectedValue="{Binding Path=MySelectedItem, Mode=TwoWay}"
Background="DarkGray">
</ListBox>
</Grid>
This can then be bound to your BrowsingPanelViewModel with the MySelectedItem property:
private MyItem m_MySelectedItem;
public MyItem MySelectedItem
{
get
{
return m_MySelectedItem;
}
set
{
m_MySelectedItem = value;
NotifyPropertyChanged("MySelectedItem");
}
}
The notifypropertychanged in the setter is key here.
You can then from your viewmodel select the first list item by assigning this property.
You will also need a DataTemplate for your MyItem object in the scope of your ListBox which can be as simple as:
<DataTemplate DataType={x:Type MyItem}>
<Textblock Text="{Binding Path=MyItemDescription"/>
</DataTemplate>
Or whatever.

Related

Binding to Selected in WPF combobox

I have a WPF, C# Application and acombobx like:
<ComboBox SelectedItem="{Binding MySelectedItem.MyString, ValidatesOnDataErrors=True}" ItemsSource="{Binding MyCollection}" />
Is there a way to bind to Selected, or let a command fire if a item is selected.
Background:
I want to start loading data based on the selected Item, the start triggger should be something like IsSelected or LostFocus
Any help or a different/better way of archive this would be very helpfully.
Thank you very much
There are couple of ways
1 - define your binded property as a full property and do the manipulations when the value changes:
private string myString;
public string MyString
{
get { return myString; }
set
{
myString = value;
// do your stuff here...
}
}
2 - Use interactivity and convert event to command:
<ComboBox ItemsSource="{Binding MyCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Don't fordet to add the needed namespace:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
And implement the SelectedItemChangedCommand in your ViewModel

CommandParameter's bound SelectedIndex is returning last selection, or -1

I've implemented MVVM in WPF and have a ListView as follows:
<ListView DockPanel.Dock="Top" Name="ListViewMain" ItemsSource="{Binding Items}">
... GridView in ListView.View ...
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding OnSelectionChangedCommand}"
CommandParameter="{Binding SelectedIndex,
ElementName=ListViewMain}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
Whenever I change selection by clicking on any item on the ListView, OnSelectionChangedCommand is called with correct SelectedIndex.
But when I change SelectedIndex programatically as follows:
ListViewMain.SelectedIndex = 0;
I get -1 in OnSelectionChangedCommand. How do I get correct SelectedIndex irrespective of selection change method?
Update
The answers in WPF Listview SelectionChanged event don't explain what'll happen when SelectedIndex is set programmatically and how to define the view model's property which is bound do SelectedIndex.
Finally I've got the solution. The ListView should bind SelectedIndex with a property in view model:
<ListView DockPanel.Dock="Top" Name="ListViewMain" ItemsSource="{Binding Items}"
SelectedIndex="{Binding SelectedIndex}">
The ListView shouldn't trigger on SelectionChanged event.
The ListView.DataContext.SelectedIndex (the view model's SelectedIndex) property should call SelectionChanged handler:
public int SelectedIndex
{
get => _selected_index;
set
{
SetProperty(ref _selected_index, value);
OnSelectionChangedCommand.Execute(_selected_index);
}
}

DataTemplate passing incorrect command parameter in WPF

I have the following in a WPF project:
Main Window
<Window x:Class="DataTemplateEventTesting.Views.MainWindow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
xmlns:vm="clr-namespace:DataTemplateEventTesting.ViewModels"
xmlns:vw="clr-namespace:DataTemplateEventTesting.Views">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions> ... </Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding SubViewModels}"
SelectedValue="{Binding MainContent, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type vm:SubViewModel}">
<TextBlock Text="{Binding DisplayText}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ContentControl Grid.Column="1" Content="{Binding MainContent}">
<ContentControl.Resources>
<DataTemplate x:Shared="False" DataType="{x:Type vm:SubViewModel}">
<vw:SubView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>
SubView (View for SubViewModel)
<UserControl x:Class="DataTemplateEventTesting.Views.SubView"
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<Grid>
<ListView ItemsSource="{Binding Models}">
<ListView.View> ... </ListView.View>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
Command="{Binding PrintCurrentItemsCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
</Grid>
</UserControl>
The problem is with the SelectionChanged EventTrigger in SubView.
PrintCurrentItemsCommand accepts a ListView as a parameter and prints a count of its items by executing the following method:
private void PrintCurrentItems(ListView listView)
{
System.Diagnostics.Debug.WriteLine("{0}: {1} items.", DisplayText, listView.Items.Count);
}
When I navigate from one SubView (where some items in its ListView are selected) to another SubView, the SelectionChanged event is fired on the ListView of the first SubView. This executes the PrintCurrentItemsCommand on the correct SubViewModel, but passes the new (incorrect) ListView as a parameter. (Either that, or the event is being fired by the new ListView, and the command is using the DataContext from the old ListView.)
Thus, while SubViewModel with DisplayText of "Sub1" has 2 items in its Models collection, and "Sub2" has 3 items, I see the following in the Output window:
Sub1: 2 items. // selected an item
Sub1: 3 items. // navigated to Sub2
Sub2: 3 items. // selected an item
Sub2: 2 items. // navigated to Sub1
Sub1: 2 items. // selected an item
Sub1: 3 items. // navigated to Sub2
Sub2: 3 items. // selected an item
Sub2: 2 items. // navigated to Sub1
Obviously the expected behaviour would be that the correct ListView would be passed.
The main confusion is that, for example, the command for "Sub1" is able to access the ListView for "Sub2" at all.
I read something about WPF caching templates, and thought I had found the solution in setting x:Shared = "False" on the DataTemplate, but this didn't change anything.
Is there an explanation for this behaviour? And is there a way around it?
I was able to reproduce the behavior you're seeing: I select an item in the right hand listview, and then change the selection the left hand listview. When the command is invoked, inside the Execute method, ! Object.ReferenceEquals(this, listView.DataContext). I would have expected them to be equal.
With this binding for Command, they were still not equal:
<i:InvokeCommandAction
Command="{Binding DataContext.PrintCurrentItemsCommand, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
/>
I didn't expect much from that experiment, but it didn't take very long to try.
Unfortunately I don't have time at the moment to investigate this in depth. I haven't been able to find the source code for System.Windows.Interactivity.InvokeCommandAction, but it certainly looks as if somewhere in the flurry of events and updates accompanying the change, things happen in the wrong order.
Resolution
The following code is almost unbearably ugly, but it behaves as expected. You could make it less ugly by writing your own behavior. It wouldn't need to be as gloriously generalized as InvokeCommandAction. Being less generalized, it would be less likely to misbehave the same way, and even if it did, you've got the source and can debug it properly.
SubView.xaml
<ListView
ItemsSource="{Binding Models}"
SelectionChanged="ListView_SelectionChanged"
>
<!-- snip -->
SubView.xaml.ds
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listView = sender as ListView;
var cmd = listView.DataContext?.GetType().GetProperty("PrintCurrentItemsCommand")?.
GetValue(listView.DataContext) as ICommand;
if (cmd?.CanExecute(listView) ?? false)
{
cmd.Execute(listView);
}
}
Slightly off topic, this would be preferable:
protected void PrintCurrentItems(System.Collections.IEnumerable items)
{
//...
XAML
<i:InvokeCommandAction
Command="{Binding PrintCurrentItemsCommand}"
CommandParameter="{Binding Items, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
/>
Code behind
if (cmd?.CanExecute(listView) ?? false)
{
cmd.Execute(listView.Items);
}
The reasoning is that a command that takes IEnumerable as a parameter is vastly more generally useful than one that expects any collection of items to be packaged in a listview. It's easy to get a collection of items from a listview; it's a real pain to need to have a listview around before you can pass somebody a collection of items. Always accept the least specific parameter you can without shooting yourself in the foot.
And from an MVVM standpoint, it's considered very bad practice for a viewmodel to have any concrete knowledge of UI. What if the UI design team decides later on that it should use a DataGrid or a ListBox instead of a ListView? If they're passing you Items, it's a total non-issue. If they're passing you ListView, they have to shoot you an email asking you to change your parameter type, then coordinate with you about it, then there's extra testing, etc. And all to accommodate a parameter that didn't actually need to be ListView at all.
It turns out that the problem was caused by the persistence of the DataTemplate.
As Ed Plunkett observed, it was the same ListView the whole time, and only the DataContext was changing. I imagine that what was happening was that the navigation took place, then the event was fired, and by this time the DataContext had changed - a simple property change.
In the hoped for behaviour, the old ListView would fire the event, and execute the first ViewModel's command, and this would occur after the navigation, hence, its items would be counted at 0. But with DataTemplate sharing, the first ListView is the second ListView, so its items are not counted at 0, they've been replaced with the items from the second ViewModel. This occurs after the navigation, so it would be expected that the RelativeSource would return the ListView with the second ViewModel as its DataContext.
I have managed to override this default behaviour by using a custom DataTemplateSelector class:
public class ViewSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (container is FrameworkElement element && item is SubViewModel)
{
return element.FindResource("subviewmodel_template") as DataTemplate;
}
return null;
}
}
The DataTemplate is stored in a ResourceDictionary (merged in App.xaml):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataTemplateEventTesting.Views"
xmlns:vm="clr-namespace:DataTemplateEventTesting.ViewModels">
<DataTemplate x:Shared="False" x:Key="subviewmodel_template" DataType="{x:Type vm:SubViewModel}">
<local:SubView />
</DataTemplate>
</ResourceDictionary>
It turns out that in a ResourceDictionary, x:Shared="False" has the critical effect that I want it to have (apparently this is effective only in a ResourceDictionary) - it keeps the templates isolated per ViewModel.
The Main Window is now written as:
<Window x:Class="DataTemplateEventTesting.Views.MainWindow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
xmlns:vm="clr-namespace:DataTemplateEventTesting.ViewModels"
xmlns:vw="clr-namespace:DataTemplateEventTesting.Views">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<vw:ViewSelector x:Key="view_selector" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions> ... </Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding SubViewModels}"
SelectedValue="{Binding MainContent, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type vm:SubViewModel}">
<TextBlock Text="{Binding DisplayText}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ContentControl Grid.Column="1" Content="{Binding MainContent}"
ContentTemplateSelector="{StaticResource view_selector}" />
</Grid>
</Window>
Interestingly, I found that both of the following need to be in place in this particular example:
One
The DataTemplate is in a ResourceDictionary with x:Shared="False".
Two
The DataTemplateSelector is used.
E.g., when I satisfy the first condition, and use <ContentControl ... ContentTemplate="{StaticResource subviewmodel_template}" />, the issue prevails.
Similarly, when x:Shared="False" is not present, the DataTemplateSelector is no longer effective.
Once these two conditions are in place, the Output window shows me:
Sub1: 2 items. // selected an item
Sub1: 0 items. // navigated to Sub2
Sub2: 3 items. // selected an item
Sub2: 0 items. // navigated to Sub1
Sub1: 2 items. // selected an item
Sub1: 0 items. // navigated to Sub2
Sub2: 3 items. // selected an item
Sub2: 0 items. // navigated to Sub1
This is the expected behaviour, which I'd previously observed when switching between ViewModels of different types.
Why DataTemplateSelector?
After reading the documentation for x:Shared, I have at least a theory on why DataTemplateSelector seems to be required for this to work.
As stated in that documentation:
In WPF, the default x:Shared condition for resources is true. This condition means that any given resource request always returns the same instance.
The key word here would be request.
Without using a DataTemplateSelector, WPF has certainty on which resource it needs to use. Therefore, it only needs to fetch it once - one request.
With a DataTemplateSelector, there is no certainty, as there may be further logic within the DataTemplateSelector even for ViewModels of the same type. Therefore, DataTemplateSelector forces a request to be made with each change in Content, and with x:Shared="False", the ResourceDictionary will always return a new instance.

React on user clicking already selected ListViewItem, MVVM

I have a ListView that looks like this, that controls which tab in my application that is opened.
<ListView Grid.ColumnSpan="2" Grid.Row="1" SelectedItem="{Binding Path=SelectedSubstanceName}" Name="listView" ItemsSource="{Binding Path=Substances}" HorizontalAlignment="Stretch" Margin="2" VerticalAlignment="Stretch">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Lägg till" Command="{Binding AddSubstanceCommand}"/>
<MenuItem Header="Ta bort" Command="{Binding RemoveSubstanceCommand}"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I use the SelectedSubstanceName property to detect which tab to open, or switch to, if it's already open.
The property looks like this:
private SubstanceName selectedSubstanceName;
public SubstanceName SelectedSubstanceName
{
get
{
return selectedSubstanceName;
}
set
{
selectedSubstanceName = value;
OnPropertyChanged("SelectedSubstanceName");
if (selectedSubstanceName != null)
{
if (!Tabs.Any(t => t.Identify(selectedSubstanceName.SubstanceNameID, typeof(SubstanceTabsViewModel))))
AddTab(selectedSubstanceName);
else
SelectedTab = Tabs.First(t => t.Identify(selectedSubstanceName.SubstanceNameID, typeof(SubstanceTabsViewModel)));
}
}
}
The case I'm not able to cover is when the user clicks "someSubstance", the corresponding tab is opened, the user closes it, and "someSubstance" is still selected. If the user wants to open it again, he has to select some other substance (which will then be opened), and then click "someSubstance" again. Is it possible to trigger the property even when clicking the same ListViewItem?
I know I could add an event on double-click, but ideally, I want to avoid both events and double-clicks.
I think the problem is that after clicking an item the first time the list's SelectedItem gets set. After clicking the same item the second time SelectedItem won't change because it is already set to that item. What you should do is set the SelectedItem to null after handling the click.
Try to unselect all Items in your ListView after the tab is closed.
YOURLISTVIEW.UnselectAll();
So the next time someone selects an Item there will be a change.
You don't actually want to use the ListView class, but instead simply use the ItemsControl, since it is the most basic way of representing a sequence of elements, but without the extras such as SelectedItem, SelectedValue, etc. that any class deriving from Selector has.
From there, it's merely a matter of how to represent each item in the ItemsControl. The behavior you want is to know when a specific item has been clicked on, which would make the Button class a good candidate, since it handles click behavior through an ICommand interface. Obviously, since you know about DataTemplates and styling in general, you should already know that you can customize how the button looks (visually) without sacrificing the actual behavior (click-handling).
<ItemsControl ItemsSource="{Binding Path=Substances}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource SomeStyleToChangeItsLook}"
Command="{Binding Path=SelectSubstanceCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public ICommand SelectSubstanceCommand { get; private set; }
private void SelectSubstance(object parameter)
{
// Add the substance that was "clicked" on here however you want to do it.
}
Keep in mind I don't know what framework you are using, so I just gave a general example of how the Command code might look in your view-model. The key to MVVM and using WPFs awesome UI is to always think of what behavior you want and which controls offer that behavior. Ignore how they actually look because that can be changed without losing that behavior.

Binding Command to Viewmodel with ItemsSource from a StaticResource

I am trying to add an ItemsSource to a MenuItem while keeping the Command bound to my ViewModel (my Window's DataContext). So far, I haven't figured out a way to make it work. Before the ItemsSource is added, the binding is fine. The collection that I am trying to bind comes from a StaticResource. Can anybody help me out with this?
<MenuItem Command="{Binding OpenTeamPage}"
DisplayMemberPath="Name"
Header="Teams"
ItemsSource="{Binding Teams,
Source={StaticResource Container}}" />
I have tried using this and variations of it with no luck:
Command="{Binding OpenTeamPage,
RelativeSource={RelativeSource AncestorType=Window},
Mode=Default}"
If anybody could tell me how to use this ItemsSource while still binding my Command to my ViewModel, I would greatly appreciate it. I suppose I could put the Command in my Team model, but I would like to avoid that if possible.
EDIT : To clarify my problem, with the ItemsSource in place, the command in the ViewModel doesn't fire at all. Without the ItemsSource, the command fires. I would like to be able to have the ItemsSource and still be able to fire the command.
EDIT:
public class GameContainer
{
static GameContainer()
{
Teams = new ObservableCollection<Team>();
}
public static ObservableCollection<Team> Teams { get; set; } 
}
In App.xaml:
<data:GameContainer x:Key="Container" />
The collection is populated when the program is started.
My goal once I get this working is to pass the selected team to the Viewmodel, hopefully via CommandParameter, and display info regarding the selected team.
EDIT: I was mistaken in my original post. A bound collection coming from the Viewmodel does not work either.
This is the behaviour of MenuItem, Item having Child MenuItem won't fire Command and it also should not as it does not make sense. But if you still want to fire a command on Parent Item click,there are two options
You can use Interactivity Triggers on your MenuItem to call command on MouseDown event like
<MenuItem
DisplayMemberPath="Name"
Header="Teams"
ItemsSource="{Binding Teams,
Source={StaticResource Container}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<cmd:EventToCommand Command="{Binding OpenTeamPage}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
you can define a Attached Property for command and define the MenuItem MouseDown behaviour like
public static class MouseCommandBehavior
{
public static readonly DependencyProperty MouseDownCommandProperty =
DependencyProperty.RegisterAttached("MouseDownCommand",
typeof(ICommand),
typeof(MouseCommandBehavior),
new FrameworkPropertyMetadata(null, (obj, e) => OnMouseCommandChanged(obj, (ICommand)e.NewValue, false)));
public static ICommand GetMouseDownCommand(DependencyObject d)
{
return (ICommand)d.GetValue(MouseDownCommandProperty);
}
public static void SetMouseDownCommand(DependencyObject d, ICommand value)
{
d.SetValue(MouseDownCommandProperty, value);
}
private static void OnMouseCommandChanged(DependencyObject d, ICommand command)
{
if (command == null) return;
var element = (FrameworkElement)d;
element.PreviewMouseDown += (obj, e) => command.Execute(null);
}
}
}
and you can set this Property value on your menuItem
<MenuItem local:MouseCommandBehavior.MouseDownCommand="{Binding OpenTeamPage}"
DisplayMemberPath="Name"
Header="Teams"
ItemsSource="{Binding Teams,
Source={StaticResource Container}}">
MenuItem will not execute its command if it's not a leaf node. Only menu items that are leafs (items with no children) are executing a command.
This is probably done due to convention - when you click an items that has children you get the children shown immediately, otherwise there's a delay from mouse hover till children shown.
Although it's probably a bad idea (from UX point of view) to have command on a parent, it's possible:
<MenuItem DisplayMemberPath="Name"
Header="{Binding OpenTeamPage}"
ItemsSource="{Binding Teams, Source={StaticResource Container}}" >
<MenuItem.HeaderTemplate>
<DataTemplate>
<!--Probably need to make this button transparent-->
<Button Content="Teams"
Command="{Binding }"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
<!--This style is for the children to fire the same command as the parent-->
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command"
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}, Path=Header}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Depending upon your design, you'd might need to style the button to be transparent.

Categories