2

我在创建带有对象数据绑定的 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列出的,没有别的。

4

4 回答 4

2

扩展@Gimalay 的答案,问题是TreeView不知道从哪里获取任何子节点的数据。TreeView您通过使用 a通知HierarchialDataTemplate,而不是 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>

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

问题是您有几个属性可以从中获取孩子,而不仅仅是一个。您要么需要将它们全部组合成一个属性,要么添加另一个返回所有子节点的属性。

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

于 2009-09-13T01:57:10.810 回答
2

我认为您不需要此模板,因为对象永远不会出现在 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>

现在您还希望显示阅读器。但是只能将 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>
于 2009-09-13T07:53:03.337 回答
0

仅仅为了它,当项目源本身实际上具有层次结构时,将使用层次结构数据模板。也就是说,对象本身有一些层次结构,父对象保留一个子对象列表。

例如,可能每个 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,来创建子通道的层次结构。我们可以使用另一种类型,比如每个频道都有一个读者列表。

于 2009-09-13T08:16:51.860 回答
0

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

在我的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>

感谢安迪让我走上正轨。

于 2009-09-14T22:29:47.957 回答