Benutzerdefinierte Abhängigkeitseigenschaften

Applies to Windows and Windows Phone

Hier erklären wir, wie Sie eigene Abhängigkeitseigenschaften für eine Windows-Runtime-App mit C++, C# oder Visual Basic verwenden können. Wir zählen mögliche Gründen für die Erstellung benutzerdefinierter Abhängigkeitseigenschaften durch App-Entwickler und Komponentenautoren auf. Des Weiteren beschreiben wir die Implementierungsschritte für eine benutzerdefinierte Abhängigkeitseigenschaft sowie einige bewährte Methoden, die die Leistung, Benutzerfreundlichkeit und Vielseitigkeit der Abhängigkeitseigenschaft verbessern.

Voraussetzungen

Wir gehen davon aus, dass Sie die Übersicht über Abhängigkeitseigenschaften bereits gelesen haben und dass Sie Abhängigkeitseigenschaften aus der Perspektive eines Konsumenten von bestehenden Abhängigkeitseigenschaften verstehen. Für ein besseres Verständnis der in diesem Thema aufgeführten Beispiele sollten Sie XAML verstehen und wissen, wie eine einfache Windows-Runtime-App mit C++, C# oder Visual Basic geschrieben wird.

Was ist eine Abhängigkeitseigenschaft?

Abhängigkeitseigenschaften sind Eigenschaften, die im Windows-Runtime-Eigenschaftssystem durch Aufrufen der DependencyProperty.Register-Methode registriert sind und durch ein DependencyProperty-Bezeichnermember der definierenden Klasse identifiziert wird. Was andernfalls eine Common Language Runtime (CLR)- oder eine C++-Eigenschaft wäre, die die Formatierung, Datenbindung und Animationen sowie Standardwerte unterstützt, können Sie durch die Implementierung als Abhängigkeitseigenschaft aktivieren. Abhängigkeitseigenschaften können nur von DependencyObject-Typen verwendet werden. Das DependencyObject ist jedoch recht weit oben in der Klassenhierarchie, sodass die Mehrzahl der Klassen, die für die UI- und Darstellungsunterstützung bestimmt sind, Abhängigkeitseigenschaften unterstützen können. Weitere Informationen zu Abhängigkeitseigenschaften und für die in dieser Dokumentation verwendeten Begriffe und Konventionen finden Sie unter Übersicht über Abhängigkeitseigenschaften.

Beispiele für Abhängigkeitseigenschaften in Windows-Runtime: Control.Background, FrameworkElement.Width, TextBox.Text und viele mehr. Jede durch eine Klasse verfügbar gemachte Abhängigkeitseigenschaft hat eine entsprechende public static readonly Eigenschaft vom Typ DependencyProperty, die für dieselbe Klasse verfügbar gemacht wird und den Bezeichner für die Abhängigkeitseigenschaft darstellt. Für die Benennung des Bezeichners wird folgende Konvention verwendet: der Name der Abhängigkeitseigenschaft mit der angehängten Zeichenfolge "Property". Der entsprechende DependencyProperty-Bezeichner für die Eigenschaft Control.Background ist beispielsweise Control.BackgroundProperty. Der Bezeichner speichert die Informationen zur Abhängigkeitseigenschaft, die bei der Registrierung gelten. Anschließend kann der Bezeichner dann für andere Vorgänge verwendet werden, die die Abhängigkeitseigenschaft betreffen, z. B. das Aufrufen von SetValue.

Eigenschaftenwrapper

Abhängigkeitseigenschaften haben für gewöhnlich eine Wrapper-Implementierung. Ohne den Wrapper können die Eigenschaften nur durch die Verwendung der Methoden der Abhängigkeitseigenschafts-Hilfsprogramme GetValue und SetValue sowie durch die Nutzung des Bezeichners als Parameter abgerufen oder festgelegt werden. Hierbei handelt es sich um eine ziemlich ungewöhnliche Verwendung für eine vermeintliche Eigenschaft. Mit dem Wrapper können Ihr Code und jegliche anderen Codes, die auf die Abhängigkeitseigenschaft verweisen, eine unkomplizierte, für die von Ihnen verwendete Sprache natürliche Syntax für Objekteigenschaften verwenden.

Wenn Sie eine benutzerdefinierte Abhängigkeitseigenschaft selbst implementieren und diese veröffentlichen und leicht auffindbar machen möchten, definieren Sie auch die Eigenschaftenwrapper. Eigenschaftenwrapper sind des Weiteren nützlich für die Berichterstattung über grundlegende Informationen der Abhängigkeitseigenschaft für Betrachtungen und statische Analysen. Der Wrapper befindet sich insbesondere dort, wo Sie Attribute, wie z. B. ContentPropertyAttribute platzieren.

