Megosztás:


Egyéni függőségi tulajdonságok

Az alábbiakban bemutatjuk, hogyan definiálhatja és implementálhatja saját függőségi tulajdonságait egy Windows-futtatókörnyezeti alkalmazáshoz c++, C# vagy Visual Basic használatával. Felsoroljuk azokat az okokat, amelyek miatt az alkalmazásfejlesztők és az összetevők szerzői egyéni függőségi tulajdonságokat szeretnének létrehozni. Ismertetjük az egyéni függőségi tulajdonság implementálási lépéseit, valamint néhány ajánlott eljárást, amely javíthatja a teljesítményt, a használhatóságot vagy a függőségi tulajdonság sokoldalúságát.

Előfeltételek

Feltételezzük, hogy elolvasta a függőségi tulajdonságok áttekintését , és hogy a függőségi tulajdonságokat a meglévő függőségi tulajdonságok fogyasztójának szemszögéből értelmezi. A jelen témakör példáinak követéséhez ismernie kell az XAML-t is, és tudnia kell, hogyan írhat egy alapszintű Windows-futtatókörnyezeti alkalmazást C++, C# vagy Visual Basic használatával.

Mi az a függőségi tulajdonság?

A tulajdonság stílusának, adatkötésének, animációinak és alapértelmezett értékeinek támogatásához a tulajdonságot függőségi tulajdonságként kell implementálnia. A függőségi tulajdonság értékei nem az osztály mezőiként vannak tárolva, hanem az xaml-keretrendszer tárolja őket, és egy kulccsal hivatkoznak rájuk, amely a DependencyProperty.Register metódus meghívásával lesz lekérve, amikor a tulajdonság regisztrálva van a Windows Futtatókörnyezet tulajdonságrendszerében. A függőségi tulajdonságok csak a DependencyObjectből származó típusok esetén használhatók. A DependencyObject azonban meglehetősen magas az osztályhierarchiában, így a felhasználói felületre és a bemutatótámogatásra szánt osztályok többsége támogatja a függőségi tulajdonságokat. A függőségi tulajdonságokról, valamint a leírásukhoz használt terminológiáról és konvenciókról további információt a Függőség tulajdonságainak áttekintése című témakörben talál.

A Windows futtatókörnyezetben a függőségi tulajdonságok például a következők: Control.Background, FrameworkElement.Width és TextBox.Text, többek között.

A konvenció az, hogy az osztály által közzétett minden függőségi tulajdonság rendelkezik egy public static readonly tulajdonsággal, amely a DependencyProperty típusú, ugyanazon az osztályon van közzétéve, és a függőségi tulajdonság azonosítóját biztosítja. Az azonosító neve a következő konvenciót követi: a függőségi tulajdonság neve, a név végéhez hozzáadva a "Tulajdonság" sztringgel. A Control.Background tulajdonság megfelelő DependencyProperty azonosítója például a Control.BackgroundProperty. Az azonosító a regisztráció során tárolja a függőségi tulajdonság adatait, majd felhasználható a függőségi tulajdonságot érintő egyéb műveletekhez, például a SetValue hívásához.

Tulajdonságburkolók

A függőségi tulajdonságok általában burkoló implementációval rendelkeznek. A burkoló nélkül a tulajdonságok lekérésének vagy beállításának egyetlen módja a GetValue és a SetValue függőségi tulajdonság segédprogram használata, valamint az azonosító paraméterként való átadása. Ez egy meglehetősen természetellenes használat valami számára, ami látszólag egy tulajdonságként van jelen. A burkolóval azonban a kód és a függőségi tulajdonságra hivatkozó bármely más kód használhat egy egyszerű objektumtulajdonság-szintaxist, amely természetes a használt nyelvhez.

Ha saját maga implementál egy egyéni függőségi tulajdonságot, és azt szeretné, hogy nyilvános és könnyen hívható legyen, definiálja a tulajdonságburkolókat is. A tulajdonságburkolók a függőségi tulajdonság alapvető információinak tükrözési vagy statikus elemzési folyamatokhoz való jelentéséhez is hasznosak. Pontosabban a burkoló az, ahol olyan attribútumokat helyez el, mint a ContentPropertyAttribute.

Mikor kell függőségi tulajdonságként implementálni egy tulajdonságot?

Amikor nyilvános olvasási/írási tulajdonságot implementál egy osztályon, amíg az osztály a DependencyObjectből származik, lehetősége van arra, hogy a tulajdonság függőségi tulajdonságként működjön. Néha a tulajdonság egy magánmezővel történő támogatásának tipikus technikája megfelelő. Az egyéni tulajdonság függőségi tulajdonságként való definiálása nem mindig szükséges vagy megfelelő. A választás attól függ, hogy milyen forgatókönyveket kíván támogatni a tulajdona.

