Свойството на зависимост в персонализирания контрол неочаквано споделя памет/стойности

Имам следната настройка:

  • Персонализирана WPF контрола (базов клас), произлизаща от Canvas
  • Реализация на този базов клас
  • ObservableCollection<T> свойство на зависимост от тази реализация

Имам тестово приложение, което показва три уникални екземпляра на моята персонализирана контрола (напр. <custom:MyControl x:Name="Test1" />, Test2, Test3 и т.н.). Когато стартирам и отстранявам грешки в приложението, съдържанието на ObservableCollection<T> е едно и също за трите екземпляра на контролата. Защо е това?


Диаграма:

[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>()));
}

Един от начините, по които променям данните от диаграмата, е (приемайки множество групи данни), например:

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

Но всеки екземпляр в DataGroups[] е идентичен.


Същото нещо се случва, ако дефинирам своята колекция(и) чрез XAML, така:

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

След това в код ще имам достъп до дефинираните колекции:

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

person qJake    schedule 07.10.2013    source източник
comment
Трябва да ни покажете някакъв код. Как изглежда декларацията за свойство на зависимост? Как го обвързвате?   -  person Gabe    schedule 07.10.2013
comment
Добавен е код, въпреки че е много частичен (опитах се да извлека съответните битове възможно най-добре, не мога просто да го поставя всичко тук, защото внедряването е огромно и голяма част от него е без значение).   -  person qJake    schedule 07.10.2013
comment
Откъде идва Points? Как се присвоява?   -  person Gabe    schedule 07.10.2013
comment
В долната част на въпроса се обяснява както къде е дефиниран, така и как се присвоява, въпреки че е също толкова вероятно, че може да бъде присвоен чрез XAML, както и чрез код (поради атрибута [ContentProperty]). И Point е просто System.Windows.Point.   -  person qJake    schedule 07.10.2013
comment
Така че, ако имате Test1.Points = new ObservableCollection<Point>(); Test2.Points = new ObservableCollection<Point>(); Test1.Points.Add(new Point());, тогава Test2.Points ще има същата точка като добавената към Test1?   -  person Gabe    schedule 07.10.2013
comment
Не, въпреки факта, че свойствата на зависимостта са статични, техните поддържащи свойства не са и обикновено не бихте задали свойството Points по този начин, защото в FrameworkPropertyMetadata указвате стойност по подразбиране, която аз така или иначе зададох на new ObservableCollection<Point>(). Освен това точките не са единствените неща, които се дублират, има и други (непоказани) свойства в ChartData, които също се дублират. По същество това е public ObservableCollection<ChartData> DataGroups, което действа статично, въпреки че не е така.   -  person qJake    schedule 07.10.2013
comment
Добре, ето го твоят отговор.   -  person Gabe    schedule 07.10.2013


Отговори (2)


Не си направил нищо лошо. Това е по дизайн. Ще работи по този начин. Вместо това просто задайте стойността си в конструктора и няма да имате сингълтон.

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

Инициализиране на колекцията извън стойността по подразбиране

Когато създавате свойство на зависимост, вие не указвате стойността по подразбиране на свойството като начална стойност на полето. Вместо това указвате стойността по подразбиране чрез метаданните на свойството на зависимост. Ако вашето свойство е референтен тип, стойността по подразбиране, посочена в метаданните за свойство на зависимост, не е стойност по подразбиране за екземпляр; вместо това е стойност по подразбиране, която се прилага за всички екземпляри от типа. Следователно трябва да внимавате да не използвате единствената статична колекция, дефинирана от метаданните на свойството на колекцията като работна стойност по подразбиране за новосъздадени екземпляри от вашия тип. Вместо това трябва да се уверите, че съзнателно сте задали стойността на колекцията на уникална колекция (екземпляр) като част от логиката на вашия конструктор на клас. В противен случай ще сте създали непреднамерен единичен клас.

person dev hedgehog    schedule 07.10.2013
comment
Перфектно, това го реши. Не бях наясно как работят стойностите по подразбиране на свойство на зависимост. Благодаря! - person qJake; 09.10.2013

PointsProperty е статична стойност, която инициализирате със стойност по подразбиране new ObservableCollection<Point>(). Този статичен инициализатор създава единичен ObservableCollection и го използва като стойност по подразбиране за Points за всеки обект от тип ChartData, който създавате. Това не е фабрика, която създава нови ObservableCollections за всеки екземпляр, който се нуждае от стойност по подразбиране; той просто използва едно и също ObservableCollection за всеки.

Предполагам, че никога не присвоявате изрично стойност на Points, като по този начин винаги разчитате на стойността по подразбиране, която се споделя във всички екземпляри. Ето защо всеки екземпляр има една и съща колекция от точки.

person Gabe    schedule 07.10.2013
comment
И двамата бяхте прави, но аз тръгнах с аутсайдера с 200 точки вместо с 44 000 точки. Дано разбереш. :) Благодаря за вашата помощ! - person qJake; 09.10.2013