Wann sollten Sie eine Eigenschaft als Abhängigkeitseigenschaft implementieren?

Wenn Sie eine öffentliche Lese-/Schreibeigenschaft in eine Klasse implementieren und die Klasse von DependencyObject abgeleitet ist, haben Sie die Möglichkeit Ihre Eigenschaft als Abhängigkeitseigenschaft zu nutzen. In einigen Fällen ist die typische Methode, Ihre Eigenschaft mit einem privaten Feld abzusichern, angemessen. Das Definieren Ihrer benutzerdefinierten Eigenschaft als Abhängigkeitseigenschaft ist nicht immer notwendig oder angemessen. Die Entscheidung hängt von den Szenarien ab, die Ihre Eigenschaft unterstützen soll.

Sie können in Betracht ziehen, Ihre Eigenschaft als Abhängigkeitseigenschaft zu implementieren, wenn diese eine oder mehrere der folgenden Eigenschaften von Windows-Runtime oder aus Windows-Runtime-Apps unterstützen soll:

  • Einrichten einer Eigenschaft über eine Style
  • Funktion als gültige Zieleigenschaft für Datenbindung
  • Unterstützung animierter Werte durch ein Storyboard
  • Berichterstattung, wenn der vorherige Wert der Eigenschaft von folgenden Dingen geändert wurde:
    • Aktionen, die von dem Eigenschaftensystem durchgeführt wurden
    • Die Umgebung
    • Benutzeraktionen
    • Lese- und Schreibstile

Prüfliste für die Definition einer Abhängigkeitseigenschaft

Das Definieren einer Abhängigkeitseigenschaft umfasst mehrere Begriffe. Bei diesen Begriffen handelt es sich nicht unbedingt um einzelne Schritte, da einige Schritte bei der Implementierung im Code in einzelnen Codezeilen zusammengefasst werden: Diese Liste verschafft Ihnen einen schnellen Überblick.. Wir werden später noch genauer auf jeden Begriff eingehen und Ihnen Beispielcodes in verschiedenen Sprachen zeigen.

  • (Optional) Erstellen Sie Eigenschaftenmetadaten für die Abhängigkeitseigenschaft. Sie benötigen Eigenschaftenmetadaten nur, wenn Sie ein Verhalten bei einem "PropertyChanged"-Ereignis oder einen auf Metadaten basierenden Standardwert haben möchten, der durch Aufrufen von ClearValue wiederhergestellt werden kann.

  • Registrieren Sie den Eigenschaftennamen im Eigenschaftensystem (Register aufrufen), indem Sie einen Besitzertyp und den Typ des Eigenschaftswerts angeben. Es gibt auch einen erforderlichen Parameter für Register, der Eigenschaftenmetadaten benötigt. Geben Sie den Wert null dafür an oder legen Sie die vorliegenden Eigenschaftenmetadaten fest (falls welche deklariert wurden).

  • Definieren Sie einen DependencyProperty-Bezeichner als ein public static readonly Eigenschaftenmember des Besitzertyps.

  • Definieren Sie eine Wrappereigenschaft nach dem Accessor-Modell für Eigenschaften, das in der von Ihnen implementierten Sprache verwendet wird. Der Name für die Wrappereigenschaft sollte mit der Zeichenfolge name, die Sie in Register verwendet haben, übereinstimmen. Implementieren Sie die get- und set-Accessors, um den Wrapper durch das Aufrufen von GetValue und SetValue sowie durch die Nutzung Ihres eigenen Eigenschaftenbezeichners als Parameter, mit der ihn umschließenden Abhängigkeitseigenschaft zu verbinden.
  • (Optional) Platzieren Sie Attribute, wie ContentPropertyAttribute, auf dem Wrapper.

Hinweis  Wenn Sie eine benutzerdefinierte angefügte Eigenschaft definieren, wird der Wrapper für gewöhnlich weggelassen. Stattdessen verwenden Sie einen anderen Accessor-Stil, den ein XAML-Prozessor verwenden kann. Siehe Benutzerdefinierte angefügte Eigenschaften.

Registrieren der Eigenschaft

