Vlastní vlastnosti závislostí (WPF .NET)

Vývojáři aplikací WPF (Windows Presentation Foundation) a autoři komponent mohou vytvářet vlastní vlastnosti závislostí, aby rozšířili funkčnost svých vlastností. Na rozdíl od vlastnosti CLR (Common Language Runtime) přidává vlastnost závislosti podporu stylu, vazby dat, dědičnosti, animací a výchozích hodnot. Background, Widtha Text jsou příklady existujících vlastností závislostí ve třídách WPF. Tento článek popisuje, jak implementovat vlastní vlastnosti závislostí a představuje možnosti pro zlepšení výkonu, použitelnosti a všestrannosti.

Důležité

Dokumentace k desktopové příručce pro .NET 7 a .NET 6 se právě připravuje.

Předpoklady

V článku se předpokládá základní znalost vlastností závislostí a že jste si přečetli přehled vlastností závislostí. Pokud chcete postupovat podle příkladů v tomto článku, pomůže vám to, pokud znáte jazyk XAML (Extensible Application Markup Language) a víte, jak psát aplikace WPF.

Identifikátor vlastnosti závislosti

Vlastnosti závislostí jsou vlastnosti, které jsou registrovány v systému vlastností WPF prostřednictvím Register nebo RegisterReadOnly volání. Metoda Register vrátí DependencyProperty instanci, která obsahuje registrovaný název a vlastnosti vlastnosti závislosti. Instanci přiřadíte DependencyProperty statickému poli jen pro čtení, kterému se říká identifikátor vlastnosti závislosti, který je podle konvence pojmenován <property name>Property. Například pole identifikátoru Background vlastnosti je vždy BackgroundProperty.

Identifikátor vlastnosti závislosti se používá jako záložní pole pro získání nebo nastavení hodnot vlastností místo standardního vzoru zálohování vlastnosti s privátním polem. Nejen že systém vlastností používá identifikátor, procesory XAML ho mohou používat a váš kód (a případně externí kód) může přistupovat k vlastnostem závislostí prostřednictvím jejich identifikátorů.

Vlastnosti závislosti lze použít pouze u tříd odvozených z DependencyObject typů. Většina tříd WPF podporuje vlastnosti závislostí, protože DependencyObject je blízko kořenu hierarchie tříd WPF. Další informace o vlastnostech závislostí a terminologii a konvencích použitých k jejich popisu naleznete v tématu Přehled vlastností závislostí.

Obálky vlastností závislostí

Vlastnosti závislostí WPF, které nejsou připojené vlastnosti, jsou vystaveny obálkou CLR, která implementuje get a set přistupují. Při použití obálky vlastností mohou spotřebitelé vlastností závislostí získat nebo nastavit hodnoty vlastností závislostí stejně jako jakékoli jiné vlastnosti CLR. Objekty get a set přístupové objekty komunikují se systémem základních vlastností prostřednictvím DependencyObject.GetValue a DependencyObject.SetValue voláními a předávají identifikátor vlastnosti závislosti jako parametr. Spotřebitelé vlastností závislostí obvykle nevolají GetValue nebo SetValue přímo, ale pokud implementujete vlastní vlastnost závislostí, použijete tyto metody v obálku.

Kdy implementovat vlastnost závislosti

Když implementujete vlastnost třídy, která je odvozena , DependencyObjectnastavíte ji jako vlastnost závislosti tím, že backing vlastnosti s identifikátorem DependencyProperty . To, jestli je výhodné vytvořit vlastnost závislosti, závisí na vašem scénáři. I když je pro některé scénáře vhodná podpora vlastnosti s privátním polem, zvažte implementaci vlastnosti závislosti, pokud chcete, aby vaše vlastnost podporovala jednu nebo více následujících funkcí WPF:

  • Vlastnosti, které jsou nastaveny v rámci stylu. Další informace najdete v části o stylech a šablonách.

  • Vlastnosti, které podporují datová vazba Další informace o vlastnostech závislostí datové vazby naleznete v tématu Vytvoření vazby vlastností dvou ovládacích prvků.

  • Vlastnosti, které jsou nastaveny prostřednictvím dynamických odkazů na prostředky. Další informace najdete v tématu Prostředky XAML.

  • Vlastnosti, které automaticky dědí jejich hodnotu z nadřazeného prvku ve stromu elementu. V takovém případě se budete muset zaregistrovat pomocí RegisterAttached, a to i v případě, že vytvoříte obálku vlastností pro přístup k CLR. Další informace naleznete v tématu Dědičnost hodnot vlastností.

  • Vlastnosti, které jsou animatovatelné. Další informace najdete v přehledu animace.

  • Oznámení systému vlastností WPF při změně hodnoty vlastnosti. Změny můžou být způsobené akcemi systému vlastností, prostředí, uživatele nebo stylů. Vaše vlastnost může zadat metodu zpětného volání v metadatech vlastností, která se vyvolá pokaždé, když systém vlastností určí, že se hodnota vlastnosti změnila. Související koncept je převod hodnot vlastností. Další informace naleznete v tématu Zpětné volání vlastností závislostí a ověření.

  • Přístup k metadatům vlastností závislostí, která čte procesy WPF. Metadata vlastností můžete například použít k:

    • Určete, jestli by změněná hodnota vlastnosti závislosti měla změnit, aby systém rozložení znovu rozložil vizuály pro prvek.

    • Nastavte výchozí hodnotu vlastnosti závislosti přepsáním metadat u odvozených tříd.

  • Podpora návrháře WPF sady Visual Studio, například úprava vlastností vlastního ovládacího prvku v okně Vlastnosti Další informace najdete v tématu Přehled vytváření obsahu ovládacích prvků.

V některých scénářích je přepsání metadat existující vlastnosti závislosti lepší možností než implementace nové vlastnosti závislosti. Zda přepsání metadat je praktické, závisí na vašem scénáři a na tom, jak blízko se tento scénář podobá implementaci existujících vlastností a tříd závislostí WPF. Další informace o přepsání metadat u existujících vlastností závislostí naleznete v tématu Metadata vlastností závislostí.

Kontrolní seznam pro vytvoření vlastnosti závislosti

Pomocí těchto kroků vytvořte vlastnost závislosti. Některé kroky je možné zkombinovat a implementovat do jednoho řádku kódu.

  1. (Volitelné) Vytvořte metadata vlastností závislostí.

  2. Zaregistrujte vlastnost závislosti v systému vlastností, zadejte název vlastnosti, typ vlastníka, typ hodnoty vlastnosti a volitelně metadata vlastností.

  3. DependencyProperty Definujte identifikátor jako public static readonly pole typu vlastníka. Název pole identifikátoru je název vlastnosti s připojenou příponou Property .

  4. Definujte vlastnost obálky CLR se stejným názvem jako název vlastnosti závislosti. V obálku CLR implementujte get a set přistupují, které se připojují k vlastnosti závislosti, která obálku zálohuje.

Registrace vlastnosti

Aby vaše vlastnost byla vlastnost závislostí, musíte ji zaregistrovat v systému vlastností. Chcete-li zaregistrovat vlastnost, zavolejte metodu Register z těla třídy, ale mimo definice členů. Metoda Register vrátí jedinečný identifikátor vlastnosti závislosti, který použijete při volání rozhraní API systému vlastností. Důvodem, proč je volání provedeno mimo definice členů je proto, že Register přiřadíte návratovou hodnotu k public static readonly poli typu DependencyProperty. Toto pole, které vytvoříte ve třídě, je identifikátor vlastnosti závislosti. V následujícím příkladu první argument Register názvů vlastnosti závislosti AquariumGraphic.

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

Poznámka:

Definování vlastnosti závislosti v těle třídy je typická implementace, ale je také možné definovat vlastnost závislosti ve statickém konstruktoru třídy. Tento přístup může dávat smysl, pokud k inicializaci vlastnosti závislosti potřebujete více než jeden řádek kódu.

Pojmenování vlastností závislostí

Zavedená konvence vytváření názvů pro vlastnosti závislostí je povinná pro normální chování systému vlastností. Název pole identifikátoru, které vytvoříte, musí být registrovaným názvem vlastnosti s příponou Property.

Název vlastnosti závislosti musí být jedinečný v rámci třídy registrace. Vlastnosti závislosti, které jsou zděděné prostřednictvím základního typu, již byly registrovány a nelze je zaregistrovat odvozeným typem. Můžete však použít vlastnost závislosti, která byla zaregistrována jiným typem, a to i typem, ze kterého třída nedědí, přidáním třídy jako vlastníka vlastnosti závislosti. Další informace o přidání třídy jako vlastníka naleznete v tématu Metadata vlastností závislostí.

Implementace obálky vlastností

Podle konvence musí být název vlastnosti obálky stejný jako první parametr Register volání, což je název vlastnosti závislosti. Implementace obálky bude volat GetValue v přístupovém objektu get a SetValue v přístupovém objektu set (pro vlastnosti pro čtení i zápis). Následující příklad ukazuje obálku – podle deklarace pole identifikátoru a volání registrace. Všechny veřejné vlastnosti závislostí ve třídách WPF používají podobný model obálky.

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );

