Vlastnosti závislostí typu kolekce
Toto téma obsahuje pokyny a navrhované vzory pro implementaci vlastnosti závislosti, kde typ vlastnosti je typ kolekce.
Implementace vlastnosti závislosti typu kolekce
Obecně platí, že vzor implementace, který sledujete, je definovat obálku vlastností CLR, kde tato vlastnost je podporována identifikátorem DependencyProperty , nikoli polem nebo jiným konstruktorem. Stejný vzor použijete při implementaci vlastnosti typu kolekce. Vlastnost typu kolekce však představuje určitou složitost vzoru vždy, když typ, který je obsažen v kolekci, je sama o sobě DependencyObject nebo Freezable odvozená třída.
Inicializace kolekce nad rámec výchozí hodnoty
Při vytváření vlastnosti závislosti nezadáte výchozí hodnotu vlastnosti jako počáteční hodnotu pole. Místo toho zadáte výchozí hodnotu prostřednictvím metadat vlastností závislosti. Pokud je vaše vlastnost referenčním typem, výchozí hodnota zadaná v metadatech vlastností závislostí není výchozí hodnotou pro každou instanci; je to výchozí hodnota, která se vztahuje na všechny instance typu. Proto musíte být opatrní, abyste nepoužíli jedinečnou statickou kolekci definovanou metadaty vlastnosti kolekce jako pracovní výchozí hodnotu pro nově vytvořené instance vašeho typu. Místo toho je nutné, abyste záměrně nastavili hodnotu kolekce na jedinečnou kolekci (instance) jako součást logiky konstruktoru třídy. Jinak vytvoříte neúmyslnou třídu singletonu.
Představte si následující příklad. Následující část příkladu ukazuje definici třídy Aquarium
, která obsahuje chybu s výchozí hodnotou. Třída definuje vlastnost AquariumObjects
závislosti typu kolekce , která používá obecný List<T> typ s FrameworkElement omezením typu. Register(String, Type, Type, PropertyMetadata) Ve volání vlastnosti závislosti metadata vytvoří výchozí hodnotu jako nový obecný List<T>.
Upozorňující
Následující kód se nechová správně.
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); }
}
// ...
}
Public Class Fish
Inherits FrameworkElement
End Class
Public Class Aquarium
Inherits DependencyObject
Private Shared ReadOnly AquariumContentsPropertyKey As DependencyPropertyKey = DependencyProperty.RegisterReadOnly("AquariumContents", GetType(List(Of FrameworkElement)), GetType(Aquarium), New FrameworkPropertyMetadata(New List(Of FrameworkElement)()))
Public Shared ReadOnly AquariumContentsProperty As DependencyProperty = AquariumContentsPropertyKey.DependencyProperty
Public ReadOnly Property AquariumContents() As List(Of FrameworkElement)
Get
Return CType(GetValue(AquariumContentsProperty), List(Of FrameworkElement))
End Get
End Property
' ...
End Class
Pokud jste ale právě opustili kód, jak je znázorněno, tato výchozí hodnota seznamu je sdílena pro všechny instance Aquarium
. Pokud jste spustili následující testovací kód, který má ukázat, jak byste vytvořili instanci dvou samostatných Aquarium
instancí a přidali do každého z nich jeden jiný Fish
, uvidíte překvapivý výsledek:
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");
Dim myAq1 As New Aquarium()
Dim myAq2 As New Aquarium()
Dim f1 As New Fish()
Dim f2 As 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")
Místo toho, aby každá kolekce měla jeden počet, má každá kolekce počet dvou! Důvodem je to, že každá Aquarium
přidala do Fish
výchozí kolekce hodnot, která byla výsledkem jediného volání konstruktoru v metadatech a je proto sdílena mezi všemi instancemi. Tato situace je téměř nikdy to, co chcete.
Chcete-li tento problém opravit, je nutné obnovit hodnotu vlastnosti závislostí kolekce na jedinečnou instanci, jako součást volání konstruktoru třídy. Vzhledem k tomu, že je vlastnost závislostí jen pro čtení, použijete metodu SetValue(DependencyPropertyKey, Object) k jeho nastavení pomocí DependencyPropertyKey , která je přístupná pouze v rámci třídy.
public Aquarium() : base()
{
SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>());
}
Public Sub New()
MyBase.New()
SetValue(AquariumContentsPropertyKey, New List(Of FrameworkElement)())
End Sub
Pokud jste teď spustili stejný testovací kód znovu, mohli byste vidět očekávané výsledky, kdy každá Aquarium
podporovala svou vlastní jedinečnou kolekci.
Pokud jste se rozhodli, že vlastnost kolekce bude pro čtení i zápis, bude tento model mírně proměnlivý. V takovém případě můžete volat objekt veřejné sady z konstruktoru k inicializaci, který bude stále volat neklíčový SetValue(DependencyProperty, Object) podpis v rámci obálky sady pomocí veřejného DependencyProperty identifikátoru.
Vytváření sestav změn hodnoty vazby z vlastností kolekce
Vlastnost kolekce, která je sama o sobě vlastností závislosti, nehlásí automaticky změny v jeho dílčích obdobích. Pokud vytváříte vazby do kolekce, může to zabránit vytváření zpráv o změnách vazby, a tím zneplatnit některé scénáře datových vazeb. Pokud však jako typ kolekce použijete typ FreezableCollection<T> kolekce, pak se správně ohlásí dílčí změny obsažených prvků v kolekci a vazba funguje podle očekávání.
Chcete-li povolit vazbu subproperty v kolekci objektů závislostí, vytvořte vlastnost kolekce jako typ FreezableCollection<T>s omezením typu pro danou kolekci na jakoukoli DependencyObject odvozenou třídu.
Viz také
.NET Desktop feedback
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro