How to get the Content of selected Menu Item? - c#

Summary of the problem
Goal
My goal is to create a Navigation menu in WPF.
The navigation items are built in the View Model using Nodes.
Selected SubItem of the menu will display the content of the selected Node.
Expected and actual result
This is what I have built so far:
Error
Now I need to present the selected MenuItem to ContentPresenter - and this is where I have the problem with.
What I have tried
This is my current code
XAML
<!--Menu-->
<Menu x:Name="NavigationTreeView" IsMainMenu="True" ItemsSource="{Binding Navigation}" Grid.Row="0">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=Title}" />
<Setter Property="Template" Value="{StaticResource VsMenuTop}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type nav:PageViewModel}" ItemsSource="{Binding Children}" />
</Menu.Resources>
</Menu>
<!--Content-->
<ContentPresenter Grid.Row="1" Content="{Binding ElementName=NavigationTreeView, Path=SelectedItem.Content}" />
This will not work, because I am not able to bind it to Menu using this line here: SelectedItem.Content - and the Menu does not have a property SelectedItem.
Alternative
My alternative option would be to use ListView, because it contains a property SelectedItem. But preferably I'd like to use Menu.
Question
How can I get the selected item from the Hierarchical Data Template?

The Menu control does not have a notion of a selected item, as its MenuItems are essentially buttons that should execute an action when clicked. You can set the IsCheckable property to true to be a able to check menu items like a CheckBox, but I guess that does not fit your use-case here.
Since you probably want to display different content depending on which menu item was clicked, you could use a command, that is executed when you click a MenuItem and gets the corresponding view model as parameter and sets a property on a view model that can be bound by the ContentPresenter.
Introduce a Selected property of type PageViewModel and a Select command in your main view model that contains the Navigation porperty. The command sets the Selected property.
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
Selected = new RelayCommand<PageViewModel>(pageViewModel => Selected = pageViewModel);
// ...other code.
}
public DelegateCommand<PageViewModel> Select { get; }
private PageViewModel _selected;
public PageViewModel Selected
{
get => _selected;
private set
{
if (_selected == value)
return;
_selected = value;
OnPropertyChanged();
}
}
// ...other code.
}
You can replace the RelayCommand by the command implementation that you have at your disposal.
Then, you can adapt your style to bind the command on the main view model (either using ElementName or RelativeSource to the data context) and to bind the view model of the clicked MenuItem as command parameter. This way it is passed to the command, which sets it as Selected.
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=Title}" />
<Setter Property="Template" Value="{StaticResource VsMenuTop}" />
<Setter Property="Command" Value="{Binding DataContext.Select, ElementName=NavigationTreeView}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
Bind the Content of your ContentPresenter to the Selected property on the main view model.
<ContentPresenter Grid.Row="1" Content="{Binding ElementName=NavigationTreeView, Path=Selected}" />

Сan think of many implementations, but it is not entirely clear what you need.
Additional details required.
SelectedItem (property of the Selector class) contains not a UI element, but a collection source element.
Maybe you too need it , not a MenuItem?
You are not using commands, so you can use them to solve the problem.
I have implemented a simple proxy using BaseInpc and RelayCommand classes.
using Simplified;
namespace SelectedItem
{
public class SelectedItemProxy : BaseInpc
{
private object _selectedMenuItem;
public object SelectedMenuItem { get => _selectedMenuItem; set => Set(ref _selectedMenuItem, value); }
private RelayCommand _selectItemCommand;
public RelayCommand SelectItemCommand => _selectItemCommand
?? (_selectItemCommand = new RelayCommand(item => SelectedMenuItem = item));
}
}
An example of its use:
<Window x:Class="SelectedItem.SmiExamleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SelectedItem"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="SmiExamleWindow" Height="450" Width="800">
<Window.Resources>
<local:SelectedItemProxy x:Key="proxy"/>
</Window.Resources>
<Grid>
<Menu x:Name="NavigationTreeView" IsMainMenu="True" ItemsSource="{Binding Navigation}" Grid.Row="0" VerticalAlignment="Top">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="Command" Value="{Binding SelectItemCommand, Source={StaticResource proxy}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</Menu.Resources>
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
</Menu>
<ContentPresenter Content="{Binding SelectedMenuItem, Source={StaticResource proxy}}" VerticalAlignment="Bottom" />
</Grid>
</Window>

