Benutzerdefinierte angefügte Eigenschaften

Applies to Windows and Windows Phone

Eine angefügte Eigenschaft ist ein XAML-Konzept. Angefügte Eigenschaften werden in der Regel als eine spezialisierte Form der Abhängigkeitseigenschaft definiert. In diesem Thema wird erläutert, wie eine angefügte Eigenschaft als Abhängigkeitseigenschaft implementiert und die Accessorkonvention definiert wird, die erforderlich ist, damit die angefügte Eigenschaft in XAML verwendet werden kann.

Voraussetzungen

Wir gehen davon aus, dass Sie die Abhängigkeitseigenschaften aus der Perspektive eines Consumers vorhandener Abhängigkeitseigenschaften sehen und dass Sie die Übersicht über Abhängigkeitseigenschaften gelesen haben. Sie sollten auch die Übersicht über angefügte Eigenschaften gelesen haben. 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.

Szenarien für angefügte Eigenschaften

Möglicherweise möchten Sie eine angefügte Eigenschaft erstellen, wenn es einen Grund für das Vorhandensein eines Mechanismus zum Festlegen von Eigenschaften für Klassen außer der definierenden Klasse gibt. Die häufigsten Szenarien dafür sind Layout- und Dienstunterstützung. Beispiele für vorhandene Layouteigenschaften sind Canvas.ZIndex und Canvas.Top. In einem Layoutszenario können Elemente, die als untergeordnete Elemente für layoutsteuernde Elemente fungieren, einzeln Layoutanforderungen gegenüber ihren übergeordneten Elementen ausdrücken. Dabei legt jedes Element einen Eigenschaftswert fest, den das übergeordnete Element als eine angefügte Eigenschaft definiert. Ein Beispiel für das Dienstunterstützungsszenario in der Windows-Runtime-API ist das Festlegen der angefügten Eigenschaften von ScrollViewer wie ScrollViewer.IsZoomChainingEnabled.

Achtung  Eine Beschränkung der XAML-Implementierung der Windows Runtime: Sie können keine benutzerdefinierten angefügten Eigenschaften animieren.

Registrieren einer benutzerdefinierten angefügten Eigenschaft

Wenn Sie die angefügte Eigenschaft immer für die Verwendung mit anderen Typen definieren, muss die Klasse, in der die Eigenschaft registriert ist, nicht aus DependencyObject abgeleitet werden. Aber die Zielparameter für Accessoren müssen DependencyObject verwenden, wenn Sie das typische Modell befolgen, bei dem Ihre angefügte Eigenschaft auch eine Abhängigkeitseigenschaft sein kann, damit Sie den Sicherungseigenschaftsspeicher verwenden können.

Definieren Sie Ihre angefügte Eigenschaft als Abhängigkeitseigenschaft, indem Sie eine public static readonly-Eigenschaft vom Typ DependencyProperty deklarieren. Sie definieren diese Eigenschaft mithilfe des Rückgabewerts der RegisterAttached-Methode. Der Eigenschaftenname muss mit dem Namen der angefügten Eigenschaft übereinstimmen, die Sie als RegisterAttached name-Parameter angeben, und am Ende muss die Zeichenfolge "Property" hinzugefügt sein. Dies ist die bestehende Konvention zum Benennen der Bezeichner von Abhängigkeitseigenschaften im Verhältnis mit den Eigenschaften, die sie darstellen.

Der Hauptunterschied beim Definieren einer benutzerdefinierten Eigenschaft und beim Definieren einer benutzerdefinierten Abhängigkeitseigenschaft besteht darin, wie Sie die Accessoren oder Wrapper definieren. Anstatt die unter Benutzerdefinierte Abhängigkeitseigenschaften beschriebene Wrappertechnik zu verwenden, müssen Sie auch statische GetPropertyName- und SetPropertyName-Methoden als Accessoren für die angefügte Eigenschaft bereitstellen. Die Accessoren werden hauptsächlich vom XAML-Parser verwendet, obwohl sie auch von anderen aufrufenden Elementen verwendet werden können, um Werte in Nicht-XAML-Szenarien festzulegen.