Damit Ihre Eigenschaft zu einer Abhängigkeitseigenschaft wird, müssen Sie die Eigenschaft in einem Eigenschaftenspeicher registrieren, die vom Windows-Runtime-Eigenschaftensystem verwaltet wird. Außerdem müssen Sie der Eigenschaft einen eindeutigen Bezeichner zuweisen, der bei späteren Vorgängen des Eigenschaftensystems als Qualifizierer verwendet wird. Bei diesen Vorgängen kann es sich um interne Vorgänge oder um Ihre eigenen APIs handeln, die Codeaufrufe durchführen. Rufen Sie für die Registrierung der Eigenschaft die Register-Methode auf.

Bei Microsoft .NET-Sprachen (C# und Microsoft Visual Basic) rufen Sie Register im Text Ihrer Klasse auf (innerhalb der Klasse, aber außerhalb jeglicher Memberdefinitionen). Der Bezeichner wird auch vom Register-Methodenaufruf als Rückgabewert bereitgestellt. Der Register-Aufruf erfolgt für gewöhnlich außerhalb von anderen Memberdefinitionen, weil Sie diesen Rückgabewert verwenden, um als Teil der Klasse eine public static readonly Eigenschaft vom Typ DependencyProperty zuzuweisen und zu erstellen. Dieses Feld wird zum Bezeichner für Ihre Abhängigkeitseigenschaft. Hier finden Sie Beispiele für den Register-Aufruf.


public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null)
);

Hinweis  Das Registrieren der Abhängigkeitseigenschaft im Klassentext ist die gängigste Art der Implementierung, aber Sie können eine Abhängigkeitseigenschaft auch im statischen Konstruktor der Klasse registrieren. Dieser Ansatz eignet sich ggf., wenn Sie mehr als eine Codezeile benötigen, um die Abhängigkeitseigenschaft zu initialisieren.

Bei C++ haben Sie verschiedene Möglichkeiten für die Aufteilung der Implementierung auf die Kopfzeile und die Codedatei. Die gängigste Art der Aufteilung ist die Deklaration des Bezeichners als public static Eigenschaft in der Kopfzeile, mit einer get-Implementierung aber keinem set. Die get-Implementierung bezieht sich auf ein privates Feld, eine nicht initialisierte DependencyProperty-Instanz. Sie können des Weiteren die Wrapper und die get- und set-Implementierungen des Wrappers deklarieren. In diesem Fall beinhaltet der Wrapper eine minimale Implementierung. Falls der Wrapper eine Zuordnung zu Windows-Runtime benötigt, führen Sie die Zuordnung auch in der Kopfzeile durch. Legen Sie den Register-Aufruf innerhalb einer Hilfsfunktion, die nur bei der ersten Initialisierung der App ausgeführt wird, in die Codedatei. Verwenden Sie den Rückgabewert von Register, um die statischen, aber noch nicht initialisierten Bezeichner zu füllen, die Sie in der Kopfzeile deklariert haben, die Sie im Stammbereich der Implementierungsdatei auf nullptr festgelegt haben.


//.h file
//using namespace Windows::UI::Xaml::Controls;
//using namespace Windows::UI::Xaml::Interop;
//using namespace Windows::UI::Xaml;
//using namespace Platform;

public ref class ImageWithLabelControl sealed : public Control
{  
private:
    static DependencyProperty^ _LabelProperty;
...
public:
    static void RegisterDependencyProperties(); 
    static property DependencyProperty^ LabelProperty
    {
        DependencyProperty^ get() {return _LabelProperty;}
    }
...
};



//.cpp file
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml.Interop;

DependencyProperty^ ImageWithLabelControl::_LabelProperty = nullptr;

// This function is called from the App constructor in App.xaml.cpp 
// to register the properties
void ImageWithLabelControl::RegisterDependencyProperties() 
{ 
    if (_LabelProperty == nullptr) 
    { 
        _LabelProperty = DependencyProperty::Register(
          "Label", Platform::String::typeid, ImageWithLabelControl::typeid, nullptr); 
    } 
}

Hinweis  Beim C++-Code besteht der Grund für das private Feld und die öffentliche schreibgeschützte Eigenschaft auf der Oberfläche der DependencyProperty darin, dass andere Aufrufer und Nutzer Ihrer Abhängigkeitseigenschaft auch eigene Hilfsprogramm-APIs nutzen können, die den Bezeichner benötigen. Wenn Sie den Bezeichner nicht preisgeben, können andere Benutzer diese Hilfsprogramm-APIs nicht verwenden. Beispiele für eine API und Szenarien dieser Art sind GetValue, SetValue, ClearValue, GetAnimationBaseValue, SetBinding und Setter.Property. Sie können dafür kein öffentliches Feld verwenden, da die Kompilierungsregeln der Windows-Runtime keine öffentlichen Datenmember zulassen, die Verweistypen wie DependencyProperty verwenden.

Namenskonventionen für Abhängigkeitseigenschaften

Für Abhängigkeitseigenschaften gelten Namenskonventionen, die Sie bis auf bestimmte Ausnahmefälle befolgen müssen. Die Abhängigkeitseigenschaft selbst hat einen Hauptnamen (im vorherigen Beispiel "Label") der als erster Parameter von Register angegeben wird. Dieser Name muss innerhalb eines jeden Registrierungstyps eindeutig sein. Diese Eindeutigkeit muss auch von vererbten Membern eingehalten werden. Für Abhängigkeitseigenschaften, die über Basistypen geerbt werden, gilt, dass sie bereits Teil des Registrierungstyps sind. Die Namen von geerbten Eigenschaften können nicht erneut registriert werden.

Achtung  Obwohl der von Ihnen angegebene Name jeder Zeichenfolge-Bezeichner sein kann, der bei der Programmierung in der von Ihnen ausgewählten Sprache gültig ist, möchten Sie Ihre Abhängigkeitseigenschaft möglicherweise auch in XAML verwenden. Für die Verwendung in XAML muss der von Ihnen gewählte Eigenschaftenname ein gültiger XAML-Name sein. Weitere Informationen finden Sie in der XAML-Übersicht.

Kombinieren Sie bei der Erstellung einer Bezeichnereigenschaft den von Ihnen registrierten Eigenschaftennamen mit dem Namenszusatz "Property" (zum Beispiel "LabelProperty"). Diese Eigenschaft ist Ihr Bezeichner für die Abhängigkeitseigenschaft und sie wird als Eingabe für die SetValue- und GetValue-Aufrufe, die Sie in Ihren eigenen Eigenschaftenwrappern vornehmen, verwendet. Sie wird des Weiteren vom Eigenschaftensystem und u. U. von XAML-Prozessoren verwendet.

Implementieren des Wrappers

Ihr Eigenschaftenwrapper sollte GetValue in der get-Implementierung und SetValue in der set-Implementierung aufrufen.

Achtung  Von Ausnahmefällen abgesehen, sollten Ihre Wrapperimplementierungen jeweils nur die Aktion GetValue und SetValue ausführen. Andernfalls bekommen Sie ein anderes Verhalten, wenn Ihre Eigenschaft über XAML anstelle über Code festgelegt wurde. Aus Effizienzgründen umgeht der XAML-Parser Wrapper, wenn Abhängigkeitseigenschaften festgelegt werden. Wenn möglich, verwendet er die Registrierung von Abhängigkeitseigenschaften.


public String Label
{
    get { return (String)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}

Eigenschaftenmetadaten für eine benutzerdefinierte Abhängigkeitseigenschaft

Wenn Eigenschaftenmetadaten einer Abhängigkeitseigenschaft zugewiesen werden, gelten die gleichen Metadaten für diese Eigenschaft für jede Instanz des Eigenschaftenbesitzers oder seiner Unterklassen. Sie können zwei Verhalten in Eigenschaftenmetadaten festlegen:

  • Einen Standardwert, den das Eigenschaftensystem allen Anfragen der Eigenschaft zuweist.
  • Eine statische Rückrufmethode, die automatisch innerhalb des Eigenschaftensystems aufgerufen wird, sobald ein Eigenschaftswert erkannt wird.

Aufrufen eines Registers mit Eigenschaftenmetadaten

Im vorangehenden Beispiel wurde beim Aufrufen von DependencyProperty.Register ein Nullwert für den propertyMetadata-Parameter übergeben. Damit ein Standardwert von einer Abhängigkeitseigenschaft bereitgestellt oder oder ein Rückruf mit veränderter Eigenschaft verwendet werden kann, müssen Sie eine Instanz von PropertyMetadatadefinieren, die mindestens eine dieser Funktionen bereitstellt.

In aller Regel wird dazu innerhalb der Parameter von DependencyProperty.Register ein PropertyMetadata-Element in Form einer inline erstellten Instanz bereitgestellt.

Hinweis  Wenn Sie eine Implementierung von CreateDefaultValueCallback verwenden, müssen Sie die Hilfsmethode PropertyMetadata.Create aufrufen. Das Aufrufen eines PropertyMetadata-Konstruktors zum Definieren der PropertyMetadata-Instanz ist nicht zulässig.

Im nächsten Beispiel werden die vorangehenden DependencyProperty.Register-Beispiele geändert, indem auf eine Instanz von PropertyMetadata mit einem PropertyChangedCallback-Wert verwiesen wird. Die Implementierung des "OnLabelChanged"-Rückrufs wird im weiteren Verlauf dieses Abschnitts behandelt.


public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null,new PropertyChangedCallback(OnLabelChanged))
);