Related

MenuCollection Binding twice MVVM + Databinding Child menu items

As you can see in the image below, you can see 2 hover states. Here is the XAML
<Menu ItemsSource="{Binding Data.MenuCollection}">
<Menu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem Header="{Binding Header}" Command="{Binding Command}" ItemsSource="{Binding Children}"/>
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
The Collection of data works on the header. However I can't get the Children nodes to appear.
public void CreateTempMenuList()
{
MenuCollection = new ObservableCollection<MenuItem>()
{
new MenuItem()
{
Header = "File",
Children = new ObservableCollection<MenuItem>()
{
new MenuItem()
{
Header = "Exit"
}
}
}
};
}
The MenuItem class is something I created. Each property has a setter that called the OnPropertiesChanged Function. I can add the class if needed, but I am pretty sure thats not the problem.
So my question is. How do i get rid of the 'double' hover. In the image you can see 2 borders. An outer border which i hover over. the hover stays until focused on something else.
My second question is how can i get the child items to work? The itemssource on the menuitem tag could be wrong but its all i could think of.
Define an HierarchicalDataTemplate:
<Menu ItemsSource="{Binding Data.MenuCollection}">
<Menu.Resources>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</Menu.Resources>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
A System.Windows.Controls.MenuItem container is implicitly created for each item so you shouldn't add another MenuItem element in the template.
Also make sure that you don't bind to an ObservableCollection<System.Windows.Controls.MenuItem> because the ItemTemplate won't be applied to built-in MenuItem elements.
To make your current code work, right click your Menu control > Edit Additional Template > Edit ItemContainerStyle > Edit Copy.
And in the generated Style,
Search for this piece of code :
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="Padding" Value="7,2,8,3"/>
<Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
</Trigger>
And change Padding to 0 instead of 7,2,8,3 .

WPF - how do I give a command to menu item with children

I have a databound hierarchical menu in WPF. All items are displayed, but the commands only fire for the leafs of the menu, not the items that have children. I'm guessing the command is overriden by expanding the child menu...
How do I get the command to execute even for the menu items with children?
What I have now is
<UserControl ...>
<WrapPanel>
<Menu>
<Menu.Resources>
<Style x:Key="MenuItemStyle" TargetType="MenuItem" d:DataContext="{d:DesignInstance local:TreeItem}">
<Setter Property="Command" Value="{Binding DataContext.AddColumnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</Menu.Resources>
<MenuItem Header="Add ▼" ItemsSource="{Binding AvailableFields}">
<MenuItem.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeItem}" ItemsSource="{Binding NestedItems}" ItemContainerStyle="{StaticResource MenuItemStyle}">
<ContentPresenter Content="{Binding Annotation}"/>
</HierarchicalDataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
</WrapPanel>
</UserControl>
I found a question with a similar name, but the situation is different and it doesn't have a good answer anyway.
All items are displayed, but the commands only fire for the leafs of the menu, not the items that have children.
Yes, this is the expected behaviour since clicking on a MenuItem with children is supposed to expand the submenu of child items. It doesn't execute a command.
If you want to expand the child items and execute the command you could handle the PreviewMouseLeftButtonDown event of the MenuItem:
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.AddColumnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnMouseDown" />
</Style>
-
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
MenuItem mi = sender as MenuItem;
if (mi != null && mi.Command != null && mi.HasItems)
mi.Command.Execute(mi.CommandParameter);
}
Note that handling the event in the code-behind of the view doesn't really break the MVVM pattern here since you are just invoking the command of the view model from the code-behind instead of invoking it from the XAML markup of the same view. But if you don't like this approach you could use an attached behaviour: https://www.codeproject.com/articles/28959/introduction-to-attached-behaviors-in-wpf

Making item selected of a ListView of ToggleButtons