Érdemes lehet függőségi tulajdonságként implementálni a tulajdonságot, ha azt szeretné, hogy a Windows Futtatókörnyezet vagy a Windows futtatókörnyezeti alkalmazások egy vagy több funkcióját támogassa:

  • A tulajdonság beállítása stíluson keresztül
  • Érvényes céltulajdonságként szolgál az adatkötésben a {Binding} esetén.
  • Animált értékek támogatása storyboardon keresztül
  • Jelentés arról, hogy a tulajdonság értékét módosították:
    • A tulajdonságrendszer által végrehajtott műveletek
    • A környezet
    • Felhasználói műveletek
    • Olvasási és írási stílusok

Függőségi tulajdonság meghatározásának ellenőrzőlistája

A függőségi tulajdonság definiálása fogalmak halmazaként is felfogható. Ezek a fogalmak nem feltétlenül eljárási lépések, mivel a megvalósítás során több fogalom is kezelhető egyetlen kódsorban. Ez a lista csak egy gyors áttekintést nyújt. A témakör későbbi részében részletesebben ismertetjük az egyes fogalmak fogalmát, és több nyelven mutatjuk be a példakódot.

  • Regisztrálja a tulajdonság nevét a tulajdonságrendszerben ( hívásregisztrálás), megadva a tulajdonos típusát és a tulajdonságérték típusát.
    • Van egy kötelező paraméter a Registerhez , amely a tulajdonság metaadatait várja. Adjon meg null értéket ehhez, vagy ha tulajdonságváltozást szeretne, vagy ha a ClearValue meghívásával visszaállítható metaadatalapú alapértelmezett értéket szeretne, adja meg a PropertyMetadata egy példányát.
  • Adjon meg egy DependencyProperty-azonosítótnyilvános statikus, olvasható tulajdonságtagként a tulajdonostípuson.
  • Definiáljon egy burkolótulajdonságot a megvalósító nyelven használt tulajdonságkiegészítési modell alapján. A burkoló tulajdonság nevének meg kell egyeznie a név karaktersorozattal, amelyet a Regisztráció során használtál. Implementálja a get és set metódusokat a burkoló és az általa burkolt függő tulajdonság összekapcsolásához, a GetValue és SetValue meghívásával, valamint a saját tulajdonságazonosító paraméterként való átadásával.
  • (Nem kötelező) Helyezze az olyan attribútumokat, mint a ContentPropertyAttribute a burkolóra.

Megjegyzés:

Ha egyéni csatolt tulajdonságot definiál, általában kihagyja a burkolót. Ehelyett más stílusú kiegészítőt kell írnia, amelyet egy XAML-processzor használhat. Lásd: Egyéni csatolt tulajdonságok.

A tulajdonság regisztrálása

Ahhoz, hogy a tulajdonság függőségi tulajdonság legyen, regisztrálnia kell a tulajdonságot a Windows Futtatókörnyezet tulajdonságrendszer által fenntartott tulajdonságtárolóban. A tulajdonság regisztrálásához hívja meg a Register metódust.

A Microsoft .NET-nyelvek (C# és Microsoft Visual Basic) esetében a Regisztrációt az osztály törzsén belül (az osztályon belül, de a tagdefiníciókon kívül) kell meghívnia. Az azonosítót a Register metódushívás adja meg visszatérési értékként. A register hívás általában statikus konstruktorként vagy egy DependencyProperty típusú nyilvános statikus írásvédett tulajdonság inicializálásának részeként történik az osztály részeként. Ez a tulajdonság elérhetővé teszi a függőségi tulajdonság azonosítóját. Íme néhány példa a Register hívásra.

Megjegyzés:

A függőségi tulajdonság regisztrálása az azonosítótulajdonság-definíció részeként a tipikus implementáció, de a függőségi tulajdonságot az osztály statikus konstruktorában is regisztrálhatja. Ennek a megközelítésnek akkor lehet értelme, ha több kódsorra van szüksége a függőségi tulajdonság inicializálásához.

A C++/CX esetén lehetősége van arra, hogy hogyan ossza fel a megvalósítást a fejléc és a kódfájl között. A jellemző felosztás az, hogy magát az azonosítót nyilvános statikus tulajdonságként deklarálja a fejlécben, get implementációval, de nincs megadva. A get implementáció egy privát mezőre hivatkozik, amely egy nem inicializált DependencyProperty-példány . Deklarálhatja a burkolókat, valamint a burkoló lekérdezési és megadási implementációit is. Ebben az esetben a fejléc minimális implementációt tartalmaz. Ha a burkolónak Windows Runtime attribúcióra van szüksége, adjunk hozzá attribútumot a fejlécben is. Helyezze a Register hívást a kódfájlba egy segédfüggvény keretein belül, amely csak akkor fut, amikor az alkalmazás először inicializálódik. A Register visszatérési értékét használja fel a fejlécben deklarált statikus és nem inicializált azonosítók kitöltésére, amelyeket kezdetben nullptr értékre állított az implementációs fájl gyökértartományában.

public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  nameof(Label),
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null)
);
Public Shared ReadOnly LabelProperty As DependencyProperty =
    DependencyProperty.Register("Label",
      GetType(String),
      GetType(ImageWithLabelControl),
      New PropertyMetadata(Nothing))