Standardwert

Sie können einen Standardwert für eine Abhängigkeitseigenschaft festlegen, damit diese immer einen bestimmten Standardwert zurückgibt, wenn ihre Festlegung aufgehoben wird. Dieser Wert kann vom vererbten Standardwert für den Typ dieser Eigenschaft abweichen.

Wenn kein Standardwert festgelegt ist, ist der Standardwert einer Abhängigkeitseigenschaft für einen Verweistyp null oder entspricht der Standardeinstellung des Werttyps oder des Sprachengrundtyps (zum Beispiel 0 für einen Integer oder eine leere Zeichenfolge für eine Zeichenfolge). Ein Standardwert wird vor allem erstellt, damit sein Wert wiederhergestellt wird, wenn Sie ClearValue in der Eigenschaft aufrufen. Das Erstellen eines Standardwerts pro Eigenschaft kann vorteilhafter als das Erstellen von Standardwerten in Konstruktoren, insbesondere für Werttypen, sein. Achten Sie jedoch bei den Verweistypen darauf, dass beim Erstellen eines Standardwerts kein unbeabsichtigtes Singleton-Muster entsteht. Weitere Informationen finden Sie weiter unten in diesem Thema unter Bewährte Methoden.

Hinweis  Führen Sie keine Registrierung bei einem Standardwert von UnsetValue durch. Diese Handlung würde Benutzer von Eigenschaften verwirren und unbeabsichtigte Konsequenzen innerhalb des Eigenschaftensystems haben.

CreateDefaultValueCallback

In einigen Szenarien werden Abhängigkeitseigenschaften für Objekte definiert, die in mehreren UI-Threads verwendet werden. Dies ist beispielsweise der Fall, wenn ein Datenobjekt oder ein Steuerelement für mehrere Apps definiert wird. Sie können den Austausch des Objekts zwischen verschiedenen UI-Threads ermöglichen, indem Sie anstelle des Standardwerts eine CreateDefaultValueCallback-Implementierung bereitstellen, die an den Thread gebunden ist, von dem die Eigenschaft registriert wurde. Im Grunde genommen definiert ein CreateDefaultValueCallback eine Factory für Standardwerte. Der Wert, der von CreateDefaultValueCallback zurückgegeben wird, ist immer mit dem aktuellen UI-Thread von CreateDefaultValueCallback verbunden, von dem das Objekt verwendet wird.

Um Metadaten zu definieren, die einen CreateDefaultValueCallback angeben, müssen Sie PropertyMetadata.Create aufrufen. Dadurch wird eine Metadateninstanz zurückgegeben. Die PropertyMetadata-Konstruktoren weisen keine Signatur mit dem CreateDefaultValueCallback-Parameter auf.

Bei der typischen Implementierung eines CreateDefaultValueCallback wird eine neue DependencyObject-Klasse erstellt. Die spezifischen Werte der einzelnen Eigenschaften für das DependencyObject werden auf die gewünschten Standardwerte festgelegt, und die neue Klasse wird mithilfe des zurückgegebenen Werts der CreateDefaultValueCallback-Methode als Object-Verweis zurückgegeben.

PropertyChanged-Rückrufmethode

Sie können eine PropertyChanged-Rückrufmethode definieren, um die Interaktionen Ihrer Eigenschaft mit anderen Abhängigkeitseigenschaften festzulegen oder um eine interne Eigenschaft oder einen Status Ihres Objekts einzurichten, wenn die Eigenschaft geändert wird. Sobald Ihr Rückruf aufgerufen wurde, hat das Eigenschaftensystem festgestellt, dass es eine aktuelle Änderung des Eigenschaftswerts gibt. Der d-Parameter des Rückrufs ist aufgrund der statischen Rückrufmethode wichtig, da dieser berichtet, welche Instanz der Klasse eine Änderung berichtet hat. Bei einer gewöhnlichen Implementierung wird die NewValue-Eigenschaft des Ereignisdatums verwendet und der Wert auf bestimmte Weise verarbeitet – für gewöhnlich durch eine andere Änderung am als d erkannten Objekt. Weitere Reaktionen auf eine Eigenschaftenänderung ist die Ablehnung des von NewValue berichteten Werts, die Wiederherstellung von OldValue oder die Festlegung des Werts auf eine programmierbare Einschränkung, die für NewValue gilt.

