Collection nodes without children are displayed empty

May 8, 2010 at 6:31 PM

Hello,

I do everything the same as in ComplexDataTemplates.UnitTest is done. One only difference is using ObservableCollection<T> instead of List<T> in collections. And if 'Traits' or 'Hobbies' nodes do not contain at least one children, they do not displayed. There is empty line in place where collection-node must be.

 

This is XAML of my window: 

<Window x:Class="HierarchyBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:complex="clr-namespace:DaisleyHarrison.WPF.ComplexDataTemplates;assembly=ComplexDataTemplates"
    xmlns:local="clr-namespace:MasterDetailBinding"
    Title="Window1" Height="300" Width="200">
    <Window.Resources>
        <complex:ComplexGroupDataTemplateSelector x:Key="complex-group-data-template-selector"/>
        <complex:ComplexGroupConverter x:Key="complex-group-converter"/>
        <!--Data views-->
        <HierarchicalDataTemplate DataType="{x:Type local:Family}" 
                                  ItemsSource="{Binding Path=Members}">
            <TextBlock Text="{Binding Path=FamilyName}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type local:Person}"
                                  ItemTemplateSelector="{StaticResource complex-group-data-template-selector}">
            <HierarchicalDataTemplate.ItemsSource>
                <MultiBinding Converter="{StaticResource complex-group-converter}" >
                    <Binding Path="Traits" />
                    <Binding Path="Hobbies" />
                </MultiBinding>
            </HierarchicalDataTemplate.ItemsSource>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Name}" />    
                <TextBlock Text=" (age: " />
                <TextBlock Text="{Binding Path=Age}" />
                <TextBlock Text=")" />
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="IEnumerable[Hobby]" ItemsSource="{Binding Path=.}">
            <TextBlock>Hobbies</TextBlock>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="IEnumerable[Trait]" ItemsSource="{Binding Path=.}">
            <TextBlock>Traits</TextBlock>
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type local:Trait}">
            <TextBlock Text="{Binding Path=Description}" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Hobby}">
            <TextBlock Text="{Binding Path=Title}" />
        </DataTemplate>
        <!--Start of Data section-->
        <local:Families x:Key="families">
            <local:Family FamilyName="Stooge">
                <local:Family.Members>
                    <local:People>
                        <local:Person Name="Larry" Age="21">
                            <local:Person.Traits>
                                <local:Traits>
                                    <local:Trait Description="In Charge" />
                                    <local:Trait Description="Mean" />
                                    <local:Trait Description="Ugly" />
                                </local:Traits>
                            </local:Person.Traits>
                            <local:Person.Hobbies>
                                <local:Hobbies>
                                    <local:Hobby Title="Swimming" />
                                    <local:Hobby Title="Dancing" />
                                    <local:Hobby Title="Fishing" />
                                </local:Hobbies>
                            </local:Person.Hobbies>
                        </local:Person>
                        <local:Person Name="Curly" Age="22" />
                        <local:Person Name="Moe" Age="23" />
                    </local:People>
                </local:Family.Members>
            </local:Family>
            <local:Family FamilyName="Addams">
                <local:Family.Members>
                    <local:People>
                        <local:Person Name="Gomez" Age="135" />
                        <local:Person Name="Morticia" Age="123" />
                        <local:Person Name="Faster" Age="137" />
                    </local:People>
                </local:Family.Members>
            </local:Family>
        </local:Families>
        <!--End of Data Section-->
    </Window.Resources>

    <TreeView DataContext="{StaticResource families}" >
        <TreeViewItem ItemsSource="{Binding}" Header="Families" />
    </TreeView>
</Window>

  This is my classes:

Family:

 

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Data;

namespace MasterDetailBinding
{
    /// <summary>
    /// Reperesents family
    /// </summary>
    public class Family
    {
        string familyName;
        public string FamilyName
        {
            get { return familyName; }
            set { familyName = value; }
        }
        People members;
        public People Members
        {
            get { return members; }
            set { members = value; }
        }
    }
    public class Families : ObservableCollection<Family> { }
}

 

Person:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Data;

namespace MasterDetailBinding
{
    /// <summary>
    /// Represents person
    /// </summary>
    public class Person
    {
        string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        int age;
        public int Age
        {
            get { return age; }
            set { age = value; }
        }

        Traits traits;
        public Traits Traits
        {
            get { return traits; }
            set { traits = value; }
        }

        public Hobbies hobbies;
        public Hobbies Hobbies
        {
            get { return hobbies; }
            set { hobbies = value; }
        }
    }
    public class People : ObservableCollection<Person> { }
}

Trait:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Data;

namespace MasterDetailBinding
{
    /// <summary>
    /// Represents a trait (of person)
    /// </summary>
    public class Trait
    {
        string description;
        public string Description
        {
            get { return description; }
            set { description = value; }
        }
    }
    public class Traits : ObservableCollection<Trait> { }
}

Hobby:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace MasterDetailBinding
{
    /// <summary>
    /// Represents hobby (of person)
    /// </summary>
    public class Hobby
    {
        public string Title
        {
            get;
            set;
        }
    }

    public class Hobbies : ObservableCollection<Hobby> { }
}

Any ideas?

 

Thanks,

Roman

May 13, 2011 at 2:54 AM
Edited Jun 23, 2011 at 6:27 AM

I made a change to ComplexGroupConverter so group headers don't appear if there are no children:
Though it has the problem that if children are added the header doesn't appear.

 

public class ComplexGroupConverter : IMultiValueConverter
	{
		#region IMultiValueConverter Members

		public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
		{
			var results = new List<object>();
			string[] parameters;
			if (parameter is string)
			{
				parameters = ((string) parameter).Split(',');
				for (var i = 0; i < parameters.Length; i++)
				{
					parameters[i] = parameters[i].Trim();
				}
			}
			else
			{
				parameters = new string[0];
			}
			var index = 0;
			foreach (var value in values)
			{
				if (value is IEnumerable)
				{
					var enumerable = (IEnumerable) value;
					if (index < parameters.Length)
					{
						results.Add(new BindingGroup(enumerable, parameters[index]));
					}
					else if (Any(enumerable))
					{
						results.Add(value);
					}
				}
				else if (value != null)
				{
					results.Add(value);
				}
				index++;
			}

			return results;
		}

		/// <summary>
		/// Determines whether the specified enumerable has any items.
		/// </summary>
		/// <param name="enumerable">The enumerable.</param>
		/// <returns></returns>
		public static bool Any(IEnumerable enumerable)
		{
			return Any(enumerable, false);
		}

		/// <summary>
		/// Determines whether the specified enumerable has any items.
		/// </summary>
		/// <param name="enumerable">The enumerable.</param>
		/// <param name="reset">if set to <c>true</c> reset the enumerator if there are any items found.</param>
		/// <returns></returns>
		public static bool Any(IEnumerable enumerable, bool reset)
		{
			if (enumerable is ICollection)
				return ((ICollection) enumerable).Count > 0;
			var enumerator = enumerable.GetEnumerator();
			var any = enumerator.MoveNext();
			if (any && reset)
				try
				{
					enumerator.Reset();
				}
				catch (NotSupportedException)
				{
				}
			return any;
		}

		public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
		{
			if (value is List<object>)
			{
				var objects = value as List<object>;
				return objects.ToArray();
			}
			throw new NotSupportedException();
		}

		#endregion
	}