WPF ContentTemplateSelector weirdness

2.6k views Asked by At

Goal: I wish to have an object that contains my data and have it bound to a ContentPresenter that uses a custom ContentTemplateSelector to select the proper data template for rendering in the main control.

Problem: DataTemplate does not render as expected in the main control; It does not render the specified View control.

Code:

MainView.xaml:

<Window x:Class="WpfContentTemplateSelector.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:this="clr-namespace:WpfContentTemplateSelector"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <this:ObjectWrapper x:Key="wrapper"/>
        </Grid.Resources>

        <ContentPresenter Content="{Binding Value}" DataContext="{StaticResource ResourceKey=wrapper}">
            <ContentPresenter.ContentTemplateSelector>
                <this:TemplateSelector/>
            </ContentPresenter.ContentTemplateSelector>
        </ContentPresenter>
    </Grid>
</Window>

DataObject.cs:

class DataObject
{
    public string Title { get; set; }

    public DataObject()
    {
        Title = "Title";
    }
}

ObjectWrapper.cs:

class ObjectWrapper
{
    public DataObject Value { get; set; }

    public ObjectWrapper()
    {
        Value = new DataObject();
    }
}

TemplateSelector.cs:

class TemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        DataTemplate template = new DataTemplate(typeof(View));

        return template;
    }
}

View.xaml:

<UserControl x:Class="WpfContentTemplateSelector.View"
             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:this="clr-namespace:WpfContentTemplateSelector"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" Background="Navy">
    <Grid Background="Navy">
        <Button Height="30" Content="{Binding Title}"/>
    </Grid>
</UserControl>

Summary:

  • The ObjectWrapper initializes.
  • The DataObject initializes.
  • On initialization, the TemplateSelector creates a new DataTemplate.
  • The created DataTemplate uses the View type in its constructor.
  • The objects are all initialized, the datacontext of the main view is set to the DataObject, and the view still doesn't render to the main window.
2

There are 2 answers

2
Adi Lester On BEST ANSWER

Your problem is that in your TemplateSelector you're defining a DataTemplate for type View but you give it no content. Your code is the equivalent of the following:

<ContentPresenter Content="{Binding Value}" DataContext="{StaticResource ResourceKey=wrapper}">
    <ContentPresenter.ContentTemplate>
        <DataTemplate DataType="this:View">
        </DataTemplate>
    </ContentPresenter.ContentTemplate>
</ContentPresenter>

Here you are merely defining an empty DataTemplate for type View, when what you actually want is a DataTemplate containing a View control:

<ContentPresenter Content="{Binding Value}" DataContext="{StaticResource ResourceKey=wrapper}">
    <ContentPresenter.ContentTemplate>
        <DataTemplate>
            <this:View />
        </DataTemplate>
    </ContentPresenter.ContentTemplate>
</ContentPresenter>

So what you need to do is modify your TemplateSelector to return a proper DataTemplate object.

0
Tyler Kendrick On

Adi Lester's response revealed that the child nodes for the DataTemplate object I was creating didn't render the view used in the constructor.

To add to a DataTemplate's visual tree in the code-behind, I augmented my TemplateSelector object to use the following code:

class TemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        View view = item as View;
        DataTemplate template = new DataTemplate(typeof(View));

        FrameworkElementFactory factory = new FrameworkElementFactory(typeof(View));
        template.VisualTree = factory;

        return template;
    }
}

This successfully changes the visual tree and renders the view using my custom TemplateSelector to provide a DataTemplate.