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 IScript
s 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!