I've nested menuitems bounded to observable collection named 'CollectionOfAuthors'.
Here's the MenuItem Hierarchy: Author -->AuthorName1-->BookName1,BookName2,BookName3
Author is TopLevelMenuItem which opens in list of Author names such that each Author name opens into list of Books.
While Clicking on each BookName menuitem through NavigateToBook command, I want to send the BookName, AuthorName and AuthorID to ViewModel as command parameters, But I am finding empty values as (DependencyProperty.UnsetValue) passed to ViewModel.
Need to know what correction is required?
View.xaml
<Menu>
<MenuItem Header="Authors" x:Name="TopLevelMenuItem"
ItemsSource="{Binding CollectionOfAuthors, Mode=TwoWay}">
<in:Interaction.Triggers>
<in:EventTrigger EventName="PreviewMouseLeftButtonDown">
<in:InvokeCommandAction Command="{Binding DataContext.RefreshAuthorsList,RelativeSource={RelativeSource AncestorType=Menu}}"/>
</in:EventTrigger>
</in:Interaction.Triggers>
<MenuItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Books}">
<StackPanel>
<TextBlock x:Name="tbAuthor" Text="{Binding AuthorName}"/>
<TextBlock x:Name="tbAuthorID" Text="{Binding AuthorID}" Visibility="Collapsed"/>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="tbBookName" Text="{Binding}">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding DataContext.NavigateToBook, RelativeSource={RelativeSource AncestorType=Menu}}" MouseAction="LeftClick" >
<MouseBinding.CommandParameter>
<MultiBinding Converter="{StaticResource MultiCommandConverter}">
<Binding Path="Text" ElementName="tbBookName"/>
<Binding Path="DataContext.AuthorName" RelativeSource="{RelativeSource AncestorLevel=2, AncestorType=MenuItem}" />
<Binding Path="DataContext.AuthorID" RelativeSource="{RelativeSource AncestorLevel=2, AncestorType=MenuItem}" />
</MultiBinding>
</MouseBinding.CommandParameter>
</MouseBinding>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
ViewModel.cs
public ICommand NavigateToBook
{
get { return new DelegateCommand(NavigateToBookExecute); }
}
private void NavigateToBookExecute(object obj)
{
string selectedBookName = ((object[])obj)[0].ToString();
string selectedAuthorName = ((object[])obj)[1].ToString();
string selectedAuhorID = ((object[])obj)[2].ToString();
}
public ICommand RefreshAuthorsList
{
get { return new DelegateCommand(RefreshAuthorsListExecute); }
}
private void RefreshAuthorsListExecute(object m)
{
CollectionOfAuthors = new ObservableCollection<Author>();
//Here AuthorDetails is another global collection which gets loaded during constructor call
foreach (var objAuthorItem in AuthorDetails)
{
CollectionOfAuthors.Add(new Author
{
AuthorName = objAuthorItem.DisplayName,
Books = objAuthorItem.ListOfBooks,
AuthorID = objAuthorItem.Id,
});
}
}
private ObservableCollection<Author> _collectionOfAuthors;
public ObservableCollection<Author> CollectionOfAuthors
{
get { return _collectionOfAuthors; }
set { SetProperty(ref _collectionOfAuthors, value); }
}
Author.cs
public class Author
{
public string AuthorName { get; set; }
public string AuthorID { get; set; }
List<string>Books = new List<string>();
}
MultiCommandConverter.cs
public class MultiCommandConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.Clone();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Since you have Command at top level menu item, this Command will try to call even before your inner Command, no mather what event should trigger it. As Workaround you could pass
IsSubmenuOpen
property of TopMenuItem as CommandParameter, and check if Menu is opened, and then in Command's execute action you could check if menu is Opened then continue or return. This will stop your items from being refreshed.CallStack of your command is:
Sample solution:
View.xaml
And then in your
RefreshAuthorsListExecute