// ImageWithLabelControl.idl
namespace ImageWithLabelControlApp
{
    runtimeclass ImageWithLabelControl : Windows.UI.Xaml.Controls.Control
    {
        ImageWithLabelControl();
        static Windows.UI.Xaml.DependencyProperty LabelProperty{ get; };
        String Label;
    }
}

// ImageWithLabelControl.h
...
struct ImageWithLabelControl : ImageWithLabelControlT<ImageWithLabelControl>
{
...
public:
    static Windows::UI::Xaml::DependencyProperty LabelProperty()
    {
        return m_labelProperty;
    }

private:
    static Windows::UI::Xaml::DependencyProperty m_labelProperty;
...
};

// ImageWithLabelControl.cpp
...
Windows::UI::Xaml::DependencyProperty ImageWithLabelControl::m_labelProperty =
    Windows::UI::Xaml::DependencyProperty::Register(
        L"Label",
        winrt::xaml_typename<winrt::hstring>(),
        winrt::xaml_typename<ImageWithLabelControlApp::ImageWithLabelControl>(),
        Windows::UI::Xaml::PropertyMetadata{ nullptr }
);
...
//.h file
//using namespace Windows::UI::Xaml::Controls;
//using namespace Windows::UI::Xaml::Interop;
//using namespace Windows::UI::Xaml;
//using namespace Platform;

public ref class ImageWithLabelControl sealed : public Control
{
private:
    static DependencyProperty^ _LabelProperty;
...
public:
    static void RegisterDependencyProperties();
    static property DependencyProperty^ LabelProperty
    {
        DependencyProperty^ get() {return _LabelProperty;}
    }
...
};

//.cpp file
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml.Interop;

DependencyProperty^ ImageWithLabelControl::_LabelProperty = nullptr;

// This function is called from the App constructor in App.xaml.cpp
// to register the properties
void ImageWithLabelControl::RegisterDependencyProperties()
{
    if (_LabelProperty == nullptr)
    {
        _LabelProperty = DependencyProperty::Register(
          "Label", Platform::String::typeid, ImageWithLabelControl::typeid, nullptr);
    }
}

Megjegyzés:

A C++/CX kódban azért van egy privát mező és egy nyilvános, csak olvasható tulajdonság, amely a DependencyProperty-t hozzáférhetővé teszi, hogy a függőségi tulajdonságot használó egyéb fejlesztők vagy módszerek is használhassák a tulajdonságrendszer segédprogramjainak API-jait, amelyekhez szükséges, hogy az azonosító nyilvános legyen. Ha az azonosító magánjellegű marad, a felhasználók nem használhatják ezeket a segédprogramokat. Ilyen API-k és forgatókönyvek például a GetValue vagy SetValue, a ClearValue, a GetAnimationBaseValue, a SetBinding és a Setter.Property. Ehhez nem használhat nyilvános mezőt, mert a Windows Futtatókörnyezet metaadat-szabályai nem engedélyezik a nyilvános mezőket.

Függőségi tulajdonságnév-konvenciók

A függőségi tulajdonságok elnevezésére vonatkozó konvenciókat minden helyzetben kövessük, kivéve kivételes esetekben. Maga a függőségi tulajdonság egy egyszerű névvel ("Label" az előző példában) rendelkezik, amely a Register első paramétereként van megadva. A névnek minden regisztrációs típuson belül egyedinek kell lennie, és az egyediségi követelmény az örökölt tagokra is vonatkozik. Az alaptípusokon öröklő függőségi tulajdonságok már a regisztrációs típus részét képezik; az örökölt tulajdonságok nevei nem regisztrálhatók újra.

Figyelmeztetés

Bár az itt megadott név bármilyen sztringazonosító lehet, amely érvényes a választott nyelv programozásában, általában a függőségi tulajdonságot is az XAML-ben szeretné beállítani. Az XAML-ben való beállításhoz a választott tulajdonságnévnek érvényes XAML-névnek kell lennie. További információt az XAML áttekintésében talál.

