TreeView不显示对象层次结构

发布于 2024-08-04 09:51:51 字数 2627 浏览 5 评论 0原文

我在使用对象数据绑定创建 WPF TreeView 时遇到了一些严重的问题。

该应用程序是配置文件编辑器。我定义了一个可以序列化为正确 XML 格式的对象结构。

我遇到的问题是格式化 TreeView 中显示正确层次结构的对象实例。 TreeView 只会渲染 Channel 节点,不会渲染其他任何东西。

public class Objects
{
    public List<Channel> Channels { get; set; }
}

public class Channel 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public Reader Reader { get; set; }
    public Filters Filters { get; set; }
    public Router Router { get; set; }
    public Persister Persister { get; set; }
}

public class Filters : ArrayList
{
    public string StopOnFailure { get; set; }
}

public class Reader
{
    public string Id { get; set; }
    public string Name { get; set; }
}

Channel 的所有子类都包含属性 IdName。 Filters 类是具有相同属性定义的其他类型的集合。

XAML

 <Window.Resources>
    <ObjectDataProvider x:Key="data"/>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Channel}">
        <WrapPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
            <TextBlock Text=" [" />
            <TextBlock  Text="{Binding Path=Id}" />
            <TextBlock Text="]" />
        </WrapPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <TreeView Margin="12,12,12,96" Name="treeView1" ItemsSource="{Binding Source={StaticResource data}, Path=Channels}">
    </TreeView>
</Grid>

这是创建数据实例的

Objects config;
var serializer = new XmlSerializer(typeof(Objects));
using (var stream = new FileStream(@"C:\test.xml", FileMode.Open))
{
    config = (Objects)serializer.Deserialize(stream);
}

var dp = (ObjectDataProvider)FindResource("data");
dp.ObjectInstance = config;

背后的代码我已经查看了无数示例,但我仍然可以找出我做错了什么。感谢您的帮助。

更新:

<HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Objects}" ItemsSource="{Binding Path=Channels}"/>
<HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Channel}" ItemsSource="Binding Path=Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

<DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>

TreeView 没有变化。经过此更改,我仍然只列出了 Channel,而没有其他内容。

I am having some serious trouble creating a WPF TreeView with an Object databinding.

The application is config file editor. I have defined an Object structure which can be serialized to the correct XML format.

The problem I am having is formatting the object instance in the TreeView showing the correct hierarchy. The TreeView will only render the Channel node, and nothing else.

public class Objects
{
    public List<Channel> Channels { get; set; }
}

public class Channel 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public Reader Reader { get; set; }
    public Filters Filters { get; set; }
    public Router Router { get; set; }
    public Persister Persister { get; set; }
}

public class Filters : ArrayList
{
    public string StopOnFailure { get; set; }
}

public class Reader
{
    public string Id { get; set; }
    public string Name { get; set; }
}

All the child classes of Channel contain properties Id and Name. The Filters class is a collection of other types with the same property definition.

Here is the XAML

 <Window.Resources>
    <ObjectDataProvider x:Key="data"/>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Channel}">
        <WrapPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
            <TextBlock Text=" [" />
            <TextBlock  Text="{Binding Path=Id}" />
            <TextBlock Text="]" />
        </WrapPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <TreeView Margin="12,12,12,96" Name="treeView1" ItemsSource="{Binding Source={StaticResource data}, Path=Channels}">
    </TreeView>
</Grid>

The code behind to create the data instance

Objects config;
var serializer = new XmlSerializer(typeof(Objects));
using (var stream = new FileStream(@"C:\test.xml", FileMode.Open))
{
    config = (Objects)serializer.Deserialize(stream);
}

var dp = (ObjectDataProvider)FindResource("data");
dp.ObjectInstance = config;

I've looked at countless examples but I still can figure out what I am doing wrong. Thanks for the help.

Update:

<HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Objects}" ItemsSource="{Binding Path=Channels}"/>
<HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Channel}" ItemsSource="Binding Path=Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

<DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>

No change to the TreeView. With this change I still have only the Channel listed, and nothing else.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

葬シ愛 2024-08-11 09:51:51

扩展@Gimalay的答案,问题是 TreeView 不知道从哪里获取任何子节点的数据。您可以使用 HierarchialDataTemplate 而不是 DataTemplate 通知 TreeView