I have a ListView and each item has a toggle button. I want the to be able to toggle the button when the item is selected from the list, and untoggle when the item is deselected. It has to follow mvvm, so no code behind.
Here is my setup:
<ListView x:Name="stampList"
ItemsSource="{Binding AllStampImages}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="0,2,0,0">
<ToggleButton Width="72"
Height="72"
Command="{Binding StampSelectedCommand}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My problem is, when I hit the toggle button the item is not selected. Likewise, when I hit outside the toggle button (still within the boundaries of the listView item), the item is selected but the button is not toggled.
How do I tie the two together?
You might have to bind the Selector.IsSelected property to you custome property. In this case to property of you toggle button.
Try something like:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Selector.IsSelected" Value="{Binding [Value of your toggle button], Mode=TwoWay}" />
</Style>
</ListView.ItemContainerStyle>
The cleanest way to do that is to have a collection of items provided by ViewModel or Model layer.
Each item should have a IsSelected boolean property :
class LineItem
{
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; }
}
private String label;
public String Label
{
get { return label; }
set { label = value; }
}
}
There should be some binding to the model IsSelected property :
2.1 On the ListView.ItemTemplate
<ListView x:Name="list1" SelectionMode="Multiple" ItemContainerStyle="{DynamicResource ListViewItemStyle1}" Margin="0,0,334,0">
<ListView.ItemTemplate>
<DataTemplate>
<ToggleButton Margin="5" Content="{Binding Label}" IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListView.ItemTemplate>
2.2 On the ListViewItem Template
Create a Template with right clicking on the listview,
In the menu : "Edit additional templates/Edit generated Item container (Empty).
Fill with following code :"
<Style x:Key="ListViewItemStyle1" TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid x:Name="Bd">
<ContentPresenter />
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="#FF204080"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The trigger is based on Model IsSelected property.
The Grid is named Bd in order to change its background in the trigger.
Link to a full working demo : http://1drv.ms/1iyvPgt
Note
I trie to answer closely to the question .
But in my humble opinion, I would not use a ListView May be you have good reasons I gnore.
I 'd prefer to use an ItemsControl that is a listbox that doesn't allow to make a selection.
I'd put Toggle button, with custom Template to put a blue background if needed.
It would remove all the trigger stuff
, ...
Best coding

How to correctly bind a ViewModel (which Include Separators) to WPF's Menu?