Az azonosító tulajdonság létrehozásakor kombinálja a tulajdonság nevét úgy, ahogy regisztrálta, a "Tulajdonság" utótaggal ("LabelProperty", például). Ez a tulajdonság a függőségi tulajdonság az azonosítója, és bemenetként használatos a saját tulajdonságburkolóiban végzett SetValue és GetValue hívásokhoz. A tulajdonságrendszer és más XAML-processzorok is használják, például {x:Bind}

A burkoló implementálása

A tulajdonságburkolónak meg kell hívnia a GetValue-t a get implementációban, a SetValue-t pedig a set implementációban.

Figyelmeztetés

Kivételes körülmények között a burkoló implementációk csak a GetValue és a SetValue műveleteket hajtják végre. Ellenkező esetben eltérő viselkedést fog kapni, ha a tulajdonság XAML-en keresztül van beállítva, szemben a kódon keresztüli beállításkor. A hatékonyság érdekében az XAML-elemző megkerüli a burkolókat a függőségi tulajdonságok beállításakor; és a SetValue-on keresztül beszél a háttértárhoz.

public String Label
{
    get { return (String)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}
Public Property Label() As String
    Get
        Return DirectCast(GetValue(LabelProperty), String)
    End Get
    Set(ByVal value As String)
        SetValue(LabelProperty, value)
    End Set
End Property
// ImageWithLabelControl.h
...
winrt::hstring Label()
{
    return winrt::unbox_value<winrt::hstring>(GetValue(m_labelProperty));
}

void Label(winrt::hstring const& value)
{
    SetValue(m_labelProperty, winrt::box_value(value));
}
...
//using namespace Platform;
public:
...
  property String^ Label
  {
    String^ get() {
      return (String^)GetValue(LabelProperty);
    }
    void set(String^ value) {
      SetValue(LabelProperty, value);
    }
  }

Egyéni függőségi tulajdonság metainformációi

Ha a tulajdonság metaadatai függőségi tulajdonsághoz vannak rendelve, a tulajdonság tulajdonostípusának vagy alosztályainak minden példányára ugyanazokat a metaadatokat alkalmazza a rendszer. A tulajdonság metaadataiban két viselkedést adhat meg:

  • A tulajdonságrendszer által a tulajdonság minden esetéhez hozzárendelt alapértelmezett érték.
  • Statikus visszahívási módszer, amelyet a rendszer automatikusan meghív a tulajdonságrendszeren belül, amikor tulajdonságérték-változást észlel.

A regiszter hívása tulajdonságok metaadataival

A DependencyProperty.Register meghívásának korábbi példáiban null értéket adtunk át a PropertyMetadata paraméternek. Ha engedélyezni szeretné, hogy egy függőségi tulajdonság alapértelmezett értéket adjon meg, vagy tulajdonságmódosítású visszahívást használjon, meg kell adnia egy PropertyMetadata-példányt , amely ezen képességek egyikét vagy mindkettőt biztosítja.

A DependencyProperty.Register paramétereiben általában beágyazott példányként ad meg egy PropertyMetadata tulajdonságot.

Megjegyzés:

Ha CreateDefaultValueCallback-implementációt definiál, a PropertyMetadata.Create segédprogrammal kell meghatároznia a PropertyMetadata-példányt, és nem kell meghívnia egy PropertyMetadata-konstruktort.

Ez a következő példa módosítja a korábban bemutatott DependencyProperty.Register példákat egy PropertyMetadata-példánypropertyChangedCallback értékkel való hivatkozásával. Az "OnLabelChanged" visszahívás implementációja később jelenik meg ebben a szakaszban.

public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  nameof(Label),
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null,new PropertyChangedCallback(OnLabelChanged))
);
Public Shared ReadOnly LabelProperty As DependencyProperty =
    DependencyProperty.Register("Label",
      GetType(String),
      GetType(ImageWithLabelControl),
      New PropertyMetadata(
        Nothing, new PropertyChangedCallback(AddressOf OnLabelChanged)))
// ImageWithLabelControl.cpp
...
Windows::UI::Xaml::DependencyProperty ImageWithLabelControl::m_labelProperty =
    Windows::UI::Xaml::DependencyProperty::Register(
        L"Label",
        winrt::xaml_typename<winrt::hstring>(),
        winrt::xaml_typename<ImageWithLabelControlApp::ImageWithLabelControl>(),
        Windows::UI::Xaml::PropertyMetadata{ nullptr, Windows::UI::Xaml::PropertyChangedCallback{ &ImageWithLabelControl::OnLabelChanged } }
);
...
DependencyProperty^ ImageWithLabelControl::_LabelProperty =
    DependencyProperty::Register("Label",
    Platform::String::typeid,
    ImageWithLabelControl::typeid,
    ref new PropertyMetadata(nullptr,
      ref new PropertyChangedCallback(&ImageWithLabelControl::OnLabelChanged))
    );

