Dela via


Anpassade beroendeegenskaper

Programutvecklare och komponentförfattare i Windows Presentation Foundation (WPF) kan skapa anpassade beroendeegenskaper för att utöka funktionerna i deras egenskaper. Till skillnad från en CLR-egenskap (Common Language Runtime) lägger en beroendeegenskap till stöd för formatering, databindning, arv, animeringar och standardvärden. Background, Widthoch Text är exempel på befintliga beroendeegenskaper i WPF-klasser. Den här artikeln beskriver hur du implementerar anpassade beroendeegenskaper och presenterar alternativ för att förbättra prestanda, användbarhet och mångsidighet.

Förutsättningar

Artikeln förutsätter grundläggande kunskaper om beroendeegenskaper och att du har läst Översikt över beroendeegenskaper. Om du vill följa exemplen i den här artikeln hjälper det om du är bekant med XAML (Extensible Application Markup Language) och vet hur du skriver WPF-program.

Identifierare för beroendeegenskap

Beroendeegenskaper är egenskaper som registreras med WPF-egenskapssystemet via Register eller RegisterReadOnly anrop. Metoden Register returnerar en DependencyProperty instans som innehåller det registrerade namnet och egenskaperna för en beroendeegenskap. Du tilldelar instansen DependencyProperty till ett statiskt skrivskyddat fält, som kallas för en beroendeegenskapsidentifierare, som enligt konventionen heter <property name>Property. Id-fältet för Background egenskapen är till exempel alltid BackgroundProperty.

Beroendeegenskapsidentifieraren används som ett bakgrundsfält för att hämta eller ange egenskapsvärden i stället för standardmönstret för att säkerhetskopiera en egenskap med ett privat fält. Egenskapssystemet använder inte bara identifieraren, XAML-processorer kan använda den och din kod (och eventuellt extern kod) kan komma åt beroendeegenskaper via sina identifierare.

Beroendeegenskaper kan endast tillämpas på klasser som härleds från DependencyObject typer. De flesta WPF-klasser stöder beroendeegenskaper eftersom DependencyObject ligger nära roten i WPF-klasshierarkin. Mer information om beroendeegenskaper och terminologi och konventioner som används för att beskriva dem finns i Översikt över beroendeegenskaper.

Omslag för beroendeegenskap

WPF-beroendeegenskaper som inte är kopplade egenskaper exponeras av en CLR-omslutning som implementerar get och set har åtkomst. Med hjälp av en egenskapsomslutning kan användare av beroendeegenskaper hämta eller ange värden för beroendeegenskaper, precis som de skulle göra med andra CLR-egenskaper. get- och set-åtkomsterna interagerar med det underliggande egenskapssystemet via DependencyObject.GetValue- och DependencyObject.SetValue-anrop och skickar in beroendeegenskapsidentifieraren som en parameter. Konsumenter av beroendeegenskaper anropar GetValue vanligtvis inte eller SetValue direkt, men om du implementerar en anpassad beroendeegenskap använder du dessa metoder i omslutningen.

När du ska implementera en beroendeegenskap

När du implementerar en egenskap i en klass som härleds från DependencyObjectgör du den till en beroendeegenskap genom att säkerhetskopiera din egenskap med en DependencyProperty identifierare. Om det är fördelaktigt att skapa en beroendeegenskap beror på ditt scenario. Även om det kan vara lämpligt att stödja din egenskap med ett privat fält i vissa scenarier, kan du överväga att implementera en beroendeegenskap om du vill att din egenskap ska ha stöd för en eller flera av följande WPF-funktioner:

  • Egenskaper som kan ställas in i en stil. Mer information finns i Formatmallar och mallar.

  • Egenskaper som stöder databindning. Mer information om beroendeegenskaper för databindning finns i Binda egenskaperna för två kontroller

  • Egenskaper som kan anges via dynamiska resursreferenser. Mer information finns i XAML-resurser.

  • Egenskaper som automatiskt ärver värdet från ett överordnat element i elementträdet. För detta måste du registrera dig med RegisterAttached, även om du också skapar ett egenskapsomslag för CLR-åtkomst. För mer information, se egenskapsvärdesarv .

  • Egenskaper som är animatable. Mer information finns i Översikt över animering

  • Meddelande från WPF-egenskapssystemet när ett egenskapsvärde ändras. Ändringar kan bero på åtgärder av egenskapssystemet, miljön, användaren eller formatmallarna. Din egenskap kan ange en återanropsmetod i egenskapsmetadata som anropas varje gång egenskapssystemet fastställer att egenskapsvärdet har ändrats. Ett relaterat begrepp är egenskapsvärdestvång. För mer information, se beroendeegenskapers återanrop och validering.

  • Åtkomst till metadata för beroendeegenskap, som läss av WPF-processer. Du kan till exempel använda egenskapsmetadata för att:

    • Ange om ett ändrat beroendeegenskapsvärde ska få layoutsystemet att rekomponera visuella objekt för ett element.

    • Ange standardvärdet för en beroendeegenskap genom att åsidosätta metadata för härledda klasser.

  • Stöd för Visual Studio WPF-designer, till exempel redigering av egenskaperna för en anpassad kontroll i fönstret Egenskaper . Mer information finns i Översikt över kontrollredigering

