Search code examples
mvvmsilverlight-4.0mvvm-lightsilverlight-toolkit

Treeview SelectedItem is sometimes the VM and sometimes TreeViewItem


I have a TreeView that the user navigates to select an item for display in a grid. Briefly the XAML looks like this:

    <local:TreeViewEx x:Name="theTreeView" ItemsSource="{Binding theData}">

                        <local:TreeViewEx.ItemTemplate>
                            <sdk:HierarchicalDataTemplate ItemsSource="{Binding theChildData}">
                                <TextBlock Text="{Binding Name}"/>
                            </sdk:HierarchicalDataTemplate>
                        </local:TreeViewEx.ItemTemplate>
                    </local:TreeViewEx>

 <Grid DataContext="{Binding ElementName=theTreeView, Path=SelectedItem}">
                            <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding}" />
                    <TextBlock Text="{Binding Name}" /></StackPanel>
        </Grid>

As the user clicks through the treeview the viewmodel type name is displayed along with the value of the Name property. Perfect. Howerver the user can also execute a search of the treeview (following to Josh Smith) which sets the IsSelected property of the TreeViewItem. Once that happens the {Binding} displays TreeViewItemEx rather than the ViewModel type name, and of course the Name property is not displayed.

How is that possible that the selectedItem would sometimes by the ViewModel, and sometimes be the TreeViewItem?


Solution

  • If you replace your grid with a ContentControl you can then use a DataTemplateSelector.

    <ContentControl Content="{Binding ElementName=theTreeView, Path=SelectedItem}" 
                    ContentTemplateSelector="{StaticResource TreeViewItemSelector}" />
    

    On the DataTemplateSelector you can then reference two templates for the different types

        <DataTemplate x:Key="ModelTemplate">
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding}" />
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    
        <TreeViewItemSelector x:Key="TreeViewItemSelector"
                              ModelTemplate="{StaticResource ModelTemplate}"
                              TreeItemTemplate="{StaticResource TreeItemTemplate}" />
    

    In the selector you will then want logic like this

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            if (item is ModelType)
                return ModelTemplate;
            if (item is TreeViewItem)
                return TreeItemTemplate;
            throw new NotImplementedException();
        }