I'm using MVVM and I want to data bind my list of MenuViewModels to my maim menu. Which consists of a set of menu items and separators.
Here's my MenuItemViewModel code:
public interface IMenuItemViewModel
{
}
[DebuggerDisplay("---")]
public class SeparatorViewModel : IMenuItemViewModel
{
}
[DebuggerDisplay("{Header}, Children={Children.Count}")]
public class MenuItemViewModel : IMenuItemViewModel, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MenuItemViewModel(string header, ICommand command, ImageSource imageSource)
{
Header = header;
Command = command;
ImageSource = imageSource;
Children = new List<IMenuItemViewModel>();
}
public string Header { get; private set; }
public ICommand Command { get; private set; }
public ImageSource ImageSource { get; private set; }
public IList<IMenuItemViewModel> Children { get; private set; }
}
And my Main window looks like this:
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Header}"
Command="{Binding Command}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ViewModel:SeparatorViewModel}">
<Separator />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}">
</Menu>
</DockPanel>
Should be very simple stuff. Unfortunately, either the menu item looks wrong or the separator is an empty menuItem (depending on what I've tried).
So, how do I get my Menu to find my two DataTemplates?
Solved my own question
After spending several hours searching the web, I found lots of examples that work against the WPF's natural intentions but none that worked with it.
Here's how to work with the Menu control and not against it...
A little Background
WPF's Menu control will normally auto create MenuItem objects for you when it is binded to a POCO collection, using the ItemsSource property.
However, this default behavior can be overridden! Here's how...
The Solution
First, you must create a class that derives from ItemContainerTemplateSelector. Or use the simple class I've created:
public class MenuItemContainerTemplateSelector : ItemContainerTemplateSelector
{
public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl)
{
var key = new DataTemplateKey(item.GetType());
return (DataTemplate) parentItemsControl.FindResource(key);
}
}
Second, you must add a reference to the MenuItemContainerTemplateSelector class to your Windows resources object, like so:
<Window.Resources>
<Selectors:MenuItemContainerTemplateSelector x:Key="_menuItemContainerTemplateSelector" />
Third, you must set two properties (UsesItemContainerTemplate, and ItemContainerTemplateSelector) on both the Menu and the MenuItem (which is defined in the HierarchicalDataTemplate).
Like so:
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Header}"
Command="{Binding Command}"
UsesItemContainerTemplate ="true"
ItemContainerTemplateSelector=
"{StaticResource _menuItemContainerTemplateSelector}"/>
</HierarchicalDataTemplate>
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}"
UsesItemContainerTemplate="True"
ItemContainerTemplateSelector=
"{StaticResource _menuItemContainerTemplateSelector}">
</Menu>
Why it Works
For optimization purposes, the Menu uses the UsesItemContainerTemplate flag (which has a default value of false) to skip the DataTemplate lookup and just returns a normal MenuItem object. Therefore, we needed to set this value to true and then our ItemContainerTemplateSelector works as expected.
Happy Coding!
A solution without the TemplateSelector:
provide ItemContainerTemplates instead of the DataTemplates :
<ContextMenu ItemsSource="{Binding Path=MenuItems}" UsesItemContainerTemplate="True">
<ContextMenu.Resources>
<ResourceDictionary>
<ItemContainerTemplate DataType="{x:Type ViewModel:MenuItemViewModel }">
<MenuItem Header="{Binding Path=Header}" Command="{Binding Path=Command}" UsesItemContainerTemplate="True">
<MenuItem.Icon>
<Image Source="{Binding Path=ImageSource}"/>
</MenuItem.Icon>
</MenuItem>
</ItemContainerTemplate>
<ItemContainerTemplate DataType="{x:Type ViewModel:SeparatorViewModel}">
<Separator >
<Separator.Style>
<Style TargetType="{x:Type Separator}" BasedOn="{StaticResource ResourceKey={x:Static MenuItem.SeparatorStyleKey}}"/>
</Separator.Style>
</Separator>
</ItemContainerTemplate>
</ResourceDictionary>
</ContextMenu.Resources>
</ContextMenu>
Notes:
I haven't tried Children
the separator styled wrong: I had to manually re-apply the style
Another approach is to:
have a Boolean property on your menu item ViewModel that indicates whether an item is a separator or not
use a trigger based on this property to change the ControlTemplate of the MenuItem so that it uses a Separator control instead
Like so:
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Command}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeparator}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Separator />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}" />
</Menu.Resources>
</Menu>

What is the proper way to designate the data context and bind hierarchical data in the treeview?

