Robert Važan

DelayedContentControl in JungleControls

DelayedContentControl loads its Content lazily when required for rendering. It's more efficient and less noisy than standard ContentControl.

DelayedContentControl from opensource JungleControls library solves the problem of annoying data binding errors one gets when both Content and ContentTemplate are changed at the same time in ContentControl.

Let's start with a simple example demonstrating the issue:

<ContentControl Content="{Binding Content}">
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSecondModel}"
                             Value="False">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding FirstContent}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsSecondModel}"
                             Value="True">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding SecondContent}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

This bit of XAML switches between two ContentTemplate values while view model behind it switches between two Content values:

class SampleViewModel
{
    readonly Independent<bool> IsSecondModelValue
        = new Independent<bool>();

    public bool IsSecondModel
    {
        get { return IsSecondModelValue.Value; }
        set { IsSecondModelValue.Value = value; }
    }
    public object Content
    {
        get
        {
            return !IsSecondModelValue.Value
                ? (object)new FirstViewModel()
                : new SecondViewModel();
        }
    }
}

class FirstViewModel
{
    public string FirstContent
    {
        get { return "First content"; }
    }
}

class SecondViewModel
{
    public string SecondContent
    {
        get { return "Second content"; }
    }
}

Content and ContentTemplate are switched almost at the same time, but there is a split second when the properties are out of sync, which results in data binding errors:

System.Windows.Data Error: 40 : BindingExpression path error: 'FirstContent' property not found on 'object' ''SecondViewModel' (HashCode=38513176)'. BindingExpression:Path=FirstContent; DataItem='SecondViewModel' (HashCode=38513176); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

The problem in this example is that Content gets switched to SecondViewModel while the old ContentTemplate still references property FirstContent from FirstViewModel. An error gets logged and a split second later, ContentTemplate is switched and it now successfully binds to SecondContent on SecondViewModel. Thus the user doesn't observe anything unusual.

While binding errors aren't fatal for the application, spurious binding errors make it hard to see the real binding errors that cause your app to not display content where it should. They also slow down the application a bit and they are thus a no-no in performance-sensitive applications.

DelayedContentControl was developed to address this issue. It's a drop-in replacement for standard ContentControl:

<jc:DelayedContentControl Content="{Binding Content}">
    <jc:DelayedContentControl.Style>
        <Style TargetType="{x:Type jc:DelayedContentControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSecondModel}"
                             Value="False">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding FirstContent}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsSecondModel}"
                             Value="True">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding SecondContent}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </jc:DelayedContentControl.Style>
</jc:DelayedContentControl>

Whenever Content, ContentTemplate, or other Content* property changes, DelayedContentControl posts a message into Dispatcher event loop with priority DataBind (unless altered through DispatcherPriority property) and collects all Content* changes until the posted message executes. After all Content* changes are buffered, DelayedContentControl applies them carefully in just the right order to avoid data binding errors.

You can get DelayedContentControl from NuGet as part of JungleControls library.