I vissa scenarier är åsidosättande av metadata för en befintlig beroendeegenskap ett bättre alternativ än att implementera en ny beroendeegenskap. Om en åsidosättning av metadata är praktisk beror på ditt scenario och hur nära det scenariot liknar implementeringen av befintliga WPF-beroendeegenskaper och -klasser. Mer information om att åsidosätta metadata för befintliga beroendeegenskaper finns i Metadata för beroendeegenskap.

Checklista för att skapa en beroendeegenskap

Följ de här stegen för att skapa en beroendeegenskap. Vissa av stegen kan kombineras och implementeras på en enda kodrad.

  1. (Valfritt) Skapa metadata för beroendeegenskap.

  2. Registrera beroendeegenskapen med egenskapssystemet, ange ett egenskapsnamn, en ägartyp, egenskapsvärdetypen och eventuellt egenskapsmetadata.

  3. Definiera en DependencyProperty identifierare som ett public static readonly fält för ägartypen. Namnet på identifierarfältet är egenskapsnamnet med det bifogade suffixet Property .

  4. Definiera en CLR-omslutningsegenskap med samma namn som beroendeegenskapens namn. I CLR-omslutningen implementerar du get och set som åtkomstmetoder som ansluter till den beroendeegenskap som stöder omslutningen.

Registrera fastigheten

För att din egenskap ska vara en beroendeegenskap måste du registrera den i systemet för egenskaper. Om du vill registrera din egenskap anropar Register du metoden inifrån brödtexten i din klass, men utanför eventuella medlemsdefinitioner. Metoden Register returnerar en unik beroendeegenskapsidentifierare som du ska använda när du anropar egenskapssystem-API:et. Anledningen till att anropet Register görs utanför medlemsdefinitionerna är att du tilldelar returvärdet till ett public static readonly fält av typen DependencyProperty. Det här fältet, som du skapar i klassen, är identifieraren för din beroendeegenskap. I följande exempel namnger det första argumentet i Register beroendeegenskapen 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)))

Anmärkning

Att definiera beroendeegenskapen i klasstexten är den typiska implementeringen, men det är också möjligt att definiera en beroendeegenskap i klassens statiska konstruktor. Den här metoden kan vara meningsfull om du behöver mer än en kodrad för att initiera beroendeegenskapen.

Namngivning av beroendeegenskap

Den etablerade namngivningskonventionen för beroendeegenskaper är obligatorisk för det normala beteendet för egenskapssystemet. Namnet på det identifierarfält som du skapar måste vara det registrerade namnet på egenskapen med suffixet Property.

Ett namn på beroendeegenskapen måste vara unikt i registreringsklassen. Beroendeegenskaper som ärvs via en bastyp har redan registrerats och kan inte registreras av en härledd typ. Du kan dock använda en beroendeegenskap som har registrerats av en annan typ, även om den inte är en typ som din klass ärver från, genom att lägga till din klass som ägare av beroendeegenskapen. Mer information om hur du lägger till en klass som ägare finns i Metadata för beroendeegenskap.

Implementera en egenskapsinpackning