Das nächste Beispiel zeigt eine PropertyChangedCallback-Implementierung. Hier wird die Methode implementiert, auf die in den vorigen Register-Beispielen, als Teil des Konstruktionsarguments für die PropertyMetadata Bezug genommen wurde. Das von diesem Rückruf adressierte Szenario ist, dass die Klasse auch über eine berechnete, schreibgeschützte Eigenschaft "HasLabelValue" (Implementierung nicht gezeigt) verfügt. Diese Rückrufmethode wird aufgerufen, wenn die "Label"-Eigenschaft neu ausgewertet wird. Der Rückruf ermöglicht, dass der abhängige, berechnete Wert mit den Änderungen der Abhängigkeitseigenschaft synchronisiert bleibt.


private static void OnLabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    ImageWithLabelControl iwlc = d as ImageWithLabelControl; //null checks omitted
    String s = e.NewValue as String; //null checks omitted
    if (s == String.Empty)
    {
        iwlc.HasLabelValue = false;
    } else {
        iwlc.HasLabelValue = true;
    }
}

Verhalten mit geänderter Eigenschaft bei Strukturen und Enumerationen

Wenn der Typ einer DependencyProperty eine Enumeration oder Struktur ist, kann der Rückruf auch dann erfolgen, wenn die zugehörigen internen Werte nicht geändert wurden. Dieses Verhalten weicht gegenüber einem Systemgrundtyp wie einer Zeichenfolge ab. Hier erfolgt der Aufruf nur bei einem geänderten Wert. Dies ist der Nebeneffekt eines internen Boxing- und Unboxing-Vorgangs für entsprechende Werte. Wenn Sie über eine PropertyChangedCallback-Methode für eine Eigenschaft verfügen und der Wert eine Enumeration oder Struktur darstellt, müssen Sie OldValue und NewValue vergleichen. Dazu wandeln Sie die Werte selbst um und verwenden die überladenen Vergleichsoperatoren, die für die nun umgewandelten Werte verfügbar sind. Wenn kein entsprechender Operator verfügbar ist (beispielsweise weil es sich um eine benutzerdefinierte Struktur handelt), müssen Sie möglicherweise die einzelnen Werte vergleichen. Wenn Sie zu dem Ergebnis kommen, dass sich die Werte nicht geändert haben, ist normalerweise keine Aktion erforderlich.



private static void OnVisibilityValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    if ((Visibility)e.NewValue != (Visibility)e.OldValue)
    {
        //value really changed, invoke your changed logic here
    } // else this was invoked because of boxing, do nothing
}

Bewährte Methoden

Behalten Sie die folgenden Überlegungen als bewährte Methoden im Gedächtnis, wenn Sie Ihre benutzerdefinierte Abhängigkeitseigenschaft festlegen.

DependencyObject und Threading

Alle DependencyObject-Instanzen müssen in dem UI-Thread erstellt werden, der mit dem aktuellen Window verknüpft ist, das von einer Windows-Runtime-App angezeigt wird. Obwohl jedes DependencyObject im zentralen UI-Thread erstellt werden muss, kann durch Aufrufen von Dispatcher mithilfe eines Verteilerverweises von anderen Threads aus auf die Objekte zugegriffen werden.

Die Bedeutung der Threadingmerkmale von DependencyObject geht darauf zurück, dass der Wert einer Abhängigkeitseigenschaft im Allgemeinen nur von Code geändert werden kann, der in dem UI-Thread ausgeführt wird. Threadingprobleme können in normalem UI-Code in der Regel vermieden werden, wenn async-Muster und -Hintergrundworkerthreads richtig eingesetzt werden. Threadingprobleme mit DependencyObject entstehen für gewöhnlich nur beim Definieren eigener DependencyObject-Typen, wenn versucht wird, diese für Datenquellen oder andere Szenarien zu verwenden, für die ein DependencyObject nicht unbedingt geeignet ist.

Unbeabsichtigte Singletons vermeiden

Ein unbeabsichtigter Singleton kann auftreten, wenn eine Abhängigkeitseigenschaft deklariert wird, die einen Verweistyp annimmt, und Sie einen Konstruktor für diesen Verweistyp als Teil des Codes, der PropertyMetadata erstellt, aufrufen. Jegliche Anwendungen der Abhängigkeitseigenschaft teilen nur eine Instanz an PropertyMetadata und versuchen so, den einen von Ihnen erstellten Verweistyp gemeinsam zu nutzen. Alle untergeordneten Eigenschaften dieses Werttyps, den Sie für Ihre Abhängigkeitseigenschaft festgelegt haben , verteilen sich anschließend auf eine von Ihnen unbeabsichtigte Weise auf andere Objekte.

