Reputation: 549
Adding a ContentControl
with a binding into my UserControl
causes an exception:
InvalidOperationException: Layout recursion reached allowed limit to avoid stack overflow: '2047'. Either the tree contains a loop or is too deep.
I try to switch between two views with use of content control:
<ContentControl Grid.Row="2" Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding IsListView}" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource ListTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsTabView}" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource TabTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Once the Content={Binding}
is removed, the exception is gone. How can I trace the source of the exception? Investigation of the exception members didn't give any clue on what can cause this recursion.
UPD: Leaving the ContentControl
empty (e.g. without ListTemplate
and TabTemplate
) doesn't solve the problem.
Upvotes: 0
Views: 458
Reputation: 6754
ContentControl
will attempt to show whatever the value of it's Content
property is using a ContentPresenter
. The MSDN doc I've linked describes the full logic that the ContentPresenter
uses in determining how to best display the content it's been given, but these are the two most relevant points:
First it will try this:
- If the
ContentTemplate
property on theContentPresenter
is set, theContentPresenter
applies thatDataTemplate
to theContent
property and the resultingUIElement
and its child elements, if any, are displayed.
Later on, if the above has failed (meaning ContentTemplate
was not set), it will try this:
- If
Content
is aUIElement
object, theUIElement
is displayed. If theUIElement
already has a parent, an exception occurs.
When you first load the ContentControl
, Content="{Binding}"
sets Content
to DataContext
, which I'm assuming is your UserControl
. Your Style
has triggers that set ContentTemplate
conditionally. This means there will be some moments when ContentTemplate
is not set, so ContentPresenter
will default to trying to display Content
, which is a UIElement
, as one of it's children.
So the ContentPresenter
will try to display the entire UserControl
as it's child, no problem- except part of that new "child" is itself, so to render than inner version of itself, it needs to display the Content
property. And it continues like this in an infinite, recursive loop of ContentControl
s trying to display themselves within themselves until a limit is reached and WPF bails and throws an exception.
Always have a ContentTemplate
.
You'll need to have some sort of default ContentTemplate
for the ContentControl
to show if none of the triggers activate.
Use ContentPresenter
directly instead of ContentControl
I'm not actually sure why this the case, but even after providing a default ContentTemplate
, it still wouldn't work. It probably has something to do with the internal workings of ContentControl
. However, switching out the ContentControl
for a ContentPresenter
fixes everything.
<ContentPresenter Grid.Row="2" Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="ContentPresenter">
<Setter Property="ContentTemplate">
<Setter.Value>
<!--"Default" template that will be used if none of the triggers are active-->
<DataTemplate>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsListView}" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource ListTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsTabView}" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource TabTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
Upvotes: 4