Wichtig  Wenn Sie die Accessoren nicht ordnungsgemäß definieren, kann der XAML-Prozessor nicht auf Ihre angefügte Eigenschaft zugreifen, und Benutzer, die sie verwenden möchten, erhalten möglicherweise einen XAML-Parserfehler. Außerdem erfordern Entwurfs- und Codiertools die "*Property"-Konventionen zum Benennen von Bezeichnern, wenn sie eine benutzerdefinierte Abhängigkeitseigenschaft in einer referenzierten Assembly feststellen.

Accessoren

Die Signatur für den GetPropertyName-Accessor lautet folgendermaßen.

public static valueType GetPropertyName (DependencyObject target)

Bei Microsoft Visual Basic lautet sie folgendermaßen.

Public Shared Function GetPropertyName(ByVal target As DependencyObject) As valueType)

Das target-Objekt kann in Ihrer Implementierung einen spezielleren Typ aufweisen, aber es muss von DependencyObject abgeleitet sein. Der valueType-Rückgabewert kann in Ihrer Implementierung auch einen spezielleren Typ aufweisen. Der grundlegende Object-Typ wird akzeptiert, aber oft möchten Sie jedoch, dass Ihre angefügte Eigenschaft eine Typsicherheit erzwingt. Die Verwendung der Typisierung in den Getter- und Setter-Signaturen ist eine empfohlene Typsicherheitstechnik.

Die Signatur für den SetPropertyName-Accessor lautet folgendermaßen.

public static void SetPropertyName (DependencyObject target , valueType value)

Bei Microsoft Visual Basic lautet sie folgendermaßen.

Public Shared Sub SetPropertyName (ByVal target As DependencyObject, ByVal value As valueType)

Das target-Objekt kann in Ihrer Implementierung einen spezielleren Typ aufweisen, aber es muss von DependencyObject abgeleitet sein. Das value-Objekt und sein valueType können in Ihrer Implementierung einen spezielleren Typ aufweisen. Beachten Sie, dass der Wert für diese Methode die Eingabe ist, die vom XAML-Prozessor stammt, wenn er Ihre angefügte Eigenschaft im Markup feststellt. Die Typkonvertierung oder vorhandene Markuperweiterung muss für den von Ihnen verwendeten Typ unterstützt werden, damit der entsprechende Typ auf der Grundlage eines Attributwerts (der letztlich nur eine Zeichenfolge ist) erstellt werden kann. Der grundlegende Object-Typ ist zwar akzeptabel, häufig möchten Sie jedoch zusätzliche Typsicherheit erreichen. Versehen Sie hierzu die Accessoren mit einer Typerzwingung.

Hinweis  Es ist auch möglich, über die Eigenschaftselementsyntax eine angefügte Eigenschaft zu definieren, die den beabsichtigten Zweck angibt. In diesem Fall benötigen Sie keine Typkonvertierung für die Werte. Sie müssen allerdings sicherstellen, dass die beabsichtigten Werte in XAML konstruierbar sind. VisualStateManager.VisualStateGroups ist ein Beispiel für eine vorhandene angefügte Eigenschaft, die nur die Eigenschaftselementverwendung unterstützt.

Codebeispiel

