将TreeView绑定到层次结构DataTemplate和DataTemplate,其中DataType是接口

本文关键字:DataTemplate 其中 DataType 接口 绑定 层次结构 TreeView | 更新日期: 2023-09-27 18:26:46

我有一个复杂的问题,我已经解决了一周,现在正努力正确地解决。我使用的是WPF.NET4.5、MVVM模式和Prism。我想显示一个绑定到ObservableCollection<IScript> LoadedScripts:的TreeView

namespace Library.Data.Scripting    
{
    public interface IScript : INotifyPropertyChanged
    {
        ...
        IScriptDescription ScriptDescription { get; }
        ...
    }

    public interface IScriptDescription : INotifyPropertyChanged
    {
        ...
        string Name { get; }
        IEnumerable<ISectionDescription> Sections { get; }
        ScriptStatus Status { get; }
        ...
    }
    public interface ISectionDescription : INotifyPropertyChanged
    {
        ...
        string Name { get; }
        ScriptStatus Status { get; }
        ...
    }
}

我希望TreeView显示脚本列表,并且每个脚本都有脚本部分的子脚本。对于每个可见的节点,我希望显示名称和状态。也就是说,对于LoadedScripts中的每个IScript,显示ScriptDescription.NameScriptDescription.Status的文本框。对于每个子ISectionDescription,一个包含ISectionDescription.NameISectionDescription.Status的TextBox。

这一切都相当简单,除了我绑定到接口这一事实。我发现了如何使用DataTemplateSelector来克服这一问题,但我的树视图没有在模板中正确显示文本。相反,我正在为我的ObservableCollection<IScript> LoadedScripts中没有孩子的IScript获得object.ToString()。我在Output窗口中也没有收到任何错误或警告。

我觉得我真的很接近,但我无法让它正确显示我的数据。我已经验证了我确实正在获取我的数据,如果我为TreeView.ItemTemplate放置一个基本的DataTemplate,我就能够显示顶级IScript节点。我不确定我做错了什么,任何意见都非常感谢!

这是我的DataTemplateSelector:

namespace Library.App.GUI
{
    public class ScriptDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;
            if (element != null && item != null && item is IScript)
            {
                return element.FindResource("ScriptTemplate") as HierarchicalDataTemplate;
            }
            else if (element != null && item != null && item is ISectionDescription)
            {
                return element.FindResource("SectionTemplate") as DataTemplate;
            }
            return null;
        }
    }
}

ViewModel:

namespace Library.App.GUI
{
    public class TestStatusViewModel
    {
        private IScriptManager ScriptManager;
        public ObservableCollection<IScript> LoadedScripts
        {
            get 
            { 
                return ScriptManager.LoadedScripts; 
            }
        }
        public TestStatusViewModel()
        {
            //Locate the ScriptManager in the MEF container using Microsoft.Practices.ServiceLocation.ServiceLocator
            ScriptManager = ServiceLocator.Current.GetInstance<IScriptManager>();
        }
    }
}

这是我的XAML:

<UserControl x:Class="Library.App.GUI.TestStatusView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
            xmlns:local="clr-namespace:Library.App.GUI"
            xmlns:script="clr-namespace:Library.Data.Scripting;assembly=MTFCommon"
            xmlns:prism="clr-namespace:Microsoft.Practices.Prism;assembly=Microsoft.Practices.Prism"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            mc:Ignorable="d" 
            d:DesignHeight="600" d:DesignWidth="200">

    <UserControl.Resources>
        <local:ScriptDataTemplateSelector x:Key="ScriptTemplateSelector"/>
        <HierarchicalDataTemplate  x:Key="ScriptTemplate" ItemsSource="{Binding ScriptDescription.Sections}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" TextAlignment="Left" Text="{Binding ScriptDescription.Name}" />
                <TextBlock Grid.Column="1" TextAlignment="Right" FontWeight="Bold" HorizontalAlignment="Stretch" Text="{Binding ScriptDescription.Status}" />
            </Grid>
        </HierarchicalDataTemplate>
        <DataTemplate x:Key="SectionTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" TextAlignment="Left" Text="{Binding Name}" />
                <TextBlock Grid.Column="1" TextAlignment="Right" FontWeight="Bold" HorizontalAlignment="Stretch" Text="{Binding Status}" />
            </Grid>
        </DataTemplate>
    </UserControl.Resources>
    <ItemsControl HorizontalAlignment="Stretch">
            <TreeView Name="treeView" ItemTemplateSelector="{StaticResource ScriptTemplateSelector}" ItemsSource="{Binding LoadedScripts}" />
    </ItemsControl>
</UserControl>

附言:库中的所有内容。数据命名空间我无法亲自编辑,因为它是一个外部库。如果必须这样做,我可以提出更改请求,但我宁愿不这样做。

附言:这是我的第一个SO问题,如果我遗漏了任何重要信息,请告诉我。谢谢

将TreeView绑定到层次结构DataTemplate和DataTemplate,其中DataType是接口

如果DataTemplateSelector不工作,您可能需要检查element.FindResource是否返回了任何内容。如果它返回null(或者as返回null),则ToString将用于显示树中的项目。

相关文章: