Vlastní vlastnosti závislosti

Toto téma popisuje důvody, proč můžou vývojáři aplikací WPF (Windows Presentation Foundation) a autoři komponent chtít vytvořit vlastní vlastnost závislostí, a popisuje kroky implementace a také některé možnosti implementace, které můžou zlepšit výkon, použitelnost nebo všestrannost vlastnosti.

Předpoklady

Toto téma předpokládá, že rozumíte vlastnostem závislostí z pohledu příjemce existujících vlastností závislostí na třídách WPF a přečetli téma Přehled vlastností závislosti. Abyste mohli postupovat podle příkladů v tomto tématu, měli byste také pochopit XAML a vědět, jak psát aplikace WPF.

Co je vlastnost závislosti?

Můžete povolit, co by jinak byla vlastnost CLR (Common Language Runtime), která podporuje styly, datové vazby, dědičnost, animace a výchozí hodnoty tím, že ji implementujete jako vlastnost závislosti. Vlastnosti závislostí jsou vlastnosti, které jsou registrovány v systému vlastností WPF voláním Register metody (nebo RegisterReadOnly) a jsou podporovány polem identifikátoru DependencyProperty . Vlastnosti závislostí lze používat pouze podle DependencyObject typů, ale DependencyObject je poměrně vysoká v hierarchii tříd WPF, takže většina tříd dostupných ve WPF může podporovat vlastnosti závislostí. Další informace o vlastnostech závislostí a některých terminologiích a konvencích používaných k jejich popisu v této sadě SDK najdete v tématu Přehled vlastností závislostí.

Příklady vlastností závislostí

Mezi příklady vlastností závislostí implementovaných ve třídách WPF patří Background mimo jiné vlastnost, Width vlastnost a Text vlastnost. Každá vlastnost závislosti vystavená třídou má odpovídající veřejné statické pole typu DependencyProperty vystavené ve stejné třídě. Toto je identifikátor vlastnosti závislosti. Identifikátor je pojmenován pomocí konvence: název vlastnosti závislosti s řetězcem Property připojeným k němu. Například odpovídající DependencyProperty pole identifikátoru Background vlastnosti je BackgroundProperty. Identifikátor ukládá informace o vlastnosti závislosti, jak byla zaregistrována, a identifikátor se pak použije později pro jiné operace zahrnující vlastnost závislosti, například volání SetValue.

Jak je uvedeno v přehledu vlastností závislostí, všechny vlastnosti závislostí v WPF (s výjimkou většiny připojených vlastností) jsou také vlastnosti CLR kvůli implementaci "obálky". Z kódu tedy můžete získat nebo nastavit vlastnosti závislostí voláním přístupových objektů CLR, které definují obálky stejným způsobem jako jiné vlastnosti CLR. Jako spotřebitel zavedených vlastností závislostí obvykle nepoužíváte DependencyObject metody GetValue a SetValue, které jsou spojovacím bodem k základnímu systému vlastností. Místo toho stávající implementace vlastností CLR bude již volána GetValue a SetValue v rámci get implementace obálky set vlastnosti pomocí pole identifikátor odpovídajícím způsobem. Pokud implementujete vlastní vlastnost závislosti sami, pak budete definovat obálku podobným způsobem.

Kdy byste měli implementovat vlastnost závislosti?

Při implementaci vlastnosti třídy, pokud vaše třída odvozena od DependencyObject, máte možnost zpět vlastnost s identifikátorem DependencyProperty , a proto ji nastavit jako vlastnost závislosti. Mít vlastnost závislostí není vždy nutná nebo vhodná a bude záviset na vašich potřebách scénáře. Někdy je typická technika zálohování vašeho majetku s privátním polem adekvátní. Vlastnost byste ale měli implementovat jako vlastnost závislosti vždy, když chcete, aby vaše vlastnost podporovala jednu nebo více z následujících funkcí WPF:

  • Chcete, aby byla vlastnost nastavená ve stylu. Další informace naleznete v tématu Styling a Šablonování.

  • Chcete, aby vaše vlastnost podporovala datové vazby. Další informace o vlastnostech závislostí datové vazby naleznete v tématu Vytvoření vazby vlastností dvou ovládacích prvků.

  • Chcete, aby byla vlastnost nastavená pomocí dynamického odkazu na prostředky. Další informace najdete v tématu Prostředky XAML.

  • Chcete zdědit hodnotu vlastnosti automaticky z nadřazeného prvku ve stromu elementu. V tomto případě zaregistrujte metodu RegisterAttached , i když vytvoříte obálku vlastností pro přístup k CLR. Další informace naleznete v tématu Dědičnost hodnot vlastností.

  • Chcete, aby vaše nemovitost byla animovatelná. Další informace najdete v přehledu animace.

  • Chcete, aby systém vlastností hlásil, kdy byla předchozí hodnota vlastnosti změněna akcemi provedenými systémem vlastností, prostředím nebo uživatelem nebo čtením a používáním stylů. Pomocí metadat vlastností může vaše vlastnost zadat metodu zpětného volání, která bude vyvolána pokaždé, když systém vlastností určí, že hodnota vlastnosti byla jednoznačně změněna. Související koncept je převod hodnot vlastností. Další informace naleznete v tématu Zpětné volání vlastností závislostí a ověření.

  • Chcete použít zavedené konvence metadat, které jsou také používány procesy WPF, jako je například generování sestav, zda změna hodnoty vlastnosti by měla vyžadovat, aby systém rozložení znovu rozložil vizuály pro prvek. Nebo chcete mít možnost používat přepsání metadat, aby odvozené třídy mohly měnit vlastnosti založené na metadatech, jako je výchozí hodnota.

  • Chcete, aby vlastnosti vlastního ovládacího prvku přijímaly podporu návrháře WPF sady Visual Studio, například úpravy okna Vlastnosti . Další informace najdete v tématu Přehled vytváření ovládacích prvků.

Při prozkoumání těchto scénářů byste také měli zvážit, jestli můžete svůj scénář dosáhnout přepsáním metadat existující vlastnosti závislosti, a ne implementací zcela nové vlastnosti. 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 ve stávajících vlastnostech a třídách závislostí WPF. Další informace o přepsání metadat u existujících vlastností naleznete v tématu Metadata vlastností závislostí.

Kontrolní seznam pro definování vlastnosti závislosti

Definování vlastnosti závislosti se skládá ze čtyř různých konceptů. Tyto koncepty nejsou nutně striktní procedurální kroky, protože některé z nich se kombinují jako jednotlivé řádky kódu v implementaci:

  • (Volitelné) Vytvořte metadata vlastností pro vlastnost závislosti.

  • Zaregistrujte název vlastnosti v systému vlastností, zadejte typ vlastníka a typ hodnoty vlastnosti. Zadejte také metadata vlastnosti, pokud se používá.

  • DependencyProperty Definujte identifikátor jako publicstaticreadonly pole typu vlastníka.

  • Definujte vlastnost obálky CLR, jejíž název odpovídá názvu vlastnosti závislosti. Implementujte vlastnost getset a přístupové objekty CLR "wrapper" a připojte se k vlastnosti závislosti, která ji zálohuje.

Registrace vlastnosti v systému vlastností

Aby vaše vlastnost byla vlastností závislosti, musíte tuto vlastnost zaregistrovat do tabulky spravované systémem vlastností a poskytnout mu jedinečný identifikátor, který se použije jako kvalifikátor pro pozdější operace systému vlastností. Tyto operace můžou být interní operace nebo vlastní rozhraní API systému vlastností volání kódu. Chcete-li zaregistrovat vlastnost, zavoláte metodu Register v těle třídy (uvnitř třídy, ale mimo definice členů). Pole identifikátoru je také poskytováno Register voláním metody jako návratová hodnota. Důvodem, proč se volání provádí mimo definice jiných členů, je to, že Register tuto návratovou hodnotu použijete k přiřazení a vytvoření publicstaticreadonly pole typu DependencyProperty jako součást třídy. Toto pole se stane identifikátorem vlastnosti závislosti.

public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))

Konvence názvů vlastností závislostí

Existují zavedené zásady vytváření názvů týkající se vlastností závislostí, které musíte dodržovat za všech výjimečných okolností.

Samotná vlastnost závislosti bude mít základní název "AquariumGraphic" jako v tomto příkladu, který je uveden jako první parametr .Register Tento název musí být jedinečný v rámci každého typu registrace. Vlastnosti závislostí zděděné prostřednictvím základních typů jsou považovány za již součást registračního typu; názvy zděděných vlastností nelze znovu zaregistrovat. Existuje však technika přidání třídy jako vlastníka vlastnosti závislosti, i když tato vlastnost závislost není zděděna; Podrobnosti naleznete v tématu Metadata vlastností závislostí.

Při vytváření pole identifikátoru pojmenujte toto pole podle názvu vlastnosti při jeho registraci a přípony Property. Toto pole je identifikátor vlastnosti závislosti a použije se později jako vstup pro SetValue obálky a GetValue volání, která provedete v obálkách, jakýmkoli jiným přístupem ke kódu k vlastnosti vlastním kódem, libovolným externím přístupem ke kódu, který povolíte, systémem vlastností a potenciálně procesory XAML.

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.

Implementace obálky

Implementace obálky by měla volat GetValue v get implementaci a SetValue v set implementaci (původní volání registrace a pole jsou zde uvedeny také pro přehlednost).

Za všech výjimečných okolností by vaše implementace obálky měly provádět pouze GetValue akce a SetValue akce. Důvod je popsán v tématu Načítání XAML a vlastnosti závislostí.

Všechny existující vlastnosti veřejné závislosti, které jsou k dispozici ve třídách WPF, používají tento jednoduchý model implementace obálky; Většina složitosti fungování vlastností závislostí je buď ze své podstaty chování systému vlastností, nebo je implementována prostřednictvím jiných konceptů, jako je převod nebo změna vlastností zpětná volání prostřednictvím metadat vlastností.


public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
public Uri AquariumGraphic
{
  get { return (Uri)GetValue(AquariumGraphicProperty); }
  set { SetValue(AquariumGraphicProperty, value); }
}

Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))
Public Property AquariumGraphic() As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set(ByVal value As Uri)
        SetValue(AquariumGraphicProperty, value)
    End Set
End Property

Opět podle konvence musí být název vlastnosti obálky stejný jako název zvolený a uveden jako první parametr Register volání, který zaregistroval vlastnost. Pokud vaše vlastnost nedodržuje konvenci, nemusí to nutně zakázat všechna možná použití, ale setkáte se s řadou případných problémů:

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

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

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

Metadata vlastností pro novou vlastnost závislosti

Při registraci vlastnosti závislosti vytvoří registrace prostřednictvím systému vlastností objekt metadat, který ukládá vlastnosti vlastnosti. Mnoho z těchto charakteristik má výchozí hodnoty, které jsou nastaveny, pokud je vlastnost registrována jednoduchými podpisy .Register Další podpisy Register umožňují určit metadata, která chcete zadat při registraci vlastnosti. Nejběžnější metadata pro vlastnosti závislosti je dát jim výchozí hodnotu, která se použije u nových instancí, které tuto vlastnost používají.

Pokud vytváříte vlastnost závislosti, která existuje v odvozené třídě FrameworkElement, můžete místo základní PropertyMetadata třídy použít specializovanější třídu FrameworkPropertyMetadata metadat. Konstruktor pro FrameworkPropertyMetadata třídu má několik podpisů, kde můžete zadat různé vlastnosti metadat v kombinaci. Pokud chcete zadat pouze výchozí hodnotu, použijte podpis, který přebírá jeden parametr typu Object. Předejte tento parametr objektu jako výchozí hodnotu specifickou pro vaši vlastnost (zadaná výchozí hodnota musí být typem, který jste zadali jako propertyType parametr ve Register volání).

V FrameworkPropertyMetadatapřípadě můžete také zadat příznaky možností metadat pro vaši vlastnost. Tyto příznaky se po registraci převedou na diskrétní vlastnosti metadat vlastností a použijí se ke komunikaci určitých podmíněných procesů s jinými procesy, jako je modul rozložení.

Nastavení příslušných příznaků metadat

  • Pokud vaše vlastnost (nebo změny v jeho hodnotě) ovlivňuje uživatelské rozhraní (UI) a zejména ovlivňuje, jak má systém rozložení velikost nebo vykreslit prvek na stránce, nastavte jeden nebo více z následujících příznaků: AffectsMeasure, AffectsArrange. AffectsRender

    • AffectsMeasure označuje, že změna této vlastnosti vyžaduje změnu uživatelského rozhraní vykreslování, kde obsahující objekt může vyžadovat více nebo méně místa v nadřazené části. Vlastnost Width by například měla obsahovat tuto sadu příznaků.

    • AffectsArrange označuje, že změna této vlastnosti vyžaduje změnu vykreslování uživatelského rozhraní, která obvykle nevyžaduje změnu ve vyhrazeném prostoru, ale znamená to, že umístění v prostoru se změnilo. Například vlastnost "Zarovnání" by měla mít tuto sadu příznaků.

    • AffectsRender značí, že došlo k nějaké jiné změně, která neovlivní rozložení a míru, ale vyžaduje další vykreslení. Příkladem může být vlastnost, která změní barvu existujícího prvku, například "Background".

    • Tyto příznaky se často používají jako protokol v metadatech pro vlastní implementace přepsání systém vlastností nebo zpětných volání rozložení. Můžete OnPropertyChanged mít například zpětné volání, které bude volat InvalidateArrange , pokud některá vlastnost instance hlásí změnu hodnoty a má AffectsArrange stejně jako true v metadatech.

  • Některé vlastnosti mohou ovlivnit vlastnosti vykreslování obsahujícího nadřazeného prvku, a to způsoby výše a nad rámec změn požadované velikosti uvedené výše. Příkladem je vlastnost použitá MinOrphanLines v modelu dokumentu toku, kde změny této vlastnosti mohou změnit celkové vykreslení dokumentu toku, který obsahuje odstavec. Použijte AffectsParentArrange nebo AffectsParentMeasure identifikujte podobné případy ve vlastních vlastnostech.

  • Ve výchozím nastavení vlastnosti závislostí podporují datové vazby. Datové vazby můžete záměrně zakázat, v případech, kdy pro datovou vazbu neexistuje žádný realistický scénář nebo kde je výkon datové vazby pro velký objekt rozpoznán jako problém.

  • Ve výchozím nastavení je datová vazba Mode pro vlastnosti závislosti výchozí hodnota OneWay. Vazby, které mají být TwoWay na instanci vazby, můžete kdykoli změnit. Podrobnosti najdete v tématu Určení směru vazby. Jako autor vlastnosti závislosti se ale můžete rozhodnout, že vlastnost bude ve výchozím nastavení používat TwoWay režim vazby. Příkladem existující vlastnosti závislosti je , scénář pro tuto vlastnost je MenuItem.IsSubmenuOpen, že IsSubmenuOpen logika nastavení a kompilace MenuItem interakce s výchozím stylem motivu. Logika IsSubmenuOpen vlastnosti nativně používá datové vazby k udržování stavu vlastnosti v souladu s jinými vlastnostmi stavu a volání metody. Další ukázková vlastnost, která ve výchozím nastavení vytvoří vazbu TwoWay , je TextBox.Text.

  • Dědičnost vlastností můžete také povolit ve vlastní vlastnosti závislosti nastavením příznaku Inherits . Dědičnost vlastností je užitečná pro scénář, kdy nadřazené elementy a podřízené prvky mají společnou vlastnost a dává smysl pro podřízené prvky mít tuto konkrétní hodnotu nastavenou na stejnou hodnotu jako nadřazená sada. Příkladem zděděné vlastnosti je DataContext, která se používá pro operace vazby k povolení důležitého hlavního scénáře podrobností pro prezentaci dat. Zděděděné prvky DataContext zdědí všechny podřízené prvky také tento kontext dat. Z důvodu dědičnosti hodnot vlastností můžete zadat kontext dat na stránce nebo kořenovém adresáři aplikace a nemusíte ho vyžadovat pro vazby ve všech možných podřízených prvcích. DataContext je také dobrým příkladem, který ilustruje, že dědičnost přepíše výchozí hodnotu, ale lze ji vždy nastavit místně u libovolného podřízeného prvku; Podrobnosti najdete v tématu Použití vzoru master-detail s hierarchickými daty. Dědičnost hodnot vlastností má možné náklady na výkon, a proto by měla být použita střídmě; podrobnosti naleznete v tématu Dědičnost hodnot vlastností.

  • Nastavte příznak tak, Journal aby indikoval, jestli má být vlastnost závislosti zjištěna nebo používána službami deníku navigace. Příkladem je SelectedIndex vlastnost; každá položka vybraná v ovládacím prvku výběru by měla být zachována při procházení historie deníku.

Vlastnosti závislosti jen pro čtení

Můžete definovat vlastnost závislosti, která je určena jen pro čtení. Scénáře, proč byste ale mohli definovat vlastnost jako jen pro čtení, jsou poněkud odlišné, stejně jako postup registrace v systému vlastností a vystavení identifikátoru. Další informace naleznete v tématu Vlastnosti závislosti jen pro čtení.

Vlastnosti závislostí typu kolekce

Vlastnosti závislostí typu kolekce mají některé další problémy s implementací, které je potřeba zvážit. Podrobnosti najdete v tématu Vlastnosti závislosti typu kolekce.

Důležité informace o zabezpečení vlastností závislostí

Vlastnosti závislostí by měly být deklarovány jako veřejné vlastnosti. Pole identifikátoru vlastnosti závislosti by měla být deklarována jako veřejná statická pole. I když se pokusíte deklarovat jiné úrovně přístupu (například chráněné), je vlastnost závislosti vždy přístupná prostřednictvím 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é z důvodu rozhraní API pro vytváření sestav metadat nebo určování hodnot, která jsou součástí systému vlastností, například LocalValueEnumerator. Další informace naleznete v tématu Zabezpečení vlastností závislostí.

Vlastnosti závislostí a konstruktory tříd

V programování spravovaného kódu (často vynucených nástroji pro analýzu kódu, jako je FxCop), existuje obecný princip, který konstruktory tříd by neměly volat virtuální metody. Důvodem je to, že konstruktory lze volat jako základní inicializaci konstruktoru odvozené třídy a zadávání virtuální metody prostřednictvím konstruktoru může dojít v neúplném inicializačním stavu instance objektu, která je vytvořena. Pokud odvozujete z jakékoli třídy, která již je odvozena DependencyObject, měli byste vědět, že 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. Chcete-li se vyhnout potenciálním problémům s inicializací modulu runtime, neměli byste v konstruktorech tříd nastavit hodnoty vlastností závislostí, pokud nebudete dodržovat velmi specifický vzor konstruktoru. Podrobnosti najdete v tématu Sejf vzory konstruktoru pro DependencyObjects.

Viz také