In diesem Beispiel sind die Abhängigkeitseigenschaftsregistrierung (mithilfe der RegisterAttached-Methode) sowie die Accessoren Get und Set für eine benutzerdefinierte angefügte Eigenschaft dargestellt. Im Beispiel lautet der Name der angefügten Eigenschaft IsMovable. Daher müssen die Accessoren die Namen GetIsMovable und SetIsMovable aufweisen. Der Besitzer der angefügten Eigenschaft ist eine Dienstklasse mit dem Namen GameService, die nicht über eine eigene UI verfügt; sie dient lediglich zur Bereitstellung der Dienste der angefügten Eigenschaft, wenn die angefügte Eigenschaft GameService.IsMovable verwendet wird.


    public class GameService : DependencyObject
    {
        public static readonly DependencyProperty IsMovableProperty = 
        DependencyProperty.RegisterAttached(
          "IsMovable",
          typeof(Boolean),
          typeof(GameService),
          new PropertyMetadata(false)
        );
        public static void SetIsMovable(UIElement element, Boolean value)
        {
            element.SetValue(IsMovableProperty, value);
        }
        public static Boolean GetIsMovable(UIElement element)
        {
            return (Boolean)element.GetValue(IsMovableProperty);
        }
    }

Das Definieren der angefügten Eigenschaft in C++ ist etwas komplexer. Sie müssen entscheiden, wie die Faktorverteilung zwischen der Header- und Codedatei lauten soll. Außerdem sollten Sie den Bezeichner als Eigenschaft mit nur einem get-Accessor verfügbar machen. Die Gründe dafür sind unter Benutzerdefinierte Abhängigkeitseigenschaften erläutert. In C++ müssen Sie diese Eigenschaft-Feld-Beziehung explizit definieren, anstatt die readonly-Schlüsselwörter und die explizite Sicherung einfacher Eigenschaften in .NET zu verwenden. Sie müssen auch die Registrierung der angefügten Eigenschaft innerhalb einer Hilfsfunktion durchführen, die nur einmal ausgeführt wird; dies erfolgt beim ersten Start der App, aber bevor XAML-Seiten geladen werden, die die angefügte Eigenschaft benötigen. Normalerweise werden Ihre Hilfsfunktionen für die Registrierung von Eigenschaften für alle Abhängigkeits- oder angefügten Eigenschaften innerhalb des App/Application-Konstruktors im Code für Ihre app.xaml-Datei aufgerufen.


//
// GameService.h
// Declaration of the GameService class.
//

#pragma once

#include "pch.h"
//namespace WUX = Windows::UI::Xaml;

namespace UserAndCustomControls {
    public ref class GameService sealed : public WUX::DependencyObject {
    private:
        static WUX::DependencyProperty^ _IsMovableProperty;
    public:
        GameService::GameService();
        void GameService::RegisterDependencyProperties();
        static property WUX::DependencyProperty^ IsMovableProperty
        {
            WUX::DependencyProperty^ get() {
                return _IsMovableProperty;
            }
        };
        static bool GameService::GetIsMovable(WUX::UIElement^ element) {
            return (bool)element->GetValue(_IsMovableProperty);
        };
        static void GameService::SetIsMovable(WUX::UIElement^ element, bool value) {
            element->SetValue(_IsMovableProperty,value);
        }
    };
}


//
// GameService.cpp
// Implementation of the GameService class.
//

#include "pch.h"
#include "GameService.h"

using namespace UserAndCustomControls;

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Documents;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Media;

GameService::GameService() {

};



GameService::RegisterDependencyProperties() {
    DependencyProperty^ GameService::_IsMovableProperty = DependencyProperty::RegisterAttached(
	     "IsMovable", Platform::Boolean::typeid, GameService::typeid, ref new PropertyMetadata(false));
}

Verwenden der angefügten Eigenschaft in XAML

Nachdem Sie die angefügte Eigenschaft definiert und ihre unterstützenden Elemente als Teil des benutzerdefinierten Typs eingefügt haben, müssen Sie die Definitionen dann für die Verwendung von XAML verfügbar machen. Dazu müssen Sie einen XAML-Namespace zuordnen, der auf den Codenamespace mit der relevanten Klasse verweist. In Fällen, in denen Sie die angefügte Eigenschaft als Teil einer Bibliothek definiert haben, müssen Sie diese Bibliothek als Teil des App-Pakets für die App einfügen.

