Daniel Triangle
Daniel Triangle

Reputation: 13

Nested objects with collections in xaml seem to leak into adjacent collections

When I define a collection of objects that each contain a collection like this:

<Graph2:NestedObjectTest x:Key="nestedTest">
    <Graph2:NestedObjectTest.Children>
        <Graph2:ChildNestedObjectTest>
            <Graph2:ChildNestedObjectTest.Children>
                <Point X="0" Y="0"/>
                <Point X="10" Y="10"/>
                <Point X="20" Y="20"/>
            </Graph2:ChildNestedObjectTest.Children>
        </Graph2:ChildNestedObjectTest>
        <Graph2:ChildNestedObjectTest>
            <Graph2:ChildNestedObjectTest.Children>
                <Point X="1" Y="0"/>
                <Point X="11" Y="10"/>
                <Point X="21" Y="20"/>
            </Graph2:ChildNestedObjectTest.Children>
        </Graph2:ChildNestedObjectTest>
        <Graph2:ChildNestedObjectTest>
            <Graph2:ChildNestedObjectTest.Children>
                <Point X="2" Y="0"/>
                <Point X="12" Y="10"/>
                <Point X="22" Y="20"/>
            </Graph2:ChildNestedObjectTest.Children>
        </Graph2:ChildNestedObjectTest>
    </Graph2:NestedObjectTest.Children>
</Graph2:NestedObjectTest>

Each of the ChildNestedObjectTest objects gets all nine of the Point objects, instead of each getting the three objects that it appears they should have. Why is this?

The code behind is:

namespace Graph2
{
    public class NestedObjectTest : DependencyObject
    {
        public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof(List<ChildNestedObjectTest>), typeof(NestedObjectTest), new FrameworkPropertyMetadata(new List<ChildNestedObjectTest>()));
        public List<ChildNestedObjectTest> Children
        {
            get { return (List<ChildNestedObjectTest>)GetValue(ChildrenProperty); }
            set { SetValue(ChildrenProperty, value); }
        }
    }
    public class ChildNestedObjectTest : DependencyObject
    {
        public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof(List<Point>), typeof(ChildNestedObjectTest), new FrameworkPropertyMetadata(new List<Point>()));
        public List<Point> Children
        {
            get { return (List<Point>)GetValue(ChildrenProperty); }
            set { SetValue(ChildrenProperty, value); }
        }
    }
}

And I tested by adding the xaml to a window as a resource, then using:

Graph2.NestedObjectTest test = (Graph2.NestedObjectTest)this.Resources["nestedTest"];

Upvotes: 1

Views: 44

Answers (1)

Michal Ciechan
Michal Ciechan

Reputation: 13898

Collection-Type Dependency Properties

Summary

If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type.

Looks like the default static value you set in the metadata

 new FrameworkPropertyMetadata(new List<Point>(),
                                          ^
                                          ---- This

Is resused whenever, the closed generic is another DependencyObject / Freezable

Fix

To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call. Because the property is a read-only dependency property, you use the SetValue(DependencyPropertyKey, Object) method to set it, using the DependencyPropertyKey that is only accessible within the class.

public NestedObjectTest() : base()
{
    SetValue(ChildrenProperty, new List<ChildNestedObjectTest>()); 
}

and

public ChildNestedObjectTest() : base()
{
    SetValue(ChildrenProperty, new List<Point>()); 
}

Upvotes: 2

Related Questions