1

I've got a complex question I've been working on for a good week now trying to work correctly. I am using WPF .NET 4.5, the MVVM pattern, and Prism. I want to display a TreeView that binds to an ObservableCollection<IScript> LoadedScripts:

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

        ...
    }
}

I want the TreeView to display the list of scripts and each script to have children for the script's sections. For each node that is visible, I want to show the Name and Status. That is, for each IScript in LoadedScripts show a textbox of ScriptDescription.Name and ScriptDescription.Status. For each child ISectionDescription, a TextBox containing ISectionDescription.Name and ISectionDescription.Status.

This is all fairly straightforward, except for the fact that I'm binding to Interfaces. I found how I'm supposed to be able to overcome this, using a DataTemplateSelector, but my treeview isn't displaying the text correctly in my templates. Instead, I am getting the object.ToString() for the IScripts in my ObservableCollection<IScript> LoadedScripts with no children. I'm not getting any kind of errors or warnings in the Output window either.

I feel like I am really close but I can't get it to correctly display my data. I have verified I am indeed getting my data and if I put a basic DataTemplate for TreeView.ItemTemplate I am able to display the top-level IScript nodes. I'm not sure what I'm doing wrong, any input is much appreciated!

Here's my 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;
        }

    }
}

The 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>();
        }
    }
}

Here's my 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>

P.S.: Everything in the Library.Data namespace I am not able to personally edit since it is an external library. I can put in a request to have it changed if that must happen, but prefer not.

P.P.S.: This is my first SO question, please let me know if I missed any important information. Thanks!

4

1 回答 1

2

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

于 2013-08-06T17:00:39.943 回答