Alapértelmezett érték

Megadhat egy alapértelmezett értéket egy függőségi tulajdonsághoz, így a tulajdonság mindig egy adott alapértelmezett értéket ad vissza, ha nincs megadva. Ez az érték eltérhet az adott tulajdonság típusához tartozó alapértelmezett értékétől.

Ha nincs megadva alapértelmezett érték, a függőségi tulajdonság alapértelmezett értéke hivatkozástípus esetén null értékű, vagy egy értéktípus vagy nyelvi primitív típus alapértelmezett értéke (például 0 egész szám esetén vagy egy karakterlánc üres sztringje). Az alapértelmezett érték létrehozásának fő oka az, hogy a rendszer visszaállítja ezt az értéket, amikor meghívja a ClearValue-t a tulajdonságon. Az alapértelmezett érték tulajdonságonkénti létrehozása kényelmesebb lehet, mint az alapértelmezett értékek létrehozása a konstruktorokban, különösen az értéktípusok esetében. A referenciatípusok esetében azonban győződjön meg arról, hogy az alapértelmezett érték létrehozása nem hoz létre véletlen egyszeri mintát. További információkért tekintse meg a jelen témakör későbbi, ajánlott eljárásait

// ImageWithLabelControl.cpp
...
Windows::UI::Xaml::DependencyProperty ImageWithLabelControl::m_labelProperty =
    Windows::UI::Xaml::DependencyProperty::Register(
        L"Label",
        winrt::xaml_typename<winrt::hstring>(),
        winrt::xaml_typename<ImageWithLabelControlApp::ImageWithLabelControl>(),
        Windows::UI::Xaml::PropertyMetadata{ winrt::box_value(L"default label"), Windows::UI::Xaml::PropertyChangedCallback{ &ImageWithLabelControl::OnLabelChanged } }
);
...

Megjegyzés:

Ne regisztráljon az UnsetValue alapértelmezett értékével. Ha így tesz, az összezavarja az ingatlanfogyasztót, és nem kívánt következményekkel jár az ingatlanrendszeren belül.

CreateDefaultValueCallback

Bizonyos esetekben függőségi tulajdonságokat határoz meg egynél több felhasználói felületi szálon használt objektumokhoz. Ez akkor fordulhat elő, ha több alkalmazás által használt adatobjektumot vagy egynél több alkalmazásban használt vezérlőt definiál. Engedélyezheti az objektum cseréjét a különböző felhasználói felületi szálak között úgy, hogy az alapértelmezett értékpéldány helyett createDefaultValueCallback implementációt biztosít, amely a tulajdonságot regisztráló szálhoz van kötve. A CreateDefaultValueCallback alapvetően egy gyárat határoz meg az alapértelmezett értékekhez. A CreateDefaultValueCallback által visszaadott érték mindig az objektumot használó jelenlegi felhasználói felületi CreateDefaultValueCallback szálhoz van társítva.

A CreateDefaultValueCallbacket meghatározó metaadatok megadásához meg kell hívnia a PropertyMetadata.Create parancsot egy metaadatpéldány visszaadásához; a PropertyMetadata konstruktorok nem rendelkeznek CreateDefaultValueCallback paramétert tartalmazó aláírással.

A CreateDefaultValueCallback tipikus implementációs mintája egy új DependencyObject osztály létrehozása, a DependencyObject tulajdonságának adott tulajdonságértéke a kívánt alapértelmezett értékre beállítása, majd az új osztályt objektumhivatkozásként adja vissza a CreateDefaultValueCallback metódus visszatérési értékével.

Tulajdonság módosítását jelző visszahívó metódus

A tulajdonság által módosított visszahívási metódus definiálható a tulajdonság más függőségi tulajdonságokkal való interakcióinak meghatározásához, illetve az objektum belső tulajdonságának vagy állapotának frissítéséhez, amikor a tulajdonság megváltozik. Amikor a visszahívás meghívásra kerül, a tulajdonságrendszer megállapította, hogy a tulajdonságértékek ténylegesen megváltoztak. Mivel a visszahívási módszer statikus, a visszahívás d paramétere azért fontos, mert azt jelzi, hogy az osztály melyik példánya jelentett változást. Egy tipikus implementáció az eseményadatok NewValue tulajdonságát használja, és valamilyen módon feldolgozza az értéket, általában úgy, hogy valamilyen más módosítást hajt végre a d.-ként átadott objektumon. A tulajdonságváltozásra adott további válaszok a NewValue által jelentett érték elvetése, az OldValue visszaállítása vagy az értéknek a NewValue-ra alkalmazott programozott kényszerre való beállítása.

Ez a következő példa egy PropertyChangedCallback-implementációt mutat be. Implementálja az előző regiszter-példákban hivatkozott metódust a PropertyMetadata konstrukciós argumentumainak részeként. A visszahívás által kezelt forgatókönyv az, hogy az osztálynak van egy "HasLabelValue" nevű számított, írásvédett tulajdonsága is (ennek megvalósítása nincs megadva). A "Label" tulajdonság újraértékelésekor a rendszer meghívja ezt a visszahívási módszert, és a visszahívás lehetővé teszi, hogy a függő számított érték szinkronban maradjon a függőségi tulajdonság módosításaival.

private static void OnLabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    ImageWithLabelControl iwlc = d as ImageWithLabelControl; //null checks omitted
    String s = e.NewValue as String; //null checks omitted
    if (s == String.Empty)
    {
        iwlc.HasLabelValue = false;
    } else {
        iwlc.HasLabelValue = true;
    }
}
    Private Shared Sub OnLabelChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim iwlc As ImageWithLabelControl = CType(d, ImageWithLabelControl) ' null checks omitted
        Dim s As String = CType(e.NewValue,String) ' null checks omitted
        If s Is String.Empty Then
            iwlc.HasLabelValue = False
        Else
            iwlc.HasLabelValue = True
        End If
    End Sub
void ImageWithLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
    auto iwlc{ d.as<ImageWithLabelControlApp::ImageWithLabelControl>() };
    auto s{ winrt::unbox_value<winrt::hstring>(e.NewValue()) };
    iwlc.HasLabelValue(s.size() != 0);
}
static void OnLabelChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs^ e)
{
    ImageWithLabelControl^ iwlc = (ImageWithLabelControl^)d;
    Platform::String^ s = (Platform::String^)(e->NewValue);
    if (s->IsEmpty()) {
        iwlc->HasLabelValue=false;
    }
}

A struktúrák és enumerálások tulajdonságváltozási viselkedése

Ha a DependencyProperty típusa enumerálás vagy struktúra, a visszahívás akkor is meghívható, ha a struktúra belső értékei vagy az enumerálási érték nem változtak. Ez eltér a rendszerprimitívektől, például egy sztringtől, ahol csak akkor hívja meg a rendszer, ha az érték megváltozott. Ezek a belsőleg végrehajtott dobozolási és kicsomagolási műveletek mellékhatásai az értékeknél. Ha van PropertyChangedCallback metódusa egy olyan tulajdonságra, amelynek az értéke felsorolás vagy struktúra, akkor össze kell hasonlítania az OldValue és a NewValue értéket úgy, hogy az értékeket saját maga végzi el a típusátalakítást, és használja az átalakított értékekhez elérhető túlterhelt összehasonlító operátorokat. Vagy ha nem áll rendelkezésre ilyen operátor (ez lehet az egyéni struktúra esetében), előfordulhat, hogy össze kell hasonlítania az egyes értékeket. Általában úgy dönt, hogy nem tesz semmit, ha az eredmény az, hogy az értékek nem változtak.

private static void OnVisibilityValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    if ((Visibility)e.NewValue != (Visibility)e.OldValue)
    {
        //value really changed, invoke your changed logic here
    } // else this was invoked because of boxing, do nothing
}
Private Shared Sub OnVisibilityValueChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
    If CType(e.NewValue,Visibility) != CType(e.OldValue,Visibility) Then
        '  value really changed, invoke your changed logic here
    End If
    '  else this was invoked because of boxing, do nothing
End Sub
static void OnVisibilityValueChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
    auto oldVisibility{ winrt::unbox_value<Windows::UI::Xaml::Visibility>(e.OldValue()) };
    auto newVisibility{ winrt::unbox_value<Windows::UI::Xaml::Visibility>(e.NewValue()) };

    if (newVisibility != oldVisibility)
    {
        // The value really changed; invoke your property-changed logic here.
    }
    // Otherwise, OnVisibilityValueChanged was invoked because of boxing; do nothing.
}
static void OnVisibilityValueChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs^ e)
{
    if ((Visibility)e->NewValue != (Visibility)e->OldValue)
    {
        //value really changed, invoke your changed logic here
    }
    // else this was invoked because of boxing, do nothing
    }
}

Ajánlott eljárások

Az egyéni függőségi tulajdonság meghatározásakor tartsa szem előtt az alábbi szempontokat ajánlott eljárásként.

DependencyObject és szálkezelés

Minden DependencyObject-példányt azon a felhasználói felületen kell létrehozni, amely a Windows Futtatókörnyezet alkalmazás által megjelenített aktuális ablakhoz van társítva. Bár minden DependencyObject-et létre kell hozni a fő felhasználói felületen, az objektumok más szálakból származó diszpécserhivatkozással érhetők el a Dispatcher meghívásával.