Enligt konventionen måste namnet på omslutningsegenskapen vara samma som den första parametern för anropet Register , som är namnet på beroendeegenskapen. Omslutningsimplementeringen anropar GetValue i get accessorn och SetValue i set accessorn (för läs- och skrivbara egenskaper). I följande exempel visas en wrapper – efter anropet för registrering och deklarationen av identifierarfältet. Alla publika beroendeegenskaperna för WPF-klasser använder en liknande omslutningsmodell.

// 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

Förutom i sällsynta fall bör wrapper-implementeringen endast innehålla GetValue och SetValue kod. Orsaken till detta finns i Implikationer för anpassade beroendeegenskaper.

Om din egendom inte följer etablerade namngivningskonventioner kan du stöta på följande problem:

  • Vissa aspekter av stilar och mallar fungerar inte.

  • De flesta verktyg och designers förlitar sig på namngivningskonventioner för korrekt serialisering av XAML och ger designermiljöassistans på en nivå per egenskap.

  • Den aktuella implementeringen av WPF XAML-inläsaren kringgår omslutarna helt och förlitar sig på namngivningskonventionen för att bearbeta attributvärden. Mer information finns i XAML-inläsnings- och beroendeegenskaper.

Metadata för beroendeegenskap

När du registrerar en beroendeegenskap skapar egenskapssystemet ett metadataobjekt för att lagra egenskapsegenskaper. Med överlagringar av Register metoden kan du ange egenskapsmetadata under registreringen, till exempel Register(String, Type, Type, PropertyMetadata). En vanlig användning av egenskapsmetadata är att tillämpa ett anpassat standardvärde för nya instanser som använder en beroendeegenskap. Om du inte anger egenskapsmetadata tilldelar egenskapssystemet standardvärden till många av egenskaperna för beroendeegenskaper.

Om du skapar en beroendeegenskap för en klass som härletts från FrameworkElementkan du använda den mer specialiserade metadataklassen FrameworkPropertyMetadata i stället för dess basklass PropertyMetadata. Med flera FrameworkPropertyMetadata konstruktorsignaturer kan du ange olika kombinationer av metadataegenskaper. Om du bara vill ange ett standardvärde använder FrameworkPropertyMetadata(Object) du och skickar standardvärdet till parametern Object . Kontrollera att värdetypen matchar angiven propertyType i anropet Register .

Med vissa FrameworkPropertyMetadata överbelastningar kan du ange metadataalternativflaggor för din egenskap. Egenskapssystemet konverterar dessa flaggor till diskreta egenskaper och flaggvärdena används av WPF-processer, till exempel layoutmotorn.

Inställning av metadataflaggor

Tänk på följande när du ställer in metadataflaggor:

  • Om egenskapsvärdet (eller ändringarna i det) påverkar hur layoutsystemet renderar ett användargränssnittselement anger du en eller flera av följande flaggor:

    • AffectsMeasure, vilket indikerar att en ändring i egenskapsvärdet kräver en ändring i gränssnittsrendering, särskilt det utrymme som upptas av ett objekt inom dess överordnade. Ange till exempel den här metadataflaggan för en Width egenskap.

    • AffectsArrange, vilket indikerar att en ändring i egenskapsvärdet kräver en ändring i användargränssnittets återgivning, särskilt positionen för ett objekt inom dess överordnade. Vanligtvis ändrar objektet inte heller storlek. Ange till exempel den här metadataflaggan för en Alignment egenskap.

    • AffectsRender, vilket indikerar att en ändring har inträffat som inte påverkar layout och mått, men fortfarande kräver en annan återgivning. Ange till exempel den här flaggan för en Background egenskap eller någon annan egenskap som påverkar färgen på ett element.

    Du kan också använda dessa flaggor som indata till dina implementeringar av återanrop till systemet för egenskaper (eller layout). Du kan till exempel använda ett OnPropertyChanged återanrop för att anropa InvalidateArrange när en egenskap för instansen rapporterar en värdeändring och har AffectsArrange angetts i metadata.

  • Vissa egenskaper påverkar återgivningsegenskaperna för deras överordnade element på andra sätt. Ändringar i MinOrphanLines egenskapen kan till exempel ändra den övergripande återgivningen av ett flödesdokument. Använd AffectsParentArrange eller AffectsParentMeasure för att signalera föräldraåtgärder i dina egna egenskaper.

  • Beroendeegenskaper stöder som standard databindning. Du kan dock använda IsDataBindingAllowed för att inaktivera databindning när det inte finns något realistiskt scenario för det, eller där databindningsprestanda är problematiska, till exempel på stora objekt.

  • Även om standardläget för databindning för beroendeegenskaper är OneWaykan du ändra bindningsläget för en specifik bindning till TwoWay. Mer information finns i Bindningsriktning. Som författare av beroendeegenskaper kan du till och med välja att göra tvåvägsbindning till standardläget. Ett exempel på en befintlig beroendeegenskap som använder dubbelriktad databindning är MenuItem.IsSubmenuOpen, som har ett tillstånd som baseras på andra egenskaper och metodanrop. Scenariot för IsSubmenuOpen är att dess inställningslogik och sammansättning av MenuItem, interagerar med standardtemaformatet. TextBox.Text är en annan WPF-beroendeegenskap som använder tvåvägsbindning som standard.

  • Du kan aktivera egenskapsarv för din beroendeegenskap genom att ange flaggan Inherits. Egenskapsarv är användbart för scenarier där överordnade och underordnade element har en egenskap gemensamt och det är vettigt att det underordnade elementet ärver det överordnade värdet för den gemensamma egenskapen. Ett exempel på en ärvbar egenskap är DataContext, som stöder bindningsåtgärder som använder huvudinformationsscenariot

  • Journal Ange flaggan för att ange att beroendeegenskapen ska identifieras eller användas av navigeringsjournaltjänster. Egenskapen SelectedIndex anger flaggan Journal för att rekommendera program att behålla en journalhistorik för markerade objekt.