I have a fairly simple data model:
public class ServiceModel
{
private static readonly IQmiServiceManager ServiceManager = Bootstrap.Instance.DomainManager.QmiServiceManager;
private string _serviceName;
private ObservableCollection<string> _serviceMessages;
public string Name
{
get { return _serviceName; }
private set { _serviceName = value.ToUpper(); }
}
public ObservableCollection<string> Messages
{
get { return _serviceMessages; }
private set { _serviceMessages = value; }
}
public ServiceModel(string ServiceName, IList<string> ServiceMessages)
{
Name = ServiceName;
Messages = new ObservableCollection<string>(ServiceMessages);
}
}
...which is encapsulated in this view model:
public class ServiceCollectionViewModel
{
private readonly ObservableCollection<ServiceModel> _serviceModels = new ObservableCollection<ServiceModel>();
public ObservableCollection<ServiceModel> ServiceModels
{
get { return _serviceModels; }
}
}
I have the following treeview xaml definition:
<TreeView Name="ServiceTree" Grid.Row="1" ItemsSource="{Binding Services}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Messages}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Nothing is being output in the tree. I have tried following several tutorials on hierarchical data-binding but I'm simply having difficulty understanding the proper technique for my specific situation.
Also, how should the data-context be set? I am setting the context as follows within the code-behind of the view:
public partial class ServiceView : UserControl
{
private readonly ServiceCollectionViewModel _serviceCollection = new ServiceCollectionViewModel();
public ObservableCollection<ServiceModel> Services
{
get { return _serviceCollection.ServiceModels; }
}
public ServiceView()
{
InitializeComponent();
DataContext = _serviceCollection;
_serviceCollection.LoadServices();
}
}
You seem to be very confused. You're trying to data bind to your ServiceView.Services property, so what is the ServiceCollectionViewModel class for? The sole purpose of a view model is to provide all of the data (and functionality) required for its view.
Now we can go different routes... it all depends what you actually want. If you want to data bind from outside the UserControl to the ServiceView.Services property, then you must declare the Services property as a DependencyProperty. Inside your UserControl, you would then data bind to the property using a RelativeSource Binding like this:
Outside:
<YourPrefix:ServiceView Services="{Binding YourExternalViewModelProperty}" />
Inside:
<TreeView Name="ServiceTree" Grid.Row="1" ItemsSource="{Binding Services,
RelativeSource={RelativeSource AncestorType={x:Type YourPrefix:ServiceView}}}">
...
</TreeView>
With this method, there is no need to set the DataContext to anything as we are using RelativeSource to set the data source. Alternatively, you could set the DataContext to either the internal UserControl code behind, or an instance of a view model (from either inside or outside) and then normally data bind like this:
<TreeView Name="ServiceTree" Grid.Row="1" ItemsSource="{Binding Services}">
...
</TreeView>
So, to recap, if you set the DataContext to an instance of an object, then your Binding Paths look at the properties in that object to resolve themselves. Otherwise, if you set a RelativeSource (or ElementName) in your Binding Path, then you are changing the data source for that Binding only and your Binding Paths should be properties from those objects instead.
UPDATE >>>
The HierarchicalDataTemplate Class is basically a slight extension on the regular old DataTemplate class and can be thought of a DataTemplate with an ItemsSource property. Therefore, if you can define the content of a DataTemplate, then you can define the content of a HierarchicalDataTemplate... just make sure to set the HierarchicalDataTemplate.ItemsSource property to a valid collection property... in your case, the Messages property:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type YourPrefix:ServiceModel}"
ItemsSource="{Binding Messages}"> <!-- Collection Property In ServiceModel -->
<TextBlock Text="{Binding Name}" /> <!-- Property In ServiceModel -->
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
If you're not too clear on DataTemplate, take a look at the Data Binding Overview page on MSDN.
You have a number of issues here.
ItemsSource binding should be to ServiceModels and not Services, because that is the name of the property in your ServiceCollectionViewModel. SO this way you get the top-level items.
You are trying to bind inside your items' style to IsExpanded and IsSelected properties, but there are no such properties on your view-models.
Inside your HierarchicalDataTemplate you bind the ItemsSource property directly to the DataContext of the TreeViewItem which in this case is ServiceModel. Either make the ServiceModel implement IEnumerable<string> exposing your messages, or bind to Messages property directly:
The second level HierarchicalDataTemplate binds the Text property to Messages, but the DataContext of this level is of String type, having no Messages property. So here you should bind to the data context itself:
Regarding the data context, you better initialize it in XAML, using static resource. Also, much cleaner approach to define the hierarchical data templates is using implicit templates, based on the item types. Because you really don't want to define hierarchical templates inline for a greater number of hierarchy levels (say: 4, 5). See below the completely fixed XAML:
<Grid>
<Grid.Resources>
<!--Data context for the whole Grid-->
<myNamespace:ServiceCollectionViewModel x:Key="MyViewModel" />
</Grid.Resources>
<TreeView Name="ServiceTree"
DataContext="{StaticResource MyViewModel}"
ItemsSource="{Binding ServiceModels}">
<TreeView.Resources>
<!--Implicit style for TreeViewItem, IsExpanded and IsSelected have no binding because
they are not used by view-models-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
<!--Template for the first level items-->
<HierarchicalDataTemplate DataType="{x:Type myNamespace:ServiceModel}" ItemsSource="{Binding Messages}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<!--Template for the second level, due to the fact that String is not hierarchical
regular DataTemplate is enough-->
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
I believe this one is both more understandable and, consequently, more maintainable.

Categories