Eine XML-Namespacezuordnung wird in der Regel im Stammelement einer XAML-Seite platziert. Für die Klasse mit dem Namen GameService im Namespace UserAndCustomControls, der die in den vorherigen Ausschnitten dargestellten Definitionen der angefügten Eigenschaften enthält, sieht die Zuordnung z. B. folgendermaßen aus.


<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:uc="using:UserAndCustomControls"
...
>

Mithilfe der Zuordnung können Sie die angefügte GameService.IsMovable-Eigenschaft für beliebige Elemente festlegen, die Ihrer Zieldefinition entsprechen. Dies schließt auch einen vorhandenen von Windows-Runtime definierten Typ mit ein.


<Image uc:GameService.IsMovable="true" .../>

Wenn Sie die Eigenschaft für ein Element festlegen, das sich auch im selben zugeordneten XML-Namespace befindet, müssen Sie das Präfix dennoch in den Namen der angefügten Eigenschaft einfügen. Das liegt daran, dass das Präfix den Besitzertyp qualifiziert. Es kann nicht angenommen werden, dass sich das Attribut der angefügten Eigenschaft innerhalb desselben XML-Namespace wie das Element befindet, in dem das Attribut enthalten ist, auch wenn Attribute gemäß normalen XML-Regeln Namespaces von Elementen erben können. Wenn Sie beispielsweise GameService.IsMovable für einen benutzerdefinierten Typ ImageWithLabelControl (Definition nicht dargestellt) festlegen, und selbst wenn beide im selben Codenamespace definiert sind, der demselben Präfix zugeordnet ist, sieht XAML-Element dennoch folgendermaßen aus.



<uc:ImageWithLabelControl uc:GameService.IsMovable="true" .../>