Sie können Klassenkonstruktoren verwenden, um erste Werte für eine Abhängigkeitseigenschaft vom Typ "Verweis" festzulegen, wenn Sie einen Nicht-null-Wert haben möchten. Beachten Sie jedoch, dass dies als lokaler Wert für die Rangfolge von Abhängigkeitseigenschaften betrachtet werden würde. Es ist unter Umständen angemessener, eine Vorlage – falls diese von Ihrer Klasse unterstützt wird – für diesen Zweck zu verwenden. Eine andere Möglichkeit, um Singleton-Muster zu vermeiden, aber dennoch einen nützlichen Standardwert bereitzustellen, besteht darin, eine statische Eigenschaft für den Verweistyp, der einen passenden Standardwert für die Werte dieser Klasse liefert, verfügbar zu machen.

Abhängigkeitseigenschaften vom Typ "Auflistung"

Bei Abhängigkeitseigenschaften vom Typ "Auflistung" sind einige zusätzliche Implementierungsaspekte zu beachten.

Abhängigkeitseigenschaften vom Typ "Auflistung" sind relativ selten in der Windows-Runtime-API. In den meisten Fällen können Sie Auflistungen verwenden, bei denen die Elemente DependencyObject-Unterklassen sind. Die Auflistungseigenschaft selbst kann jedoch als eine herkömmliche CLR- oder eine C++-Eigenschaft implementiert werden. Das ist darauf zurückzuführen, dass sich Auflistungen nicht unbedingt für herkömmliche Szenarien eignen, bei denen Abhängigkeitseigenschaften involviert sind. Beispiel:

  • Sie animieren für gewöhnlich keine Auflistung.
  • Sie füllen die Elemente einer Auflistung für gewöhnlich nicht vorher mit Stilen oder einer Vorlage aus.
  • Obwohl das Binden an Auflistungen ein wichtiges Szenario ist, muss die Auflistung keine Abhängigkeitseigenschaft sein, um eine Bindungsquelle darzustellen. Bei Bindungszielen werden für gewöhnlich Unterklassen von ItemsControl oder DataTemplate verwendet, um die Auflistungselemente zu unterstützen oder die Modellanzeigemuster zu verwenden. Weitere Informationen zur Bindung zu und von Auflistungen finden Sie unter Datenbindung mit XAML.
  • Benachrichtigungen zu Auflistungsänderungen können in Benutzeroberflächen wie INotifyPropertyChanged oder INotifyCollectionChanged oder durch die Ableitung des Auflistungstyps von ObservableCollection besser adressiert werden.

Dennoch existieren Szenarien für Abhängigkeitseigenschaften vom Typ "Auflistung". In den nächsten drei Abschnitten finden Sie Informationen zur Implementierung einer Abhängigkeitseigenschaft vom Typ "Auflistung".

Initialisieren der Auflistung

Wenn Sie eine Abhängigkeitseigenschaft erstellen, geben Sie den Standardwert über die Metadaten für die Abhängigkeitseigenschaft an. Achten Sie darauf, keine einzelne statische Auflistung als Standardwert zu verwenden. Stattdessen müssen Sie sicherstellen, dass Sie den Auflistungswert bewusst auf eine eindeutige (Instanz-)Auflistung im Rahmen der Klassenkonstruktorlogik für die Besitzerklasse der Auflistungseigenschaft festlegen.

Änderungsbenachrichtigungen

Eine Auflistung als eine Abhängigkeitseigenschaft zu definieren, meldet nicht automatisch Änderungen an Elementen der Auflistung durch das Eigenschaftensystem, das die Rückrufmethode "PropertyChanged" aufruft. Wenn Sie Benachrichtigungen für Auflistungen und Auflistungselemente haben möchten – zum Beispiel für ein Datenbindungsszenario – müssen Sie die INotifyPropertyChanged- oder INotifyCollectionChanged-Benutzeroberfläche implementieren. Weitere Informationen finden Sie unter Datenbindung mit XAML.

Sicherheitsüberlegungen für Abhängigkeitseigenschaften