// Declare a read-write property wrapper.
public Uri AquariumGraphic
{
    get => (Uri)GetValue(AquariumGraphicProperty);
    set => SetValue(AquariumGraphicProperty, value);
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

' Declare a read-write property wrapper.
Public Property AquariumGraphic As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set
        SetValue(AquariumGraphicProperty, Value)
    End Set
End Property

Kromě výjimečných případů by implementace obálky měla obsahovat GetValue pouze kód a SetValue kód. Z těchto důvodů se podívejte na důsledky pro vlastní vlastnosti závislostí.

Pokud vaše vlastnost nevyhovuje zavedeným konvencím vytváření názvů, můžete narazit na tyto problémy:

  • Některé aspekty stylů a šablon nebudou fungovat.

  • Většina nástrojů a návrhářů spoléhá na konvence vytváření názvů k správné serializaci XAML a poskytování pomoci prostředí návrháře na úrovni vlastností.

  • Aktuální implementace zavaděče WPF XAML obchází obálky zcela a spoléhá na konvenci pojmenování pro zpracování hodnot atributů. Další informace najdete v tématu Načítání XAML a vlastnosti závislostí.

Metadata vlastností závislostí

Při registraci vlastnosti závislosti vytvoří systém vlastností objekt metadat pro uložení vlastností. Register Přetížení metody umožňují zadat metadata vlastností během registrace, například Register(String, Type, Type, PropertyMetadata). Běžným použitím metadat vlastností je použít vlastní výchozí hodnotu pro nové instance, které používají vlastnost závislosti. Pokud nezadáte metadata vlastností, systém vlastností přiřadí výchozí hodnoty mnoha vlastnostem závislosti.

Pokud vytváříte vlastnost závislosti na třídě odvozené z FrameworkElement, můžete místo její základní třídy PropertyMetadatapoužít specializovanější třídu FrameworkPropertyMetadata metadat . Několik FrameworkPropertyMetadata podpisů konstruktoru umožňuje zadat různé kombinace charakteristik metadat. Pokud chcete jenom zadat výchozí hodnotu, použijte FrameworkPropertyMetadata(Object) a předejte do parametru Object výchozí hodnotu. Ujistěte se, že typ hodnoty odpovídá propertyType zadanému Register ve volání.

Některá FrameworkPropertyMetadata přetížení umožňují zadat příznaky možností metadat pro vaši vlastnost. Systém vlastností tyto příznaky převede na diskrétní vlastnosti a hodnoty příznaku používají procesy WPF, jako je modul rozložení.

Nastavení příznaků metadat

Při nastavovánípříznakůch

  • Pokud hodnota vlastnosti (nebo změny v ní) ovlivňuje, jak systém rozložení vykresluje prvek uživatelského rozhraní, pak nastavte jeden nebo více následujících příznaků:

    • AffectsMeasure, který označuje, že změna hodnoty vlastnosti vyžaduje změnu vykreslování uživatelského rozhraní, konkrétně prostor obsazený objektem v rámci nadřazeného objektu. Například nastavte tento příznak metadat pro Width vlastnost.

    • AffectsArrange, který označuje, že změna hodnoty vlastnosti vyžaduje změnu v vykreslování uživatelského rozhraní, konkrétně umístění objektu v rámci nadřazeného objektu. Obvykle se objekt také nemění velikost. Nastavte například tento příznak metadat pro Alignment vlastnost.

    • AffectsRender, což označuje, že došlo ke změně, která nemá vliv na rozložení a míru, ale přesto vyžaduje další vykreslení. Můžete například nastavit tento příznak pro Background vlastnost nebo jakoukoli jinou vlastnost, která ovlivňuje barvu prvku.

    Tyto příznaky můžete použít také jako vstupy do implementací zpětného volání systému vlastností (nebo rozložení). Zpětné volání můžete použít OnPropertyChanged například k volání InvalidateArrange , když vlastnost instance hlásí změnu hodnoty a je AffectsArrange nastavena v metadatech.

  • Některé vlastnosti ovlivňují vlastnosti vykreslování jejich nadřazeného prvku jinými způsoby. Například změny MinOrphanLines vlastnosti můžou změnit celkové vykreslení dokumentu toku. Můžete použít AffectsParentArrange nebo AffectsParentMeasure signalizovat nadřazené akce ve vlastních vlastnostech.

  • Ve výchozím nastavení vlastnosti závislostí podporují datové vazby. Datové vazby ale můžete zakázat IsDataBindingAllowed , pokud pro ni neexistuje žádný realistický scénář nebo je problém s výkonem datových vazeb, například u velkých objektů.

  • I když je výchozí režim datové vazby pro vlastnosti OneWayzávislosti , můžete změnit režim vazby konkrétní vazby na TwoWay. Další informace naleznete v tématu Směr vazby. Jako autor vlastnosti závislosti můžete dokonce zvolit obousměrnou vazbu výchozího režimu. Příkladem existující vlastnosti závislosti, která používá obousměrnou datovou vazbu, je MenuItem.IsSubmenuOpen, který má stav založený na jiných vlastnostech a volání metody. Scénář IsSubmenuOpen je, že jeho logika nastavení a kompilace MenuItem, interakce s výchozím stylem motivu. TextBox.Text je další vlastnost závislosti WPF, která ve výchozím nastavení používá obousměrnou vazbu.

  • Dědičnost vlastností pro vlastnost závislosti můžete povolit nastavením příznaku Inherits . Dědičnost vlastností je užitečná ve scénářích, kdy nadřazené a podřízené elementy mají společnou vlastnost a dává smysl pro podřízený prvek dědit nadřazenou hodnotu společné vlastnosti. Příkladem zděděné vlastnosti je DataContext, která podporuje operace vazby, které používají scénář master-detail pro prezentaci dat. Dědičnost hodnot vlastností umožňuje zadat kontext dat na stránce nebo kořenovém adresáři aplikace, který ukládá, že je nutné zadat pro podřízené vazby elementů. Přestože zděděná hodnota vlastnosti přepíše výchozí hodnotu, hodnoty vlastností lze nastavit místně u libovolného podřízeného prvku. Dědičnost hodnot vlastností používejte střídmě, protože má náklady na výkon. Další informace naleznete v tématu Dědičnost hodnot vlastností.

  • Nastavte příznak tak Journal , aby označoval, že vlastnost závislosti by měla být zjištěna nebo používána službami deníku navigace. SelectedIndex Například vlastnost nastaví Journal příznak, který doporučuje, aby aplikace zachovaly historii deníku položek vybrané.

Vlastnosti závislostí jen pro čtení

Můžete definovat vlastnost závislosti, která je určena jen pro čtení. Typickým scénářem je vlastnost závislosti, která ukládá interní stav. Je například jen pro čtení, IsMouseOver protože jeho stav by měl být určen pouze vstupem myši. Další informace naleznete v tématu Vlastnosti závislosti jen pro čtení.

Vlastnosti závislostí typu kolekce

Vlastnosti závislostí typu kolekce mají další problémy s implementací, které je potřeba zvážit, například nastavení výchozí hodnoty pro referenční typy a podporu datových vazeb pro prvky kolekce. Další informace naleznete v tématu Vlastnosti závislostí typu kolekce.

Zabezpečení vlastností závislostí

Obvykle deklarujete vlastnosti závislosti jako veřejné vlastnosti a DependencyProperty pole identifikátoru jako public static readonly pole. Pokud zadáte přísnější úroveň přístupu, jako protectedje například , vlastnost závislosti může být stále přístupná prostřednictvím jeho identifikátoru v kombinaci s rozhraními API systému vlastností. Dokonce i pole chráněného identifikátoru je potenciálně přístupné prostřednictvím rozhraní API pro vytváření sestav metadat WPF nebo rozhraní API pro určování hodnot, například LocalValueEnumerator. Další informace naleznete v tématu Zabezpečení vlastností závislostí.

U vlastností závislostí jen pro čtení je DependencyPropertyKeyhodnota vrácená RegisterReadOnly z hodnoty a obvykle nebudete členy DependencyPropertyKeypublic třídy. Vzhledem k tomu, že systém vlastností WPF nešíří DependencyPropertyKey mimo váš kód, má vlastnost závislostí jen pro čtení lepší set zabezpečení než vlastnost závislosti pro čtení i zápis.

Vlastnosti závislostí a konstruktory tříd

Ve spravovaném programování kódu existuje obecný princip, který často vynucuje nástroje pro analýzu kódu, že konstruktory tříd by neměly volat virtuální metody. Důvodem je to, že základní konstruktory lze volat během inicializace konstruktoru odvozené třídy a virtuální metoda volat základní konstruktor může běžet před dokončením inicializace odvozené třídy. Pokud odvozujete z třídy, která již je odvozena DependencyObject, systém vlastností sám volá a zveřejňuje virtuální metody interně. Tyto virtuální metody jsou součástí služeb systému vlastností WPF. Přepsání metod umožňuje odvozené třídy účastnit se stanovení hodnoty. Abyste se vyhnuli potenciálním problémům s inicializací modulu runtime, neměli byste v konstruktorech tříd nastavit hodnoty vlastností závislostí, pokud nepoužíte konkrétní vzor konstruktoru. Další informace naleznete v tématu Sejf vzory konstruktoru pro DependencyObjects.

Viz také