<HierarchialDataTemplate DataType="{x:Type ConfigurationEditor:Channel}"
    ItemsSource="...">
    <WrapPanel>
        <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
        <TextBlock Text=" [" />
        <TextBlock  Text="{Binding Path=Id}" />
        <TextBlock Text="]" />
    </WrapPanel>
</HierarchialDataTemplate>

两者之间的主要区别在于 ItemsSource > 属性。这是一个绑定表达式,它返回用作子节点的对象集合。

问题是你有几处房产可以生孩子,而不仅仅是一处。您要么需要将它们全部组合到一个属性中,要么添加另一个返回所有子节点的属性。

最后,您需要为每个子项类型定义一个 DataTemplate,以便 TreeView 知道如何显示它们(您可以使用 HierarchialDataTemplate code> 也适用于子节点,如果它们又具有子节点)。

Expanding on @Gimalay's answer, the problem is that the TreeView doesn't know where to get the data for any child nodes. You inform the TreeView by using a HierarchialDataTemplate, rather than a DataTemplate:

<HierarchialDataTemplate DataType="{x:Type ConfigurationEditor:Channel}"
    ItemsSource="...">
    <WrapPanel>
        <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
        <TextBlock Text=" [" />
        <TextBlock  Text="{Binding Path=Id}" />
        <TextBlock Text="]" />
    </WrapPanel>
</HierarchialDataTemplate>

The main difference between the two is the ItemsSource attribute. This is a binding expression that returns a collection of objects to use as child nodes.

The problem is that you have a few properties to get children from, not just one. You either need to combine them all into one property, or add another property that returns all of the child nodes.

Finally, you'll need to define a DataTemplate for each child item type, so that the TreeView knows how to display them (you can use a HierarchialDataTemplate for the children as well, if they in turn have child nodes).

泪眸﹌ 2024-08-11 09:51:51

我认为您不需要这个模板,因为对象永远不会出现在 TreeView 的图片中。

<HierarchicalDataTemplate
    DataType="{x:Type ConfigurationEditor:Objects}" 
    ItemsSource="{Binding Path=Channels}"/>

您已在 XAML 中设置了此设置,它显示了通道..完美!

<TreeView 
    Margin="12,12,12,96" Name="treeView1" 
    ItemsSource="{Binding Source={StaticResource data}, Path=Channels}">
</TreeView>

现在您还希望显示 Reader。 但是只能将 IEnumerable 类型的对象指定为 ItemsSource,因此下面的模板将“Reader”指定为 ItemsSource 是不正确的。

<HierarchicalDataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" 
    ItemsSource="{Binding Path=Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

因此,如果您还希望显示读者姓名,请使用如下所示的模板。

<DataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" >
    <StackPanel>
        <TextBlock Text="{Binding Path=Name}"/>
        <TextBlock Text="{Binding Path=Reader.Name}"/>
    <StackPanel>
</DataTemplate>

I don't think you need this template, as Objects never come in the picture in your TreeView.

<HierarchicalDataTemplate
    DataType="{x:Type ConfigurationEditor:Objects}" 
    ItemsSource="{Binding Path=Channels}"/>

You have set this in XAML, which shows the Channels.. perfect!

<TreeView 
    Margin="12,12,12,96" Name="treeView1" 
    ItemsSource="{Binding Source={StaticResource data}, Path=Channels}">
</TreeView>

Now you want the Reader to be shown as well. But one can only specify objects of type IEnumerable as an ItemsSource, so the template below is incorrect which specifies "Reader" as the ItemsSource.

<HierarchicalDataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" 
    ItemsSource="{Binding Path=Reader}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

So if you want reader name to be shown as well, use the template as shown below.

<DataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" >
    <StackPanel>
        <TextBlock Text="{Binding Path=Name}"/>
        <TextBlock Text="{Binding Path=Reader.Name}"/>
    <StackPanel>
</DataTemplate>
夏了南城 2024-08-11 09:51:51

正是为了这个目的,当项目源本身内部实际上具有层次结构时,才会使用层次结构数据模板。也就是说,对象本身具有一定的层次结构,父对象保留子对象的列表。

作为示例,每个 Channel 可能都有一个属性 SubChannels,如下所示。

