Свойства зависимостей типа коллекции (WPF .NET)
В этой статье приведены рекомендации и предлагаемые шаблоны для реализации свойства зависимостей, которое является типом коллекции.
Важно!
Документация по рабочему столу для .NET 7 и .NET 6 находится в стадии разработки.
Необходимые компоненты
Для понимания статьи нужно иметь базовые знания о свойствах зависимостей и прочитать Общие сведения о свойствах зависимостей. Чтобы понимать примеры в этой статье полезно познакомиться с языком XAML и узнать, как создавать приложения WPF.
Реализация свойства зависимости типа коллекции
Как правило, шаблон реализации для свойства зависимостей — это оболочка свойства CLR, поддерживаемая DependencyProperty идентификатором вместо поля или другой конструкции. При реализации свойства зависимостей типа коллекции можно следовать тому же шаблону. Шаблон более сложный, если тип элемента коллекции является DependencyObject производным или производным классом Freezable .
Инициализация коллекции
При создании свойства зависимостей обычно указывается значение по умолчанию с помощью метаданных свойства зависимостей вместо указания начального значения свойства. Однако если значение свойства является ссылочным типом, значение по умолчанию должно быть задано в конструкторе класса, регистрирующего свойство зависимостей. Метаданные свойства зависимостей не должны включать значение ссылочного типа по умолчанию, так как это значение будет назначено всем экземплярам класса, создавая одинарный класс.
В следующем примере объявляется Aquarium
класс, содержащий коллекцию FrameworkElement элементов в универсальном List<T>формате. Значение коллекции по умолчанию не включается в PropertyMetadata переданный RegisterReadOnly(String, Type, Type, PropertyMetadata) метод, а конструктор классов используется для задания значения коллекции по умолчанию новым универсальным List
.
public class Aquarium : DependencyObject
{
// Register a dependency property with the specified property name,
// property type, owner type, and property metadata.
private static readonly DependencyPropertyKey s_aquariumContentsPropertyKey =
DependencyProperty.RegisterReadOnly(
name: "AquariumContents",
propertyType: typeof(List<FrameworkElement>),
ownerType: typeof(Aquarium),
typeMetadata: new FrameworkPropertyMetadata()
//typeMetadata: new FrameworkPropertyMetadata(new List<FrameworkElement>())
);
// Set the default collection value in a class constructor.
public Aquarium() => SetValue(s_aquariumContentsPropertyKey, new List<FrameworkElement>());
// Declare a public get accessor.
public List<FrameworkElement> AquariumContents =>
(List<FrameworkElement>)GetValue(s_aquariumContentsPropertyKey.DependencyProperty);
}
public class Fish : FrameworkElement { }
Public Class Aquarium
Inherits DependencyObject
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata.
Private Shared ReadOnly s_aquariumContentsPropertyKey As DependencyPropertyKey =
DependencyProperty.RegisterReadOnly(
name:="AquariumContents",
propertyType:=GetType(List(Of FrameworkElement)),
ownerType:=GetType(Aquarium),
typeMetadata:=New FrameworkPropertyMetadata())
'typeMetadata:=New FrameworkPropertyMetadata(New List(Of FrameworkElement)))
' Set the default collection value in a class constructor.
Public Sub New()
SetValue(s_aquariumContentsPropertyKey, New List(Of FrameworkElement)())
End Sub
' Declare a public get accessor.
Public ReadOnly Property AquariumContents As List(Of FrameworkElement)
Get
Return CType(GetValue(s_aquariumContentsPropertyKey.DependencyProperty), List(Of FrameworkElement))
End Get
End Property
End Class
Public Class Fish
Inherits FrameworkElement
End Class
Следующий тестовый код создает экземпляры двух отдельных Aquarium
экземпляров и добавляет в каждую коллекцию разные Fish
элементы. При запуске кода вы увидите, что каждый Aquarium
экземпляр имеет один элемент коллекции, как ожидалось.
private void InitializeAquariums(object sender, RoutedEventArgs e)
{
Aquarium aquarium1 = new();
Aquarium aquarium2 = new();
aquarium1.AquariumContents.Add(new Fish());
aquarium2.AquariumContents.Add(new Fish());
MessageBox.Show(
$"aquarium1 contains {aquarium1.AquariumContents.Count} fish\r\n" +
$"aquarium2 contains {aquarium2.AquariumContents.Count} fish");
}
Private Sub InitializeAquariums(sender As Object, e As RoutedEventArgs)
Dim aquarium1 As New Aquarium()
Dim aquarium2 As New Aquarium()
aquarium1.AquariumContents.Add(New Fish())
aquarium2.AquariumContents.Add(New Fish())
MessageBox.Show($"aquarium1 contains {aquarium1.AquariumContents.Count} fish{Environment.NewLine}" +
$"aquarium2 contains {aquarium2.AquariumContents.Count} fish")
End Sub
Но если вы закомментируете конструктор класса и передайте значение коллекции по умолчанию в PropertyMetadataRegisterReadOnly(String, Type, Type, PropertyMetadata) метод, вы увидите, что каждый Aquarium
экземпляр получает два элемента коллекции! Это связано с тем, что оба Fish
экземпляра добавляются в один список, который разделяется всеми экземплярами класса Аквариума. Таким образом, если намерение предназначено для каждого экземпляра объекта, чтобы иметь собственный список, значение по умолчанию должно быть задано в конструкторе классов.
Инициализация коллекции чтения и записи
В следующем примере объявляется свойство зависимостей типа коллекции для чтения и записи в Aquarium
классе, используя методы Register(String, Type, Type) подписи, отличные от ключей, и SetValue(DependencyProperty, Object).
public class Aquarium : DependencyObject
{
// Register a dependency property with the specified property name,
// property type, and owner type. Store the dependency property
// identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumContentsProperty =
DependencyProperty.Register(
name: "AquariumContents",
propertyType: typeof(List<FrameworkElement>),
ownerType: typeof(Aquarium)
);
// Set the default collection value in a class constructor.
public Aquarium() => SetValue(AquariumContentsProperty, new List<FrameworkElement>());
// Declare public get and set accessors.
public List<FrameworkElement> AquariumContents
{
get => (List<FrameworkElement>)GetValue(AquariumContentsProperty);
set => SetValue(AquariumContentsProperty, value);
}
}
Public Class Aquarium
Inherits DependencyObject
' Register a dependency property with the specified property name,
' property type, and owner type. Store the dependency property
' identifier as a static member of the class.
Public Shared ReadOnly AquariumContentsProperty As DependencyProperty =
DependencyProperty.Register(
name:="AquariumContents",
propertyType:=GetType(List(Of FrameworkElement)),
ownerType:=GetType(Aquarium))
' Set the default collection value in a class constructor.
Public Sub New()
SetValue(AquariumContentsProperty, New List(Of FrameworkElement)())
End Sub
' Declare public get and set accessors.
Public Property AquariumContents As List(Of FrameworkElement)
Get
Return CType(GetValue(AquariumContentsProperty), List(Of FrameworkElement))
End Get
Set
SetValue(AquariumContentsProperty, Value)
End Set
End Property
End Class
Свойства зависимости FreezableCollection
Свойство зависимостей типа коллекции не автоматически сообщает об изменениях в его подпропастерствах. В результате, если вы привязываетесь к коллекции, привязка может не сообщать об изменениях, недействив некоторые сценарии привязки данных. Но если вы используете FreezableCollection<T> для типа свойства зависимостей, изменения свойств элементов коллекции правильно сообщаются, и привязка работает должным образом.
Чтобы включить вложенную привязку в коллекции объектов зависимостей, используйте тип FreezableCollection
коллекции с ограничением типа любого DependencyObject производного класса.
В следующем примере объявляется Aquarium
класс, содержащий FreezableCollection
ограничение FrameworkElementтипа. Значение коллекции по умолчанию не включается в PropertyMetadata переданный RegisterReadOnly(String, Type, Type, PropertyMetadata) метод, а конструктор классов используется для задания значения коллекции по умолчанию новым FreezableCollection
.
public class Aquarium : DependencyObject
{
// Register a dependency property with the specified property name,
// property type, and owner type.
private static readonly DependencyPropertyKey s_aquariumContentsPropertyKey =
DependencyProperty.RegisterReadOnly(
name: "AquariumContents",
propertyType: typeof(FreezableCollection<FrameworkElement>),
ownerType: typeof(Aquarium),
typeMetadata: new FrameworkPropertyMetadata()
);
// Store the dependency property identifier as a static member of the class.
public static readonly DependencyProperty AquariumContentsProperty =
s_aquariumContentsPropertyKey.DependencyProperty;
// Set the default collection value in a class constructor.
public Aquarium() => SetValue(s_aquariumContentsPropertyKey, new FreezableCollection<FrameworkElement>());
// Declare a public get accessor.
public FreezableCollection<FrameworkElement> AquariumContents =>
(FreezableCollection<FrameworkElement>)GetValue(AquariumContentsProperty);
}
Public Class Aquarium
Inherits DependencyObject
' Register a dependency property with the specified property name,
' property type, and owner type.
Private Shared ReadOnly s_aquariumContentsPropertyKey As DependencyPropertyKey =
DependencyProperty.RegisterReadOnly(
name:="AquariumContents",
propertyType:=GetType(FreezableCollection(Of FrameworkElement)),
ownerType:=GetType(Aquarium),
typeMetadata:=New FrameworkPropertyMetadata())
' Set the default collection value in a class constructor.
Public Sub New()
SetValue(s_aquariumContentsPropertyKey, New FreezableCollection(Of FrameworkElement)())
End Sub
' Declare a public get accessor.
Public ReadOnly Property AquariumContents As FreezableCollection(Of FrameworkElement)
Get
Return CType(GetValue(s_aquariumContentsPropertyKey.DependencyProperty), FreezableCollection(Of FrameworkElement))
End Get
End Property
End Class
См. также
.NET Desktop feedback
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по