A DependencyObject szálkezeléssel kapcsolatos szempontjai azért relevánsak, mert általában azt jelenti, hogy csak a felhasználói felületen futó kód módosíthatja vagy akár beolvashatja a függőségi tulajdonság értékét. A szálkezeléssel kapcsolatos problémák általában elkerülhetők a tipikus felhasználói felületi kódban, amely helyesen használja az aszinkron mintákat és a háttérmunkaszálakat. Általában csak a DependencyObject-hez kapcsolódó szálkezeléssel kapcsolatos problémákba ütközik, ha saját DependencyObject-típusokat definiál, és azokat adatforrásokhoz vagy más olyan forgatókönyvekhez próbálja használni, ahol a DependencyObject nem feltétlenül megfelelő.

A véletlen egytonnák elkerülése

Nem szándékos singleton akkor fordulhat elő, ha egy referenciatípust használó függőségi tulajdonságot deklarál, és meghív egy konstruktort az adott referenciatípushoz a PropertyMetadata tulajdonságot megadó kód részeként. Az történik, hogy a függőségi tulajdonság összes használata csak a PropertyMetadata egy példányát használja, és így megpróbálja megosztani a létrehozott egyetlen referenciatípust. Azoknak az értéktípusoknak az altulajdonságai, amelyeket a függőségi tulajdonságon keresztül állított be, olyan módokon terjedhetnek más objektumokra, amelyeket nem szándékozott.

Osztálykonstruktorokkal beállíthatja a referencia típusú függőségi tulajdonság kezdeti értékeit, ha nem null értéket szeretne, de vegye figyelembe, hogy a függőségi tulajdonságok áttekintéséhez ez helyi értéknek számít. Ha az osztály támogatja a sablonokat, célszerűbb lehet sablont használni erre a célra. Egy önálló minta elkerülésének másik módja, ha egy statikus tulajdonságot tesz elérhetővé a referenciatípuson, amely megfelelő alapértelmezett értéket biztosít az adott osztály értékeihez.

Gyűjtemény típusú függőség tulajdonságai

A gyűjtemény típusú függőségi tulajdonságok további megvalósítási problémákat is figyelembe veendők.

A Gyűjtemény típusú függőségi tulajdonságok viszonylag ritkák a Windows Runtime API-ban. A legtöbb esetben olyan gyűjteményeket használhat, amelyekben az elemek egy DependencyObject alosztály, de maga a gyűjteménytulajdonság hagyományos CLR vagy C++ tulajdonságként van implementálva. Ennek az az oka, hogy a gyűjtemények nem feltétlenül felelnek meg bizonyos tipikus forgatókönyveknek, amelyekben függőségi tulajdonságok szerepelnek. Például:

  • Általában nem animál egy gyűjteményt.
  • Általában nem kell előre feltölteni a gyűjtemény elemeit stílusokkal vagy sablonokkal.
  • Bár a gyűjteményekhez való kötés fő forgatókönyv, a gyűjteményeknek nem kell függőségi tulajdonságnak lenniük ahhoz, hogy kötésforrásként legyenek használva. Kötési célok esetében jellemzőbb az ItemsControl vagy a DataTemplate alosztályainak használata a gyűjteményelemek támogatásához, vagy a nézetmodell-minták használata. További információ a gyűjteményekhez való kötésről és a gyűjteményekből való kötésről: Részletes adatkötés.
  • A gyűjteménymódosítások értesítéseit jobban kezelik olyan felületeken keresztül, mint az INotifyPropertyChanged vagy az INotifyCollectionChanged, vagy a gyűjteménytípus az ObservableCollection<T-ből> származtatva.

A gyűjtemény típusú függőségi tulajdonságokra azonban léteznek forgatókönyvek. A következő három szakasz útmutatást nyújt a gyűjtemény típusú függőségi tulajdonság implementálásához.

A gyűjtemény inicializálása

Függőségi tulajdonság létrehozásakor létrehozhat egy alapértelmezett értéket a függőségi tulajdonság metaadataival. Ügyeljen azonban arra, hogy ne használjon egyetlen statikus gyűjteményt alapértelmezett értékként. Ehelyett szándékosan egy egyedi (példány)gyűjteményre kell beállítania a gyűjteménytulajdonság tulajdonososztályának osztálykonstruktorlogika részeként a gyűjtemény értékét.

// WARNING - DO NOT DO THIS
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
  nameof(Items),
  typeof(IList<object>),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(new List<object>())
);

// DO THIS Instead
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
  nameof(Items),
  typeof(IList<object>),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null)
);

public ImageWithLabelControl()
{
    // Need to initialize in constructor instead
    Items = new List<object>();
}

A DependencyProperty és annak PropertyMetadata alapértelmezett értéke a DependencyProperty statikus definíciójának része. Ha alapértelmezett gyűjtemény (vagy más példány) értéket ad meg alapértelmezett értékként, az az osztály összes példánya között meg lesz osztva ahelyett, hogy minden osztály saját gyűjteményt használna, ahogyan az általában kívánatos lenne.

Értesítések módosítása

A gyűjtemény függőségi tulajdonságként való definiálása nem biztosít automatikusan változásértesítést a gyűjtemény elemeiről a "PropertyChanged" visszahívási metódust invesztő tulajdonságrendszer miatt. Ha értesítéseket szeretne kapni a gyűjteményekről vagy gyűjteményelemekről – például adatkötési forgatókönyv esetén – implementálja az INotifyPropertyChanged vagy INotifyCollectionChanged interfészt. További információ: Az adatkötés részletesen.

Függőségi tulajdonság biztonsági szempontjai

Függőségi tulajdonságok deklarálása nyilvános tulajdonságokként. A függőségi tulajdonság azonosítóit nyilvános statikus csak olvasható tagokként deklarálja. Még ha meg is próbálja deklarálni a nyelv által engedélyezett egyéb hozzáférési szinteket (például védett), a függőségi tulajdonság mindig elérhető az azonosítón keresztül a tulajdonságrendszer API-ival együtt. A függőségi tulajdonság azonosítójának belső vagy privátként való deklarálása nem működik, mert a tulajdonságrendszer nem működik megfelelően.

A burkoló tulajdonságai valóban csak a kényelem kedvéért használhatók, a burkolóra alkalmazott biztonsági mechanizmusok megkerülhetők a GetValue vagy a SetValue meghívásával. Ezért tartsa nyilvánosan a burkoló tulajdonságait; ellenkező esetben csak megnehezíti a tulajdonát a jogos hívók számára anélkül, hogy valódi biztonsági előnyöket biztosítanának.

A Windows futtatókörnyezet nem biztosít módot az egyéni függőségi tulajdonság írásvédettként való regisztrálására.

Függőségi tulajdonságok és osztálykonstruktorok

Általános alapelv, hogy az osztálykonstruktorok nem hívnak virtuális metódusokat. Ennek az az oka, hogy konstruktorok meghívhatók egy származtatott osztálykonstruktor alap inicializálásához, és a virtuális metódus konstruktoron keresztüli beírása akkor fordulhat elő, ha a létrehozandó objektumpéldány még nem inicializálódik teljesen. Ha olyan osztályból származik, amely már a DependencyObjectből származik, ne feledje, hogy maga a tulajdonságrendszer meghívja és elérhetővé teszi a virtuális metódusokat belsőleg a szolgáltatásai részeként. A futásidejű inicializálás lehetséges problémáinak elkerülése érdekében ne állítson be függőségi tulajdonságértékeket az osztályok konstruktoraiban.

A C++/CX-alkalmazások függőségi tulajdonságainak regisztrálása

A C++/CX-ben történő tulajdonságregisztráció implementációja a C#-nál bonyolultabb, egyrészt a fejlécbe és a implementálási fájlba való elkülönítés miatt, mind pedig azért, mert a implementálási fájl gyökértartományában történő inicializálás rossz gyakorlat. (A Visual C++ összetevőbővítmények (C++/CX) közvetlenül a DllMainbe helyezik a gyökér hatókörből származó statikus inicializáló kódot, míg a C#-fordítók a statikus inicializálókat osztályokhoz rendelik, és így elkerülik a DllMain terhelészárolási problémáit.) Itt az ajánlott eljárás egy segédfüggvény deklarálása, amely elvégzi az összes függőségi tulajdonság regisztrálását egy osztályhoz, osztályonként egy függvényhez. Ezután az alkalmazás által használt minden egyes egyéni osztályhoz hivatkoznia kell a használni kívánt egyéni osztályok által közzétett segédregisztrációs függvényre. Az egyes segédregisztrációs függvényeket egyszer hívja meg az alkalmazáskonstruktor (App::App()) részeként, mielőtt .InitializeComponent Ez a konstruktor csak akkor fut, ha az alkalmazásra először hivatkoznak, nem fog újra futni, ha például egy felfüggesztett alkalmazás folytatódik. Az előző C++ regisztrációs példában látható módon az egyes regisztrációs hívások nullptr-ellenőrzése is fontos: az a biztosítás, hogy a függvény egyik hívója sem tudja kétszer regisztrálni a tulajdonságot. Egy második regisztrációs hívás valószínűleg összeomlasztaná az alkalmazást ilyen ellenőrzés nélkül, mert a tulajdonság neve duplikált lenne. Ezt a megvalósítási mintát az XAML-felhasználó és az egyéni vezérlők mintájában láthatja, ha a minta C++/CX verziójának kódját látja.