Hinweis  Wenn Sie eine XAML-Benutzeroberfläche mit C++ schreiben, müssen Sie den Header für den benutzerdefinierten Typ, der die angefügte Eigenschaft definiert, jedes Mal einfügen, sobald eine XAML-Seite diesen Typ verwendet. Jede XAML-Seite hat einen zugeordneten .xaml.h-CodeBehind-Header. Dort sollten Sie (mithilfe von #include) den Header für die Definition des Besitzertyps der angefügten Eigenschaft einfügen.

Werttyp einer benutzerdefinierten angefügten Eigenschaft

Der Typ, der als Werttyp einer benutzerdefinierten angefügten Eigenschaft verwendet wird, wirkt sich auf die Nutzung, die Definition oder auf beides aus. Der Werttyp der angefügten Eigenschaft wird an verschiedenen Stellen deklariert: in den Signaturen der Get- und Set-Accessormethoden und auch im propertyType-Parameter des RegisterAttached-Aufrufs.

Der häufigste Werttyp für angefügte Eigenschaften (benutzerdefinierte oder andere) ist eine einfache Zeichenfolge. Das liegt daran, dass angefügte Eigenschaften in der Regel für die Nutzung mit XAML-Attributen vorgesehen sind, und durch die Verwendung einer Zeichenfolge als Werttyp bleiben die Eigenschaften einfach. Andere Grundtypen, die über eine systemeigene Konvertierung in Zeichenfolgenmethoden verfügen (z. B. eine ganze Zahl, ein Double oder ein Enumerationswert) werden auch häufig als Werttypen für angefügte Eigenschaften verwendet. Sie können andere Werttypen – solche, die die systemeigene Zeichenfolgekonvertierung nicht unterstützen – als Wert der angefügten Eigenschaft verwenden. Dies hat jedoch zur Folge, dass eine Auswahl in Bezug auf die Nutzung oder Implementierung getroffen werden muss:

  • Sie können die angefügte Eigenschaft so belassen, wie sie ist. Dann kann die angefügte Eigenschaft die Nutzung nur unterstützen, wenn die angefügte Eigenschaft ein Eigenschaftselement darstellt und der Wert als Objektelement deklariert wurde. In diesem Fall muss der Eigenschaftstyp die XAML-Nutzung als Objektelement unterstützen. Überprüfen Sie bei vorhandenen Windows-Runtime-Referenzklassen die XAML-Syntax, um sicherzustellen, dass der Typ die XAML-Objektelementnutzung unterstützt.
  • Sie können die angefügte Eigenschaft so belassen, wie sie ist. Dann können Sie sie aber nur in einer Attributnutzung über eine XAML-Referenztechnik wie Binding oder StaticResource verwenden, die als Zeichenfolge ausgedrückt werden kann.

Weitere Infos zum Canvas.Left-Beispiel

In vorherigen Beispielen für die Nutzung angefügter Eigenschaften wurden verschiedene Methoden zum Festlegen der angefügten Eigenschaft Canvas.Left gezeigt. Doch wie wirkt sich das auf die Interaktion einer Canvas mit Ihrem Objekt aus, und wann tritt diese Auswirkung auf? Wir schauen uns dieses bestimmte Beispiel genauer an. Es ist beim Implementieren einer angefügten Eigenschaft nämlich interessant zu wissen, wozu eine typische Besitzerklasse der angefügten Eigenschaft die Werte der angefügten Eigenschaft noch verwendet, wenn sie sie in anderen Objekten findet.

Canvas dient hauptsächlich dazu, einen Layoutcontainer mit absoluter Position auf der UI darzustellen Die untergeordneten Elemente einer Canvas werden in der definierten Basisklasseneigenschaft Children gespeichert. Canvas verwendet als einziges Panel die absolute Positionierung. Das Hinzufügen von Eigenschaften, die nur für Canvas und die speziellen UIElement-Fälle von Bedeutung sind, wenn untergeordnete Elemente eines UIElement-Elements vorhanden sind, hätte das Objektmodell des allgemeinen UIElement-Typs aufgebläht. Wenn Sie die Eigenschaften von Layoutsteuerelementen einer Canvas als angefügte Eigenschaften definieren, die von allen UIElement-Elementen verwendet werden können, ist das Objektmodell übersichtlicher.

Die Canvas soll ein praktisches Panel sein und weist daher ein Verhalten auf, das die Methoden Measure und Arrange auf Frameworkebene außer Kraft setzt. Hier wird von Canvas nach Werten der angefügten Eigenschaften für ihre untergeordneten Elemente gesucht. Ein Teil der Muster Measure und Arrange ist eine Schleife, die alle Inhalte durchläuft. Ein Panel besitzt die Children-Eigenschaft, die es explizit zu einem als untergeordnetes Element betrachtetes Objekt macht. Das Canvas-Layoutverhalten durchläuft also diese untergeordneten Elemente und führt statische Canvas.GetLeft- und Canvas.GetTop-Aufrufe für alle untergeordneten Elemente auf, um zu ermitteln, ob diese angefügten Eigenschaften einen nicht standardmäßigen Wert (Standard = 0) enthalten. Mithilfe dieser Werte werden die untergeordneten Elemente auf der verfügbaren Canvas-Layoutfläche gemäß den von den einzelnen untergeordneten Elementen bereitgestellten spezifischen Werten absolut positioniert. Anschließend wird mithilfe von Arrange ein Commit für sie ausgeführt.

Der Code ähnelt dem folgenden Pseudocode:


    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement child in Children)
        {
            double x = (double) Canvas.GetLeft(child);
            double y = (double) Canvas.GetTop(child);
            child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
        }
        return base.ArrangeOverride(finalSize); 
        // real Canvas has more sophisticated sizing
    }

Hinweis  Weitere Informationen zur Funktionsweise von Bereichen finden Sie unter Übersicht über benutzerdefinierte Bereiche.

Verwandte Themen

RegisterAttached
Übersicht über angefügte Eigenschaften
Benutzerdefinierte Abhängigkeitseigenschaften
Übersicht über XAML

 

 

Anzeigen:
© 2015 Microsoft