Abhängigkeitseigenschaften sollten als öffentliche Eigenschaften deklariert werden. Bezeichner von Abhängigkeitseigenschaften sollten als öffentliche schreibgeschützte statische Member deklariert werden. Auch wenn Sie versuchen, andere durch eine Sprache zugelassene Zugriffsebenen (wie z. B. protected) zu deklarieren, können Sie auf eine Abhängigkeitseigenschaft immer zugreifen, indem Sie den Bezeichner in Verbindung mit den APIs des Eigenschaftensystems verwenden. Das Deklarieren des Bezeichners der Abhängigkeitseigenschaft als intern oder privat wird nicht funktionieren, da das Eigenschaftensystem in diesem Fall nicht ordnungsgemäß arbeitet.

Wrappereigenschaften sind nur als Vereinfachung gedacht. Die Sicherheitsmechanismen, die für die Wrapper angewendet werden, können durch das Aufrufen von GetValue oder SetValue umgangen werden. Wrapper-Eigenschaften sollten daher öffentlich bleiben. Andernfalls machen Sie die Nutzung Ihrer Eigenschaft für zulässige Besucher einfach nur schwieriger, ohne einen tatsächlichen Vorteil bei der Sicherheit zu haben.

Die Windows-Runtime bietet keine Möglichkeit, eine benutzerdefinierte Abhängigkeitseigenschaft als schreibgeschützt zu registrieren.

Abhängigkeitseigenschaften und Klassenkonstruktoren

Hier gilt der allgemeine Grundsatz, dass Klassenkonstruktoren keine virtuellen Methoden aufrufen sollten. Der Grund ist, dass Konstruktoren als Basisinitialisierung eines abgeleiteten Klassenkonstruktors aufgerufen werden können, und das Eingeben der virtuellen Methode über den Konstruktor könnte zu einem Zeitpunkt erfolgen, zu dem das erstellte Objekt einen unvollständigen Initialisierungszustand aufweist. Wenn Sie eine Ableitung von einer Klasse durchführen, die bereits von DependencyObject abgeleitet ist, sollten Sie bedenken, dass das Eigenschaftensystem intern selbst virtuelle Methoden aufruft und offenlegt. Um mögliche Probleme mit der Laufzeitinitialisierung zu vermeiden, sollten Sie in Konstruktoren von Klassen keine Werte von Abhängigkeitseigenschaften festlegen.

Registrieren der Abhängigkeitseigenschaften für C++/CX-Apps

Die Implementierung für das Registrieren einer Eigenschaft in C++/CX ist schwieriger als für C#C#, zum einen aufgrund der Aufteilung in Kopfzeile und Implementierungsdatei und zum anderen, weil die Initialisierung im Stammbereich der Implementierungsdatei nicht empfohlen wird. (Visual C++-Komponentenerweiterungen (C++/CX) legen statischen Initialisierungscode aus dem Stammbereich direkt in DllMain, während C#-Compiler die statischen Initialisierer Klassen zuweisen und so DllMain-Ladeprobleme vermeiden). Die beste Methode ist das Deklarieren einer Hilfsfunktion, die die gesamte Registrierung von Abhängigkeitseigenschaften für eine Klasse durchführt, d. h. eine Funktion pro Klasse. Für jede benutzerdefinierte Klasse, die Ihre App verwendet, müssen Sie dann auf die Hilfsregistrierungsfunktion verweisen, die von jeder benutzerdefinierten Klasse verfügbar gemacht wird, die Sie verwenden möchten. Rufen Sie jede Hilfsregistrierungsfunktion einmal als Teil der Application constructor (App::App()) vor InitializeComponent auf. Dieser Konstruktor wird nur ausgefüllt, wenn wirklich zum ersten Mal auf die App verwiesen wird; er wird nicht erneut ausgefüllt, wenn z. B. eine angehaltene App fortgesetzt wird. Wie im vorhergehenden C++-Registrierungsbeispiel gezeigt, ist auch nullptr-Überprüfung jedes Register-Aufrufs wichtig, da dadurch sichergestellt wird, dass kein Aufrufer der Funktion die Eigenschaft zweimal registrieren kann. Bei einem zweiten Registrierungsaufruf würde Ihre App ohne eine solche Überprüfung wahrscheinlich abstürzen, da der Eigenschaftsname doppelt vorhanden wäre. Dieses Implementierungsmuster können Sie im XAML-Beispiel für Benutzer und benutzerdefinierte Steuerelemente im Code für die C++/CX-Version des Beispiels sehen.

Verwandte Themen

DependencyObject
DependencyProperty.Register
Übersicht über Abhängigkeitseigenschaften
XAML-Benutzer und benutzerdefinierte Steuerelemente

 

 

Anzeigen:
© 2014 Microsoft