Search code examples
wpfdatatemplatecontenttemplateselector

WPF ContentTemplateSelector weirdness


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.

Solution

  • 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.