public class Channel 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public ObservableCollection<Channel> SubChannels { get; }
}

那么我们就可以使用这样的模板。

<HierarchicalDataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" 
    ItemsSource="{Binding Path=SubChannels}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

现在,对象结构可以是多级深度的,每个子通道又具有子通道。

另外,请注意,在示例中,我只是使用相同的类型 Channel 来创建子通道的层次结构。我们可以使用另一种类型,假设每个频道都有一个读者列表。

Just for the sake of it, hierarchical data templates are used when the items source actually has a hierarchy within itself. That is, the objects themselves have some hierarchy, with parent objects keeping a list of child objects.

As an example, may be the each Channel has a property SubChannels like below.

public class Channel 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public ObservableCollection<Channel> SubChannels { get; }
}

Then we could have used a template like this.

<HierarchicalDataTemplate 
    DataType="{x:Type ConfigurationEditor:Channel}" 
    ItemsSource="{Binding Path=SubChannels}">
    <TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>

Now the object structure could be multilevel deep with each subchannel again having subchannels.

Also, do note that in the example I just used the same type, Channel, to create a hierarchy of subchannels. We could have used another type, say each Channel having a list of readers.

木槿暧夏七纪年 2024-08-11 09:51:51

好吧,经过多次尝试错误后,我能够让它按我想要的方式工作。我是这样做的。

在我的 Channel 对象中,我添加了一个新属性,它是我想要在 TreeView 中显示的所有其他属性的集合,

public ArrayList Components
{
    get { return new ArrayList { Reader, Filters, Router, Persister  }; } 
    set
    {
        ArrayList list = value;
        if (list != null)
        {
            foreach (var item in list)
            {
                if (item is Reader)
                {
                    Reader = (Reader)item;
                }
                else if (item is Router)
                {
                    Router = (Router) item;
                }
                else if (item is Persister)
                {
                    Persister = (Persister) item;
                }
                else if (item is Filters)
                {
                    Filters = (Filters) item;
                }
            }
        }
    }
}

然后我的 XAML 如下所示,用于显示树视图。

    <HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Channel}" ItemsSource="{Binding Path=Components}">
        <WrapPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
            <TextBlock Text=" [" />
            <TextBlock  Text="{Binding Path=Id}" />
            <TextBlock Text="]" />
        </WrapPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Filters}" ItemsSource="{Binding Path=.}">
        <TextBlock Text="Filters"/>
        <HierarchicalDataTemplate.ItemTemplate>
            <DataTemplate >
                <TextBlock Text="{Binding Path=Name}"/>
            </DataTemplate>
        </HierarchicalDataTemplate.ItemTemplate>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Router}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Persister}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>

感谢安迪让我走上正轨。

Okay, After much trial an error I was able to get this to work as I wanted. Here is how i did it.

In my Channel object I added a new property that was a collection of all the other properties which I wanted to display in the TreeView

public ArrayList Components
{
    get { return new ArrayList { Reader, Filters, Router, Persister  }; } 
    set
    {
        ArrayList list = value;
        if (list != null)
        {
            foreach (var item in list)
            {
                if (item is Reader)
                {
                    Reader = (Reader)item;
                }
                else if (item is Router)
                {
                    Router = (Router) item;
                }
                else if (item is Persister)
                {
                    Persister = (Persister) item;
                }
                else if (item is Filters)
                {
                    Filters = (Filters) item;
                }
            }
        }
    }
}

Then my XAML is as follows to display the treeview.

    <HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Channel}" ItemsSource="{Binding Path=Components}">
        <WrapPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
            <TextBlock Text=" [" />
            <TextBlock  Text="{Binding Path=Id}" />
            <TextBlock Text="]" />
        </WrapPanel>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type ConfigurationEditor:Filters}" ItemsSource="{Binding Path=.}">
        <TextBlock Text="Filters"/>
        <HierarchicalDataTemplate.ItemTemplate>
            <DataTemplate >
                <TextBlock Text="{Binding Path=Name}"/>
            </DataTemplate>
        </HierarchicalDataTemplate.ItemTemplate>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Reader}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Router}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ConfigurationEditor:Persister}">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>

Thanks Andy for putting me on the right track.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文