【问题标题】:Binding a TreeView to a collection with different types of objects on same level将 TreeView 绑定到同一级别上具有不同类型对象的集合
【发布时间】:2020-06-10 20:45:12
【问题描述】:

我想将我的 Treeview 控件绑定到包含 MeasurementResult 类型对象的集合。 MeasurementResult 对象本身有两个集合,一个用于 MeasurementInfo,一个用于 DeviceInfo 类型。

在 SO 上进行谷歌搜索和搜索后,我发现最好的解决方案可能是 CompositeCollection。我遇到的问题是我无法弄清楚如何定义(分层?!)DataTemplate,以使我的数据以我想要的方式显示在 Treeview 中。

最终我想要一个这样的 TreeView 结构:

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

但问题是我当前的 Treeview 看起来像这样:

MeasurementData 和 DeviceData 的嵌套属性未显示在我的 TreeView 中。

到目前为止我的代码,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>

我的相关课程:

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;
    }
}

我的绑定有什么问题?我猜 DataTemplates 是错误的,但我不知道如何定义它们以获得我的预期结果。

【问题讨论】:

    标签: c# wpf data-binding collections treeview


    【解决方案1】:

    这将允许您将特定项目添加到特定叶子,它们将由GetEnumerator 连接,因此TreeView 以您期望的方式呈现事物。

    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;
            }
        }
    }
    

    【讨论】:

    • Hard 和 Soft 列表拥有相同的类型(Container)。我需要的是在容器中保存两个不同类型的列表并将它们绑定到树视图。根据您的建议,我该如何实现?
    • 使用接口?
    • 感谢您的提示。你的方法很花哨,但我不明白如何用你的解决方案和使用界面的提示来解决我的问题。您介意提供一个使用接口的示例来说明我如何处理两种不同类型的对象吗?
    • 检查我的编辑,希望你现在不会出错。至于为什么DeviceInformationMeasureInformationDescription,那就没办法了。给这个属性一个好名字并接受它,但我想Description 已经是你可能需要的东西了。此外,树总是很难构建,只需编写一些漂亮的方法来完成你的对象的繁重工作以产生根InformationTreeItem,即不要将你的代码与树的东西粘合太多,而只能通过一个方法.
    • 太棒了,非常感谢。完成这项工作的好方法!我只是古玩,我还没有尝试过,但这是否也适用于可观察的集合而不是列表?我问是因为我想知道如果将项目添加到列表中,电视是否会更新。我想这只适用于 ObservableCollection?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 1970-01-01
    • 2011-12-16
    • 2015-04-27
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多