Skrivskyddade beroendeegenskaper

Du kan definiera en beroendeegenskap som är endast läsbar. Ett typiskt scenario är en beroendeegenskap som lagrar internt tillstånd. Till exempel IsMouseOver är skrivskyddad eftersom dess tillstånd endast ska bestämmas av musingång. Mer information finns i Skrivskyddade beroendeegenskaper.

Beroendeegenskaper av samlingstyp

Beroendeegenskaper av samlingstyp har extra implementeringsproblem att tänka på, till exempel att ange ett standardvärde för referenstyper och stöd för databindning för samlingselement. Mer information finns i beroendeegenskaper för samlingstyp.

Beroendeegenskapssäkerhet

Vanligtvis deklarerar du beroendeegenskaper som offentliga egenskaper och DependencyProperty identifierarfält som public static readonly fält. Om du anger en mer restriktiv åtkomstnivå, till exempel protected, kan en beroendeegenskap fortfarande nås via dess identifierare i kombination med egenskapssystem-API:er. Även ett skyddat identifierarfält är potentiellt tillgängligt via WPF-metadatarapportering eller API:er för värdebestämning, till exempel LocalValueEnumerator. Mer information finns i Dependency Property Security.

För skrivskyddade beroendeegenskaper är det värde som returneras från RegisterReadOnlyDependencyPropertyKey, och vanligtvis kommer du inte att göra DependencyPropertyKey till en public-medlem i din klass. Eftersom WPF-egenskapssystemet inte sprider DependencyPropertyKey utanför koden har en skrivskyddad beroendeegenskap bättre set säkerhet än en skrivskyddad beroendeegenskap.

Beroendeegenskaper och klasskonstruktorer

Det finns en allmän princip i hanterad kodprogrammering, som ofta framtvingas av kodanalysverktyg, att klasskonstruktorer inte ska anropa virtuella metoder. Det beror på att baskonstruktorer kan anropas under initieringen av en härledd klasskonstruktor, och en virtuell metod som anropas av en baskonstruktor kan köras innan den härledda klassen initieras. När du härleder från en klass som redan härleds från DependencyObjectanropar själva egenskapssystemet och exponerar virtuella metoder internt. Dessa virtuella metoder är en del av WPF-egenskapssystemtjänsterna. Genom att åsidosätta metoderna kan härledda klasser delta i värdebestämning. För att undvika potentiella problem med körningsinitiering bör du inte ange värden för beroendeegenskap i konstruktorer för klasser, såvida du inte följer ett specifikt konstruktormönster. Mer information finns i Säkra konstruktormönster för DependencyObjects.

Se även