I need to display Hierarchy in the treeview. But Details should be displayed in the datagrid.
How do I have to write my template to achieve this? I misunderstand smth in templates for now.
<TreeView ItemsSource="{Binding Path=Categories}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type stackProjects:Category}" ItemsSource="{Binding Path=SubCategories}">
<TextBlock Margin="3" Text="{Binding Path=CategoryName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type stackProjects:SubCategory}" ItemsSource="{Binding Path=Details}">
<TextBlock Text="{Binding Path=SubCategoryName}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type stackProjects:Detail}" >
<StackPanel Orientation="Horizontal">
<TextBlock Margin="3" Text="{Binding Path=Name}"/>
<TextBlock Margin="3" Text=" - "/>
<TextBlock Margin="3" Text="{Binding Path=Info}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
I've found a workaround. I had to understand that Details should be presented as a collection within a single element which has IEnumerable property. May be it's not the best solution but it works.
I needed to create a Converter to wrap my collection into single one.
public class BoxingItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var values = value as IEnumerable;
var type = parameter as Type;
if (values == null || type == null)
return null;
if (type.GetInterfaces().Any(x => x == typeof (IItemsWrapper)))
{
var instance = (IItemsWrapper) type.Assembly.CreateInstance(type.FullName);
instance.Items = (IEnumerable) value;
//returned value should be IEnumerable with one element.
//Otherwise we will not see children nodes
return new List<IItemsWrapper> {instance};
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Example for wrappers:
internal interface IItemsWrapper
{
IEnumerable Items { get; set; }
}
public class ItemsWrapper : IItemsWrapper
{
public IEnumerable Items { get; set; }
}
public class DetailItemsWrapper : ItemsWrapper{}
public class InvoiceItemsWrapper:ItemsWrapper{}
And the xaml. It will not require a lot of changes. You just need to use Boxing converter and set the Type to return in the converter parameter.
<TreeView ItemsSource="{Binding Path=Categories}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type wpfProj:Category}" ItemsSource="{Binding Path=SubCategories}">
<TextBlock Margin="4" Text="{Binding Path=CategoryName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type wpfProj:SubCategory}"
ItemsSource="{Binding Path=Details, Converter={StaticResource boxingItems}, ConverterParameter={x:Type wpfProj:DetailItemsWrapper}}" >
<StackPanel>
<TextBlock Margin="4" Text="{Binding Path=SubCategoryName}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type wpfProj:DetailItemsWrapper}" >
<DataGrid ItemsSource="{Binding Path=Items}"/>
</DataTemplate>
</TreeView.Resources>
</TreeView>
I've uploaded sample to dropbox. Here is how it does look like:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With