After working on it for a few hours I found a working solution using Multibinding and two Converters.
First, the definition of HierarchicalDataTemplate in XAML:
<HierarchicalDataTemplate>
<Grid>
<Grid.Width>
<MultiBinding Converter="{StaticResource SumConverterInstance}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ScrollContentPresenter, AncestorLevel=1}" Path="ActualWidth" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=TreeViewItem, AncestorLevel=1}" Converter="{StaticResource ParentCountConverterInstance}" />
</MultiBinding>
</Grid.Width>
.... (content of the template) ....
</Grid>
</HierarchicalDataTemplate>
The first binding in multibinding gets the width of the ScrollContentPresenter in TreeView, which is the total visible width of the TreeView. The second binding calls a converter with the TreeViewItem as an argument and computes how many parents does the TreeViewItem have before reaching the root item. Using these two inputs we use the SumConverterInstance in Multibinding to compute the available width for the given TreeViewItem.
Here are the converter instances defined in XAML:
<my:SumConverter x:Key="SumConverterInstance" />
<my:ParentCountConverter x:Key="ParentCountConverterInstance" />
and the code for the two converters:
// combine the width of the TreeView control and the number of parent items to compute available width
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double totalWidth = (double)values[0];
double parentCount = (double)values[1];
return totalWidth - parentCount * 20.0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
// count the number of TreeViewItems before reaching ScrollContentPresenter
public class ParentCountConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int parentCount = 1;
DependencyObject o = VisualTreeHelper.GetParent(value as DependencyObject);
while (o != null && o.GetType().FullName != "System.Windows.Controls.ScrollContentPresenter")
{
if (o.GetType().FullName == "System.Windows.Controls.TreeViewItem")
parentCount += 1;
o = VisualTreeHelper.GetParent(o);
}
return parentCount;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This is now the correct look: