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í. Oproti Common Language Runtime (CLR) vlastnosti, závislá vlastnost přidává podporu pro styling, datovou vazbu, dědičnost, animace a výchozí hodnoty. 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.
Požadavky
Článek 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
Vázané vlastnosti jsou vlastnosti, které jsou registrovány v systému vlastností WPF prostřednictvím volání Register nebo RegisterReadOnly. Metoda Register
vrátí instanci DependencyProperty, která obsahuje registrovaný název a vlastnosti závislosti. Instanci DependencyProperty
přiřadíte statickému poli jen pro čtení, kterému se říká identifikátor vlastnosti závislostí, který se podle konvence jmenuje <property name>Property
. Například pole identifikátoru vlastnosti Background 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ávislostí lze použít pouze u tříd odvozených z DependencyObject typů. Většina tříd WPF podporuje vlastnosti závislosti, protože DependencyObject
je blízko kořene hierarchie tříd WPF. Další informace o vlastnostech závislostí a terminologii a konvencích použitých k jejich popisu najdete v tématu Přehled vlastností závislostí.
Obálky závislostních vlastností
Vlastnosti závislostí WPF, které nejsou připojené vlastnosti, jsou vystaveny obálkou CLR, která implementuje get
a set
přístupové objekty. 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. Přístupové objekty get
a set
komunikují se systémem základních vlastností prostřednictvím DependencyObject.GetValue a DependencyObject.SetValue volání a předávají identifikátor vlastnosti závislosti jako parametr. Uživatelé závislostních vlastností obvykle nevolají GetValue
ani SetValue
přímo, ale pokud implementujete vlastní závislostní vlastnost, použijete tyto metody v obalu.
Kdy implementovat vlastnost závislosti
Když implementujete vlastnost třídy, která je odvozena z DependencyObject, nastavíte ji jako vlastnost závislosti tím, že zasadíte vlastnost 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 naleznete v tématu Styly a šablony.
Vlastnosti, které podporují datovou vazbu Další informace o vlastnostech závislostí vazby dat naleznete v tématu Vytvoření vazby vlastností dvou ovládacích prvků.
Vlastnosti, které lze nastavit prostřednictvím dynamických odkazů na prostředky. Další informace viz 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 hodnoty vlastnosti.
Vlastnosti, které jsou animatovatelné. Další informace naleznete v tématu Přehled 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ím konceptem je vynucení hodnoty vlastnosti. Pro více informací viz Zpětné volání a ověřování vlastností závislosti.
Přístup k metadatům vlastností závislostí, které čtou procesy WPF. Metadata vlastností můžete například použít k:
Určete, jestli by změněná hodnota vlastnosti závislosti měla způsobit, že rozvrhový systém znovu sestaví vizuály pro prvek.
Nastavte výchozí hodnotu vlastnosti závislosti tím, že přepíšete metadata u odvozených tříd.
Podpora návrháře WPF sady Visual Studio, například úpravy 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 existujících závislostních vlastností naleznete v tématu Metadata závislostních vlastností.
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.
(Volitelné) Vytvořte metadata vlastností závislostí.
Zaregistrujte vlastnost závislosti v systému vlastností, zadejte název vlastnosti, typ vlastníka, typ hodnoty vlastnosti a volitelně metadata vlastností.
Definujte identifikátor DependencyProperty jako pole
public static readonly
typu vlastníka. Název pole identifikátoru je název vlastnosti s příponouProperty
připojen.Definujte vlastnost obálky CLR se stejným názvem jako název vlastnosti závislosti. V obálce CLR implementujte přístupové metody
get
aset
, které se připojují k vlastnosti závislosti, která podporuje obálku.
Registrace nemovitosti
Aby vaše vlastnost byla závislá vlastnost, musíte ji zaregistrovat do systému vlastností. Chcete-li zaregistrovat vlastnost, zavolejte metodu Register uvnitř 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ůvod, proč je volání Register
provedeno mimo definice členů, je, že přiřadíte návratovou hodnotu do pole public static readonly
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
pojmenuje vlastnost 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í vlastnosti závislosti
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 volání Register, což je název vlastnosti závislosti. Vaše implementace vrstvy zavolá GetValue v modulu přístupu get
a SetValue v modulu přístupu set
(pro vlastnosti čtení a zápisu). Následující příklad ukazuje obálku – po volání registrace a deklaraci pole identifikátoru. 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 pouze GetValue a SetValue kód. Z důvodů za tímto se podívejte na Důsledky pro vlastní vlastnosti závislosti.
Pokud váš název nevyhovuje zavedeným konvencím pojmenování, můžete narazit na problémy jako jsou:
Některé aspekty stylů a šablon nebudou fungovat.
Většina nástrojů a návrhářů spoléhá na pojmenovací konvence pro správnou serializaci XAML a poskytování podpory 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 naleznete 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í. Přetížení metody Register umožňují během registrace zadat metadata vlastností, 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 použít specializovanější třídu metadat FrameworkPropertyMetadata místo její základní třídy PropertyMetadata. Několik podpisů konstruktoru FrameworkPropertyMetadata umožňuje zadat různé kombinace charakteristik metadat. Pokud chcete jenom zadat výchozí hodnotu, použijte FrameworkPropertyMetadata(Object) a předejte výchozí hodnotu Object
parametru. Zajistěte, že typ hodnoty odpovídá zadanému propertyType
ve volání Register
.
Některá přetížení FrameworkPropertyMetadata vám umožňují určit příznaky možností metadat pro vaší 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ů
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, což 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 vlastnost
Width
.AffectsArrange, který označuje, že změna hodnoty vlastnosti vyžaduje změnu vykreslování uživatelského rozhraní, konkrétně umístění objektu v jeho nadřazeném objektu. Obvykle se objekt nemění také ve velikosti. Například nastavte tento příznak metadat pro vlastnost
Alignment
.AffectsRender, což značí, že došlo ke změně, která nemá vliv na rozložení a míru, ale stále 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í). Můžete například použít zpětné volání OnPropertyChanged k volání InvalidateArrange, když vlastnost instance hlásí změnu hodnoty a má 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 vlastnosti MinOrphanLines můžou změnit celkové vykreslení dokumentu toku. Pomocí AffectsParentArrange nebo AffectsParentMeasure můžete signalizovat nadřazené akce ve vlastních vlastnostech.
Ve výchozím nastavení vlastnosti závislostí podporují datové vazby. Můžete ale použít IsDataBindingAllowed k zakázání datové vazby, pokud pro ni neexistuje žádný realistický scénář nebo kde je výkon datové vazby problematický, například u velkých objektů.
I když je výchozí režim pro vlastnosti závislosti OneWay, můžete změnit režim vazby konkrétní vazby na TwoWay. Další informace naleznete v části 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ář pro
IsSubmenuOpen
spočívá v tom, že jeho logika nastavení a kompozice MenuIteminteragují 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 úrovni stránky nebo kořene aplikace, což eliminuje nutnost jeho specifikace pro vazby podřízených 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 najdete v části Dědičnost hodnoty vlastnosti.
Nastavte příznak Journal tak, aby indikoval, že vlastnost závislostí by měla být zjištěna nebo používána službami deníku navigace. Například vlastnost SelectedIndex nastaví příznak
Journal
tak, aby doporučovala aplikacím uchovávat historii záznamu o vybraných položkách.
Vlastnosti závislosti pouze 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. Například IsMouseOver je jen pro čtení, protože jeho stav by měl být určen pouze vstupem myši. Další informace naleznete v tématu vlastnosti závislostí 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ávislosti
Obvykle deklarujete vlastnosti závislosti jako veřejné vlastnosti a pole identifikátoru DependencyProperty jako pole public static readonly
. Pokud zadáte více omezující úroveň přístupu, například protected
, vlastnost závislosti je 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, jako je LocalValueEnumerator. Další informace naleznete v tématu zabezpečení vlastností závislostí.
U vlastností závislostí pouze pro čtení je hodnota vrácená z RegisterReadOnlyDependencyPropertyKeya obvykle nebudete dělat z DependencyPropertyKey
public
člena vaší třídy. Vzhledem k tomu, že systém vlastností WPF nerozšíří 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, že základní konstruktory lze volat během inicializace konstruktoru odvozené třídy, a virtuální metoda volaná základním konstruktorem může běžet před úplným dokončením inicializace odvozené třídy. Když odvozujete od třídy, která je již odvozena z DependencyObject, systém vlastností sám interně volá a zveřejňuje virtuální metody. Tyto virtuální metody jsou součástí služeb systému vlastností WPF. Přepsání metod umožňuje odvozeným třídám podílet se na 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 najdete v tématu Vzory bezpečného konstruktoru pro DependencyObjects.
Viz také
- Přehled vlastností závislostí
- metadata vlastností závislostí
- Přehled ovládání autorského zpracování
- Vlastnosti závislostí typu kolekcí
- zabezpečení vlastností závislostí
- Načítání XAML a vlastnosti závislosti
- Bezpečné vzory konstruktorů pro DependencyObjects
.NET Desktop feedback