Свойства зависимостей типа коллекция
Этот раздел содержит рекомендации и примеры шаблонов для реализации свойства зависимостей, где типом свойства является коллекция.
Реализация свойства зависимостей типа "коллекция"
Для свойства зависимостей в общем шаблон реализации, которому необходимо следовать, является определение CLR обертки свойства, где это свойство является DependencyProperty идентификатор, а не полем или другой конструкцией. При реализации свойства типа "коллекция" вы следуете этому же шаблону. Тем не менее, свойство типа "коллекция" некоторым образом усложняет шаблон всякий раз, когда тип, содержащийся в коллекции является DependencyObject или Freezable производного класса.
Инициализация коллекции за пределами значения по умолчанию
При создании свойства зависимостей вы не указываете значение свойства по умолчанию в качестве начального значения поля. Вместо этого значение по умолчанию указывается через метаданные свойства зависимостей. Если свойство является ссылочным типом, значение по умолчанию, заданное в метаданных свойства зависимостей, не является значением по умолчанию для каждого экземпляра. Напротив, это значение по умолчанию, которое применяется ко всем экземплярам типа. Поэтому необходимо избегать использования единственной статической коллекции, определенной метаданными свойства коллекции, в качестве рабочего значения по умолчанию для вновь создаваемых экземпляров типа. Вместо этого необходимо явно задать в качестве значения коллекции уникальную коллекцию (экземпляр) как часть логики конструктора класса. В противном случае будет создан случайный одноэлементный класс.
Рассмотрим следующий пример. В следующем разделе примера показано определение класса Aquarium
. Этот класс определяет свойство зависимостей типа коллекции AquariumObjects
, который использует универсальный List<T> тип с FrameworkElement ограничения типа. В Register(String, Type, Type, PropertyMetadata) вызов для свойства зависимостей метаданные устанавливают значение по умолчанию к новым базовым List<T>.
public class Fish : FrameworkElement { }
public class Aquarium : DependencyObject {
private static readonly DependencyPropertyKey AquariumContentsPropertyKey =
DependencyProperty.RegisterReadOnly(
"AquariumContents",
typeof(List<FrameworkElement>),
typeof(Aquarium),
new FrameworkPropertyMetadata(new List<FrameworkElement>())
);
public static readonly DependencyProperty AquariumContentsProperty =
AquariumContentsPropertyKey.DependencyProperty;
public List<FrameworkElement> AquariumContents
{
get { return (List<FrameworkElement>)GetValue(AquariumContentsProperty); }
}
// ...
}
Однако если оставить такой код, как в примере, это значение по умолчанию одного списка будет использоваться совместно для всех экземпляров Aquarium
. Если выполнить следующий тестовый код, предназначенный для демонстрации того, как создаются два отдельных экземпляра Aquarium
и добавляется один отличный Fish
для каждого из них, вы увидите странный результат.
Aquarium myAq1 = new Aquarium();
Aquarium myAq2 = new Aquarium();
Fish f1 = new Fish();
Fish f2 = new Fish();
myAq1.AquariumContents.Add(f1);
myAq2.AquariumContents.Add(f2);
MessageBox.Show("aq1 contains " + myAq1.AquariumContents.Count.ToString() + " things");
MessageBox.Show("aq2 contains " + myAq2.AquariumContents.Count.ToString() + " things");
Вместо того чтобы каждой коллекции назначалось число 1, каждой коллекции назначается число 2! Это происходит, поскольку каждый Aquarium
добавил свой объект Fish
в коллекцию значений по умолчанию, которая является результатом вызова одного конструктора в метаданных и, таким образом, совместно используется всеми экземплярами. Как правило, такая ситуация нежелательна.
Чтобы устранить эту проблему, необходимо сбросить значение свойства зависимостей коллекции, задав уникальный экземпляр в составе вызова конструктора класса. Так как свойство является свойством зависимостей только для чтения, используйте SetValue(DependencyPropertyKey, Object) метод задается с помощью DependencyPropertyKey , доступна только в классе.
public Aquarium() : base()
{
SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>());
}
Теперь, запустив тот же тестовый код еще раз, вы можете заметить, что результаты стали более ожидаемыми, то есть каждый Aquarium
поддерживает свою собственную уникальную коллекцию.
Если свойство коллекции будет доступным для чтения и записи, в этот шаблон потребуется внести небольшое изменение. В этом случае можно вызвать открытый метод доступа set из конструктора для выполнения инициализации, он по-прежнему будет вызывать сигнатура SetValue(DependencyProperty, Object) внутри обертки, с помощью общедоступного DependencyProperty идентификатор.
Передача сведений об изменении значений привязки из свойств коллекции
Свойство коллекции, которое само является свойством зависимостей, не сообщает об изменениях своих подсвойств автоматически. При создании привязок в коллекции это может мешать привязке сообщать об изменениях и, следовательно, нарушить некоторые сценарии привязки данных. Тем не менее если вы используете тип FreezableCollection<T> как типа коллекции, затем изменении подсвойств в содержащихся элементов в коллекции будут передаваться правильно, и привязка будет работать должным образом.
Чтобы включить привязку подсвойств в коллекции объектов зависимостей, создайте свойство коллекции типа FreezableCollection<T>, с ограничением типа для этой коллекции к любому DependencyObject производного класса.