Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency property in custom control is sharing memory/values unexpectedly

I have the following set up:

  • Custom WPF Control (Base class), deriving from Canvas
  • Implementation of that base class
  • An ObservableCollection<T> dependency property on that implementation

I have a test app that displays three unique instances of my custom control (e.g. <custom:MyControl x:Name="Test1" />, Test2, Test3, and so on). When I run and debug the app, the contents of the ObservableCollection<T> are the same for all three instances of the control. Why is this?


Chart:

[ContentProperty("DataGroups")]
public abstract class Chart : Canvas
{
    static Chart()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Chart), new FrameworkPropertyMetadata(typeof(Chart)));
    }

    public ObservableCollection<ChartData> DataGroups
    {
        get { return (ObservableCollection<ChartData>)GetValue(DataGroupsProperty); }
        set { SetValue(DataGroupsProperty, value); }
    }
    public static readonly DependencyProperty DataGroupsProperty =
        DependencyProperty.Register("DataGroups", typeof(ObservableCollection<ChartData>), typeof(Chart), new FrameworkPropertyMetadata(new ObservableCollection<ChartData>(), FrameworkPropertyMetadataOptions.AffectsArrange));

    public abstract void Refresh();
}

ChartData:

[ContentProperty("Points")]
public class ChartData : FrameworkElement
{
    public ObservableCollection<Point> Points
    {
        get { return (ObservableCollection<Point>)GetValue(PointsProperty); }
        set { SetValue(PointsProperty, value); }
    }
    public static readonly DependencyProperty PointsProperty =
        DependencyProperty.Register("Points", typeof(ObservableCollection<Point>), typeof(ChartData), new PropertyMetadata(new ObservableCollection<Point>()));
}

One way I modify the chart data is (assuming multiple data groups), for example:

MyChart.DataGroups[index].Points.Add(new Point() { Y = someNumber });
MyChart.Refresh();

But every instance inside DataGroups[] is identical.


The same thing is happening if I define my collection(s) via XAML, like so:

<c:Chart x:Name="ChartA">
    <c:ChartData x:Name="DataGroup1" />
    <c:ChartData x:Name="DataGroup2" />
</c:Chart>

Then, in code, I would access the defined collections:

ChartA.DataGroups[0].Points.Add(new Point() { Y = someNumber });
ChartA.Refresh();
like image 459
qJake Avatar asked Dec 19 '22 23:12

qJake


2 Answers

You havent done anything wrong. It is by design. It shall work that way. Just set your value in constructor instead and you will not have a singleton.

http://msdn.microsoft.com/en-us/library/aa970563.aspx

Initializing the Collection Beyond the Default Value

When you create a dependency property, you do not specify the property default value as the initial field value. Instead, you specify the default value through the dependency property metadata. 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. Therefore you must be careful to not use the singular static collection defined by the collection property metadata as the working default value for newly created instances of your type. Instead, you must make sure that you deliberately set the collection value to a unique (instance) collection as part of your class constructor logic. Otherwise you will have created an unintentional singleton class.

like image 130
dev hedgehog Avatar answered Dec 28 '22 23:12

dev hedgehog


PointsProperty is a static value that you initialize with a default value of new ObservableCollection<Point>(). This static initializer creates a single ObservableCollection and uses that as the default value for Points on any object of type ChartData that you create. It is not a factory that creates new ObservableCollections for every instance that needs a default value; it simply uses the same ObservableCollection for each.

I'm guessing that you never explicitly assign a value to Points, thus always relying on the default value, which is shared across all instances. That's why each instance has the same collection of points.

like image 21
Gabe Avatar answered Dec 28 '22 23:12

Gabe