Content update - Dependency properties collection (user story 1878471) (#1173)

* Add code snippets

* Add article and updated code snippets

* Resolve conflicts

* Make reviewer requested changes

* Name class parameters and show how to pass in default value in metedata

* Add example for creating a FreezableCollection type dependency property

* Add newline

* Further edits
This commit is contained in:
Tris Shores
2021-10-22 09:55:35 -07:00
committed by GitHub
parent 090e480dcf
commit cf712b82b0
18 changed files with 695 additions and 0 deletions
+8
View File
@@ -496,6 +496,14 @@
{
"source_path": "dotnet-desktop-guide/framework/wpf/properties/how-to-register-an-attached-property.md",
"redirect_url": "/dotnet/desktop/wpf/advanced/how-to-register-an-attached-property?view=netframeworkdesktop-4.8"
},
{
"source_path": "dotnet-desktop-guide/net/wpf/advanced/collection-type-dependency-properties.md",
"redirect_url": "/dotnet/desktop/wpf/properties/collection-type-dependency-properties?view=netdesktop-5.0"
},
{
"source_path": "dotnet-desktop-guide/framework/wpf/properties/collection-type-dependency-properties.md",
"redirect_url": "/dotnet/desktop/wpf/advanced/collection-type-dependency-properties?view=netframeworkdesktop-4.8"
}
]
}
@@ -0,0 +1,65 @@
---
title: "Collection-type dependency properties"
description: "Learn how to implement a dependency property that's a collection type and how to assign a default collection value."
ms.date: "10/06/2021"
dev_langs:
- "csharp"
- "vb"
helpviewer_keywords:
- "properties [WPF], dependency"
- "properties [WPF], collection-type"
- "dependency properties [WPF]"
- "collection-type properties [WPF]"
---
<!-- The acrolinx score was 96 on 10/07/2021-->
# Collection-type dependency properties (WPF .NET)
This article provides guidance and suggested patterns for implementing a dependency property that's a collection type.
## Implement a collection-type dependency property
In general, the implementation pattern for a dependency property is a CLR property wrapper backed by a <xref:System.Windows.DependencyProperty> identifier instead of a field or other construct. You can follow the same pattern when you implement a collection-type dependency property. The pattern is more complex if the collection element type is a <xref:System.Windows.DependencyObject> or a <xref:System.Windows.Freezable> derived class.
## Initialize the collection
When you create a dependency property, you typically specify the default value through dependency property metadata instead of specifying an initial property value. However, if your property value is a reference type, the default value should be set in the constructor of the class that registers the dependency property. The dependency property metadata shouldn't include a default reference-type value because that value will be assigned to all instances of the class, creating a singleton class.
The following example declares an `Aquarium` class that contains a collection of <xref:System.Windows.FrameworkElement> elements in a generic <xref:System.Collections.Generic.List%601>. A default collection value isn't included in the <xref:System.Windows.PropertyMetadata> passed to the <xref:System.Windows.DependencyProperty.RegisterReadOnly%28System.String%2CSystem.Type%2CSystem.Type%2CSystem.Windows.PropertyMetadata%29> method, and instead the class constructor is used to set the default collection value to a new generic `List`.
:::code language="csharp" source="./snippets/collection-type-dependency-properties/csharp/MainWindow.xaml.cs" id="SetCollectionDefaultValueInConstructor":::
:::code language="vb" source="./snippets/collection-type-dependency-properties/vb/MainWindow.xaml.vb" id="SetCollectionDefaultValueInConstructor":::
The following test code instantiates two separate `Aquarium` instances and adds a different `Fish` item to each collection. If you run the code, you'll see that each `Aquarium` instance has a single collection item, as expected.
:::code language="csharp" source="./snippets/collection-type-dependency-properties/csharp/MainWindow.xaml.cs" id="InitializeAquariums":::
:::code language="vb" source="./snippets/collection-type-dependency-properties/vb/MainWindow.xaml.vb" id="InitializeAquariums":::
But, if you comment out the class constructor and pass the default collection value as <xref:System.Windows.PropertyMetadata> to the <xref:System.Windows.DependencyProperty.RegisterReadOnly%28System.String%2CSystem.Type%2CSystem.Type%2CSystem.Windows.PropertyMetadata%29> method, you'll see that each `Aquarium` instance gets two collection items! This is because both `Fish` instances are added to the same list, which is shared by all instances of the Aquarium class. So, when the intent is for each object instance to have its own list, the default value should be set in the class constructor.
### Initialize a read-write collection
The following example declares a read-write collection-type dependency property in the `Aquarium` class, using the non-key signature methods <xref:System.Windows.DependencyProperty.Register%28System.String%2CSystem.Type%2CSystem.Type%29> and <xref:System.Windows.DependencyObject.SetValue%28System.Windows.DependencyProperty%2CSystem.Object%29>.
:::code language="csharp" source="./snippets/collection-type-dependency-properties/csharp/MainWindow.xaml.cs" id="ReadWriteDependencyProperty":::
:::code language="vb" source="./snippets/collection-type-dependency-properties/vb/MainWindow.xaml.vb" id="ReadWriteDependencyProperty":::
## FreezableCollection dependency properties
A collection-type dependency property doesn't automatically report changes in its subproperties. As a result, if you're binding to a collection, the binding might not report changes, invalidating some data binding scenarios. But, if you use <xref:System.Windows.FreezableCollection%601> for the dependency property type, changes to the properties of collection elements are properly reported and binding works as expected.
To enable subproperty binding in a collection of dependency objects, use the collection type `FreezableCollection`, with a type constraint of any <xref:System.Windows.DependencyObject> derived class.
The following example declares an `Aquarium` class that contains a `FreezableCollection` with a type constraint of <xref:System.Windows.FrameworkElement>. A default collection value isn't included in the <xref:System.Windows.PropertyMetadata> passed to the <xref:System.Windows.DependencyProperty.RegisterReadOnly%28System.String%2CSystem.Type%2CSystem.Type%2CSystem.Windows.PropertyMetadata%29> method, and instead the class constructor is used to set the default collection value to a new `FreezableCollection`.
:::code language="csharp" source="./snippets/collection-type-dependency-properties/csharp/MainWindow.xaml.cs" id="FreezableCollectionAquarium":::
:::code language="vb" source="./snippets/collection-type-dependency-properties/vb/MainWindow.xaml.vb" id="FreezableCollectionAquarium":::
## See also
- <xref:System.Windows.FreezableCollection%601>
- [XAML and Custom Classes for WPF](/dotnet/desktop/wpf/advanced/xaml-and-custom-classes-for-wpf?view=netframeworkdesktop-4.8&preserve-view=true)
- [Data Binding Overview](/dotnet/desktop/wpf/data/)
- [Dependency Properties Overview](dependency-properties-overview.md)
- [Custom Dependency Properties](/dotnet/desktop/wpf/advanced/custom-dependency-properties?view=netframeworkdesktop-4.8&preserve-view=true)
- [Dependency Property Metadata](/dotnet/desktop/wpf/advanced/dependency-property-metadata?view=netframeworkdesktop-4.8&preserve-view=true)
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace CodeSampleCsharp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
<Window x:Class="CodeSampleCsharp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:src="clr-namespace:CodeSampleCsharp"
Title="Collection-Type Dependency Properties" Height="100" Width="400">
<StackPanel>
<Button Click="InitializeAquariums">Set collection default value in class constructor</Button>
</StackPanel>
</Window>
@@ -0,0 +1,141 @@
using System.Collections.Generic;
using System.Windows;
namespace CodeSampleCsharp
{
/// <summary>
/// Interaction logic for MainWindow.xaml.
/// </summary>
public partial class MainWindow : Window
{
//<InitializeAquariums>
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");
}
//</InitializeAquariums>
}
//<SetCollectionDefaultValueInConstructor>
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>())
);
// 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 List<FrameworkElement>());
// Declare a read-only property.
public List<FrameworkElement> AquariumContents =>
(List<FrameworkElement>)GetValue(AquariumContentsProperty);
}
public class Fish : FrameworkElement { }
//</SetCollectionDefaultValueInConstructor>
public class ReadWriteAquariumContents
{
private static void InitializeAquariums()
{
Aquarium aquarium1 = new();
Aquarium aquarium2 = new();
aquarium1.AquariumContents.Add(new Fish());
aquarium2.AquariumContents.Add(new Fish());
aquarium2.AquariumContents = new List<FrameworkElement>();
MessageBox.Show(
$"Aquarium1 contains {aquarium1.AquariumContents.Count} fish\r\n" +
$"Aquarium2 contains {aquarium2.AquariumContents.Count} fish");
}
//<ReadWriteDependencyProperty>
public class Aquarium : DependencyObject
{
// Register a dependency property with the specified property name,
// property type, and owner type.
private static readonly DependencyProperty s_aquariumContentsProperty =
DependencyProperty.Register(
name: "AquariumContents",
propertyType: typeof(List<FrameworkElement>),
ownerType: typeof(Aquarium)
);
// Store the dependency property identifier as a static member of the class.
public static readonly DependencyProperty AquariumContentsProperty =
s_aquariumContentsProperty;
// Set the default collection value in a class constructor.
public Aquarium() => SetValue(s_aquariumContentsProperty, new List<FrameworkElement>());
// Declare a read-write property.
public List<FrameworkElement> AquariumContents
{
get => (List<FrameworkElement>)GetValue(AquariumContentsProperty);
set => SetValue(AquariumContentsProperty, value);
}
}
//</ReadWriteDependencyProperty>
public class Fish : FrameworkElement { }
}
public class FreezableCollectionAquarium
{
public static void InitializeAquariums()
{
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");
}
//<FreezableCollectionAquarium>
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 read-only property.
public FreezableCollection<FrameworkElement> AquariumContents =>
(FreezableCollection<FrameworkElement>)GetValue(AquariumContentsProperty);
}
//</FreezableCollectionAquarium>
public class Fish : FrameworkElement { }
}
}
@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CodeSampleCsharp.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeSampleCsharp.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,9 @@
<Application x:Class="CodeSampleCsharp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CodeSampleCsharp"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
@@ -0,0 +1,9 @@
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CodeSampleVb"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
@@ -0,0 +1,6 @@
Class Application
' Application-level events, such as Startup, Exit, and DispatcherUnhandledException
' can be handled in this file.
End Class
@@ -0,0 +1,11 @@
Imports System.Windows
'The ThemeInfo attribute describes where any theme specific and generic resource dictionaries can be found.
'1st parameter: where theme specific resource dictionaries are located
'(used if a resource is not found in the page,
' or application resource dictionaries)
'2nd parameter: where the generic resource dictionary is located
'(used if a resource is not found in the page,
'app, and any theme specific resource dictionaries)
<Assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)>
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<RootNamespace>CodeSampleVb</RootNamespace>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<Import Include="System.Windows" />
<Import Include="System.Windows.Controls" />
<Import Include="System.Windows.Data" />
<Import Include="System.Windows.Documents" />
<Import Include="System.Windows.Input" />
<Import Include="System.Windows.Media" />
<Import Include="System.Windows.Media.Imaging" />
<Import Include="System.Windows.Navigation" />
<Import Include="System.Windows.Shapes" />
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
<Window x:Class="CodeSampleVb.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:src="clr-namespace:CodeSampleCsharp"
Title="Collection-Type Dependency Properties" Height="100" Width="400">
<StackPanel>
<Button Click="InitializeAquariums">Set collection default value in class constructor</Button>
</StackPanel>
</Window>
@@ -0,0 +1,161 @@
Namespace CodeSampleVb
' <summary>
' Interaction logic for MainWindow.xaml.
' </summary>
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
'<InitializeAquariums>
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
'</InitializeAquariums>
End Class
'<SetCollectionDefaultValueInConstructor>
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)))
' Store the dependency property identifier as a static member of the class.
Public Shared ReadOnly AquariumContentsProperty As DependencyProperty =
s_aquariumContentsPropertyKey.DependencyProperty
' Set the default collection value in a class constructor.
Public Sub New()
SetValue(s_aquariumContentsPropertyKey, New List(Of FrameworkElement)())
End Sub
' Declare a read-only property.
Public ReadOnly Property AquariumContents As List(Of FrameworkElement)
Get
Return CType(GetValue(AquariumContentsProperty), List(Of FrameworkElement))
End Get
End Property
End Class
Public Class Fish
Inherits FrameworkElement
End Class
'</SetCollectionDefaultValueInConstructor>
Public Class ReadWriteAquariumContents
Public Shared Sub InitializeAquariums()
Dim aquarium1 As New Aquarium()
Dim aquarium2 As New Aquarium()
aquarium1.AquariumContents.Add(New Fish())
aquarium2.AquariumContents.Add(New Fish())
aquarium2.AquariumContents = New List(Of FrameworkElement)()
MessageBox.Show($"Aquarium1 contains {aquarium1.AquariumContents.Count} fish{Environment.NewLine}" +
$"Aquarium2 contains {aquarium2.AquariumContents.Count} fish")
End Sub
'<ReadWriteDependencyProperty>
Public Class Aquarium
Inherits DependencyObject
' Register a dependency property with the specified property name,
' property type, and owner type.
Private Shared ReadOnly s_aquariumContentsProperty As DependencyProperty =
DependencyProperty.Register(
name:="AquariumContents",
propertyType:=GetType(List(Of FrameworkElement)),
ownerType:=GetType(Aquarium))
' Store the dependency property identifier as a static member of the class.
Public Shared ReadOnly AquariumContentsProperty As DependencyProperty =
s_aquariumContentsProperty
' Set the default collection value in a class constructor.
Public Sub New()
SetValue(s_aquariumContentsProperty, New List(Of FrameworkElement)())
End Sub
' Declare a read-write property.
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
'</ReadWriteDependencyProperty>
Public Class Fish
Inherits FrameworkElement
End Class
End Class
Public Class FreezableCollectionAquarium
Public Shared Sub InitializeAquariums()
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
'<FreezableCollectionAquarium>
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())
' Store the dependency property identifier as a static member of the class.
Public Shared ReadOnly AquariumContentsProperty As DependencyProperty =
s_aquariumContentsPropertyKey.DependencyProperty
' Set the default collection value in a class constructor.
Public Sub New()
SetValue(s_aquariumContentsPropertyKey, New FreezableCollection(Of FrameworkElement)())
End Sub
' Declare a read-only property.
Public ReadOnly Property AquariumContents As FreezableCollection(Of FrameworkElement)
Get
Return CType(GetValue(AquariumContentsProperty), FreezableCollection(Of FrameworkElement))
End Get
End Property
End Class
'</FreezableCollectionAquarium>
Public Class Fish
Inherits FrameworkElement
End Class
End Class
End Namespace
+2
View File
@@ -86,6 +86,8 @@ items:
href: properties/dependency-property-value-precedence.md
- name: Register an attached property
href: properties/how-to-register-an-attached-property.md
- name: Collection-type dependency properties
href: properties/collection-type-dependency-properties.md
- name: Resources
items:
- name: Overview
+5
View File
@@ -334,6 +334,11 @@
"SourceUrl": "/dotnet/desktop/wpf/advanced/how-to-register-an-attached-property?view=netframeworkdesktop-4.8",
"TargetUrl": "/dotnet/desktop/wpf/properties/how-to-register-an-attached-property?view=netdesktop-5.0"
},
{
"Redirect": "TwoWay",
"SourceUrl": "/dotnet/desktop/wpf/advanced/collection-type-dependency-properties?view=netframeworkdesktop-4.8",
"TargetUrl": "/dotnet/desktop/wpf/properties/collection-type-dependency-properties?view=netdesktop-5.0"
},
// Systems - XAML
{