ck84vi
ck84vi

Reputation: 1576

Binding a TreeView to a collection with different types of objects on same level

I want to bind my Treeview control to a collection that holds objects of type MeasurementResult. The MeasurementResult object itself has two collections, one for MeasurementInfo and one for DeviceInfo types.

After googling and searching on SO I found out that the best solution might be a CompositeCollection. The problem I have with that is that I just can't figure out how to define the (Hierarchical?!)DataTemplate's in a way that my data get shown in the Treeview in the way I want it.

Ultimately I would like to have a TreeView structure like that:

-MeasurementResult1
---MeasurementInformation
------MeasurementInformation1
------MeasurementInformation2
------MeasurementInformation3
---DeviceInformation
------DeviceInformation1
------DeviceInformation2
------DeviceInformation3
-MeasurementResult2
---MeasurementInformation
------MeasurementInformation1
------MeasurementInformation2
------MeasurementInformation3
---DeviceInformation
------DeviceInformation1
------DeviceInformation2
------DeviceInformation3
-MeasurementResultN

But the problem is that my current Treeview looks like that:

TreeView

The nested properties for MeasurementData and DeviceData are not shown in my TreeView.

The code that I have so far, XAML:

<local:TreeViewSampleData x:Key="TreeViewSampleData"/>

<DataTemplate x:Key="MeasurementDataTemplate">
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Finished: " Margin="0,0,10,0"/>
            <TextBlock Text="{Binding Finished}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Median: " Margin="0,0,10,0"/>
            <TextBlock Text="{Binding Median}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Maximum: " Margin="0,0,10,0"/>
            <TextBlock Text="{Binding Maximum}" />
        </StackPanel>
    </StackPanel>
</DataTemplate>

<HierarchicalDataTemplate x:Key="DeviceDataTemplate" DataType="{x:Type local:DeviceData}" ItemTemplate="{StaticResource MeasurementDataTemplate}" 
       ItemsSource="{Binding MeasurementData}">
    <TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>

<HierarchicalDataTemplate x:Key="MeasurementResultTemplate" DataType="{x:Type local:MeasurementResult}" ItemTemplate="{StaticResource DeviceDataTemplate}" 
       ItemsSource="{Binding Measurements}">
    <TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>


    <telerik:RadTreeView x:Name="tvMeasResults" 
                            ItemsSource="{Binding Source={StaticResource TreeViewSampleData}, Path = MeasurementResults}" 
                            ItemTemplate="{StaticResource MeasurementResultTemplate}"
                         >
    </telerik:RadTreeView>

My related classes:

public class MeasurementResult
{
    public string Name { get; set; } = "Measurement Result";
    internal ObservableCollection<MeasurementInfo> MeasurementInfo { get; set; }
    internal ObservableCollection<DeviceInfo> DeviceInfo { get; set; }

    public CompositeCollection Measurements
    {
        get
        {
            var items = new CompositeCollection();
            items.Add(new CollectionContainer { Collection = MeasurementInfo });
            items.Add(new CollectionContainer { Collection = DeviceInfo });
            return items;
        }
    }

    public MeasurementResult()
    {
        MeasurementInfo = new ObservableCollection<MeasurementInfo>();
        DeviceInfo = new ObservableCollection<DeviceInfo>();
    }
}

public class MeasurementInfo
{
    public string Name { get; set; } = "Measurement Information";
    public ObservableCollection<MeasurementData> ThicknessData { get; set; }

    public MeasurementInfo()
    {
        ThicknessData = new ObservableCollection<MeasurementData>();
    }
}

public class MeasurementData
{
    public DateTime Finished { internal set; get; }
    public double Median { internal set; get; }
    public double Maximum { internal set; get; }

    public MeasurementData()
    {
        Finished = DateTime.Now;
        Median = 150;
        Maximum = 200;
    }
}

public class DeviceInfo
{
    public string Name { get; set; } = "Device Information";
    public ObservableCollection<DeviceData> DeviceData { get; set; }

    public DeviceInfo()
    {
        DeviceData = new ObservableCollection<DeviceData>();
    }
}

public class DeviceData
{
    public DateTime Finished { internal set; get; }
    public int Port { internal set; get; }
    public int Slot { internal set; get; }

    public DeviceData()
    {
        Finished = DateTime.Now;
        Port = 1;
        Slot = 1;
    }
}

What is wrong with my bindings? I guess the DataTemplates are wrong but I couldn't figure out how to define them to get my expected result.

Upvotes: 1

Views: 452

Answers (1)

aybe
aybe

Reputation: 16652

This will allow you to add specific items to specific leaves and they will be concatenated by GetEnumerator so the TreeView presents things in the way you expected.

using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace WpfApp1
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            var item = new InformationTreeItem("ROOT")
            {
                Children =
                {
                    new InformationTreeItem("Level 1")
                    {
                        DeviceInformation =
                        {
                            new DeviceInformation("Device 1/1"),
                            new DeviceInformation("Device 1/2")
                        },
                        MeasurementInformation =
                        {
                            new MeasurementInformation("Measure 1/1"),
                            new MeasurementInformation("Measure 1/2")
                        },
                        Children =
                        {
                            new InformationTreeItem("Level 2")
                            {
                                DeviceInformation =
                                {
                                    new DeviceInformation("Device 2/1"),
                                    new DeviceInformation("Device 2/2")
                                },
                                MeasurementInformation =
                                {
                                    new MeasurementInformation("Measure 2/1"),
                                    new MeasurementInformation("Measure 2/2")
                                },
                                Children =
                                {
                                    new InformationTreeItem("Level 3")
                                }
                            }
                        }
                    }
                }
            };

            DataContext = item;
        }
    }

    public interface IInformation
    {
        string Description { get; }
    }

    public class InformationTreeItem : IEnumerable<IInformation>, IInformation
    {
        public InformationTreeItem(string description)
        {
            Description = description;
        }

        private InformationTreeItem(string description, IList<IInformation> children)
        {
            Description = description;
            Children = children;
        }

        public IList<IInformation> Children { get; } = new List<IInformation>();

        public IList<DeviceInformation> DeviceInformation { get; } = new List<DeviceInformation>();

        public IList<MeasurementInformation> MeasurementInformation { get; } = new List<MeasurementInformation>();

        public string Description { get; }

        public IEnumerator<IInformation> GetEnumerator()
        {
            var list = new List<IInformation>();

            if (DeviceInformation.Any())
            {
                list.Add(new InformationTreeItem(nameof(DeviceInformation), new List<IInformation>(DeviceInformation)));
            }

            if (MeasurementInformation.Any())
            {
                list.Add(new InformationTreeItem(nameof(MeasurementInformation), new List<IInformation>(MeasurementInformation)));
            }

            foreach (var child in Children)
            {
                list.Add(child);
            }

            return list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public override string ToString()
        {
            return Description;
        }
    }

    public class DeviceInformation : IInformation
    {
        public DeviceInformation(string description)
        {
            Description = description;
        }

        public string Description { get; }

        public override string ToString()
        {
            return Description;
        }
    }

    public class MeasurementInformation : IInformation
    {
        public MeasurementInformation(string description)
        {
            Description = description;
        }

        public string Description { get; }

        public override string ToString()
        {
            return Description;
        }
    }
}

enter image description here

Upvotes: 2

Related Questions