Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

WPF Custom Control with ItemsControl owning itself causes stackoverflow

Code example is reduced to the minimum amount reproducing the issue.
Given a custom WPF user control with a list of children defined as such:

[ContentProperty(nameof(FormItems))]
public class FormContainer : ContentControl
{
    static FormContainer()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(FormContainer),
            new FrameworkPropertyMetadata(typeof(FormContainer))
        );
    }

    #region FormItems Dependency Property

    public static readonly DependencyProperty FormItemsProperty = DependencyProperty.Register(
        "FormItems", typeof(ObservableCollection<UIElement>), typeof(FormContainer), 
        new PropertyMetadata(new ObservableCollection<UIElement>()));

    public ObservableCollection<UIElement> FormItems
    {
        get => (ObservableCollection<UIElement>)GetValue(FormItemsProperty);
        set => SetValue(FormItemsProperty, value);
    }

    #endregion

    // ... extra boilerplate removed
}

Which is being rendered in a simple Stack Panel with an ItemsControl help using the following style:

<ControlTemplate x:Key="FormContainerControlTemplate" TargetType="{x:Type uc:FormContainer}">
    <ItemsControl ItemsSource="{TemplateBinding FormItems}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ControlTemplate>

<Style TargetType="{x:Type uc:FormContainer}">
    <Setter Property="Template" Value="{StaticResource FormContainerControlTemplate}"/>
</Style>

This usage of the control works perfectly:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<controls:FormContainer>
    <Label Content="Something" />
</controls:FormContainer>

This usage crashed with a stackoverflow:

<controls:FormContainer>
    <Label Content="Something" />
    <controls:FormContainer>
        <Label Content="Something else" />
    </controls:FormContainer>
</controls:FormContainer>

I would expected to just get two containers nested with their own controls.

Why does it crashed? What am I missing? And more importantly how can I enable nesting functionality for my custom controls?

>Solution :

DP metadata declaration is incorrect. DP object (FormItemsProperty) is a singletone, so its PropertyMetadata and default value is shared by all intances of FormContainer. Which in case of reference type DP can cause issues.

fix them by assigning a different collection for each instance:

public class FormContainer : ContentControl
{
    static FormContainer()
    {
        DefaultStyleKeyProperty.OverrideMetadata
        (
            typeof(FormContainer),
            new FrameworkPropertyMetadata(typeof(FormContainer))
        );
    }

    public FormContainer()
    {
        SetCurrentValue(FormItemsProperty, new ObservableCollection<UIElement>());
    }

    #region FormItems Dependency Property

    public static readonly DependencyProperty FormItemsProperty = DependencyProperty.Register
    (
        nameof(FormItems),
        typeof(ObservableCollection<UIElement>),
        typeof(FormContainer), 
        new PropertyMetadata(null)
    );

    public ObservableCollection<UIElement> FormItems
    {
        get => (ObservableCollection<UIElement>)GetValue(FormItemsProperty);
        set => SetValue(FormItemsProperty, value);
    }

    #endregion
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading