Megosztás a következőn keresztül:


A szerzői műveletek áttekintése

A Windows Presentation Foundation (WPF) vezérlőmodell bővíthetősége jelentősen csökkenti az új vezérlők létrehozásának szükségességét. Bizonyos esetekben azonban még mindig létre kell hoznia egy egyéni vezérlőt. Ez a témakör azokat a funkciókat ismerteti, amelyek minimálisra csökkentik az egyéni vezérlők és a különböző vezérlőkészítő modellek létrehozását a Windows Presentation Foundationben (WPF). Ez a témakör azt is bemutatja, hogyan hozhat létre új vezérlőt.

Új vezérlő írásának alternatívái

Korábban, ha testre szabott felületet szeretne kapni egy meglévő vezérlőből, a vezérlő szokásos tulajdonságainak módosítására volt korlátozva, például a háttérszín, a szegélyszélesség és a betűméret. Ha egy vezérlő megjelenését vagy viselkedését az előre definiált paramétereken túl szeretné kiterjeszteni, akkor létre kell hoznia egy új vezérlőt, amely általában egy meglévő vezérlőtől öröklődik, és felülírja a vezérlő megrajzolásáért felelős módszert. Bár ez továbbra is lehetőség, a WPF lehetővé teszi a meglévő vezérlők testreszabását a gazdag tartalommodell, a stílusok, a sablonok és az eseményindítók használatával. Az alábbi lista példákat mutat be arra, hogyan használhatók ezek a funkciók egyéni és konzisztens szolgáltatások létrehozásához új vezérlő létrehozása nélkül.

  • Gazdag tartalom. A szabványos WPF-vezérlők közül sok támogatja a tartalmas tartalmakat. Egy Button tartalomtulajdonsága például Objecttípusú, így elméletileg bármi megjeleníthető egy Button. Ahhoz, hogy egy gomb képet és szöveget jelenítsen meg, hozzáadhat egy képet és egy TextBlock a StackPanel-hez, majd hozzárendelheti a StackPanel-t a Content tulajdonsághoz. Mivel a vezérlők képesek WPF-vizualizációelemeket és tetszőleges adatokat megjeleníteni, kevesebb szükség van új vezérlő létrehozására vagy meglévő vezérlő módosítására az összetett vizualizációk támogatásához. A Button és a WPF egyéb tartalommodelljeinek tartalommodellel kapcsolatos további információkért lásd WPF-tartalommodell.

  • Stílusok. A Style olyan értékek gyűjteménye, amelyek egy vezérlőelem tulajdonságait jelölik. Stílusok használatával új vezérlő írása nélkül is létrehozhatja a kívánt vezérlő megjelenésének és viselkedésének újrafelhasználható megjelenítését. Tegyük fel például, hogy azt szeretné, hogy az összes TextBlock vezérlő piros, Arial betűtípust használjon 14-es betűmérettel. Létrehozhat egy stílust erőforrásként, és ennek megfelelően állíthatja be a megfelelő tulajdonságokat. Ezután minden TextBlock, amit az alkalmazásához hozzáad, ugyanúgy fog kinézni.

  • Adatsablonok. A DataTemplate segítségével testre szabhatja, hogyan jelennek meg az adatok egy vezérlőn. Például egy DataTemplate megadhatja, hogy az adatok hogyan jelenjenek meg egy ListBox-ben. Erre példa: Adatsablon áttekintése. Az adatok megjelenésének testreszabása mellett a DataTemplate felhasználói felületi elemeket is tartalmazhatnak, ami nagy rugalmasságot biztosít az egyéni felhasználói felületekben. Például egy DataTemplatehasználatával létrehozhat egy ComboBox, amelyben minden elem tartalmaz egy jelölőnégyzetet.

  • Vezérlősablonok. A WPF számos vezérlője egy ControlTemplate használ a vezérlő szerkezetének és megjelenésének meghatározásához, amely elválasztja a vezérlő megjelenését a vezérlő funkciójától. A vezérlők megjelenését drasztikusan módosíthatja a ControlTemplateújradefiniálásával. Tegyük fel például, hogy olyan vezérlőt szeretne, amely úgy néz ki, mint egy stoplight. Ez a vezérlő egyszerű felhasználói felülettel és funkciókkal rendelkezik. A vezérlő három körből áll, amelyek közül egyszerre csak egy világíthat. Némi visszatükrözés után előfordulhat, hogy egy RadioButton egyszerre csak egy kiválasztott funkcióval rendelkezik, de a RadioButton alapértelmezett megjelenése nem hasonlít a stoplight fényeihez. Mivel a RadioButton egy vezérlősablon használatával határozza meg a megjelenését, könnyen újradefiniálhatja a ControlTemplate-et, hogy megfeleljen a vezérlő követelményeinek, és készítsen jelzőlámpát választógombok segítségével.

    Megjegyzés:

    Bár egy RadioButton használhat DataTemplate, ebben a példában nem elegendő egy DataTemplate. A DataTemplate határozza meg a vezérlőelem tartalmának megjelenését. RadioButtonesetén a tartalom a kör jobb oldalán látható, amely jelzi, hogy a RadioButton van-e kiválasztva. A stoplight példájában a választógombnak csak egy olyan körnek kell lennie, amely képes "világítani". Mivel a stoplight megjelenési követelménye annyira eltér a RadioButtonalapértelmezett megjelenésétől , újra kell definiálni a ControlTemplate. A vezérlők tartalmának (vagy adatainak) meghatározásához általában egy DataTemplate, a vezérlők struktúrájának meghatározásához pedig egy ControlTemplate szolgál.

  • Indítók. A Trigger lehetővé teszi a vezérlők megjelenésének és viselkedésének dinamikus módosítását új vezérlő létrehozása nélkül. Tegyük fel például, hogy több ListBox vezérlő van az alkalmazásban, és azt szeretné, hogy az egyes ListBox elemei félkövérek és pirosak legyenek a kijelölésükkor. Első ösztöne az lehet, hogy létrehoz egy osztályt, amely ListBox-t örököl, és felülbírálja a OnSelectionChanged metódust a kijelölt elem megjelenésének megváltoztatásához, de egy jobb megközelítés az, ha egy ListBoxItem stílushoz ad egy kiváltót, ami megváltoztatja a kijelölt elem megjelenését. Az eseményindítók segítségével módosíthatja a tulajdonságértékeket, vagy műveleteket hajthat végre egy tulajdonság értéke alapján. A EventTrigger lehetővé teszik a műveletek végrehajtását egy esemény bekövetkezésekor.

A stílusokkal, sablonokkal és eseményindítókkal kapcsolatos további információkért lásd stílus- és templatingcímű témakört.

Ha a vezérlő egy meglévő vezérlő funkcióit tükrözi, de azt szeretné, hogy a vezérlő másként nézzen ki, először fontolja meg, hogy az ebben a szakaszban tárgyalt módszerek bármelyikével módosíthatja-e a meglévő vezérlő megjelenését.

Modellek a szerkesztés vezérléséhez

A gazdag tartalommodell, a stílusok, a sablonok és az eseményindítók minimálisra csökkentik az új vezérlők létrehozásának szükségességét. Ha azonban új vezérlőt kell létrehoznia, fontos tisztában lenni a WPF különböző vezérlőkészítő modelljeivel. A WPF három általános modellt biztosít egy vezérlő létrehozásához, amelyek mindegyike különböző funkciókat és rugalmassági szintet biztosít. A három modell alaposztályai UserControl, Controlés FrameworkElement.

Származtatás a UserControlból

A legegyszerűbb módja annak, hogy vezérlőt hozzon létre a WPF-ben, ha a UserControlosztályból származtatja. Ha olyan vezérlőelemet hoz létre, amely örököl a UserControl-tól, meglévő összetevőket ad hozzá a UserControl-hez, elnevezi az összetevőket, és az eseménykezelőkre XAML-ban hivatkozik. Ezután hivatkozhat az elnevezett elemekre, és definiálhatja az eseménykezelőket a kódban. Ez a fejlesztési modell nagyon hasonló a WPF-ben használt alkalmazásfejlesztéshez használt modellhez.

Ha megfelelően van felépítve, a UserControl kihasználhatják a gazdag tartalom, a stílusok és az eseményindítók előnyeit. Ha azonban a vezérlője a UserControl-ből öröklődik, azok a személyek, akik a vezérlőt használják, nem tudnak DataTemplate-et vagy ControlTemplate-t használni a megjelenés testreszabásához. A sablonokat támogató egyéni vezérlő létrehozásához a Control osztályból vagy annak egyik származtatott osztályából (a UserControlkivételével) kell származnia.

A UserControlból származó előnyök

Fontolja meg a UserControl származtatását, ha az alábbiak mindegyike érvényes:

  • A vezérlőt ugyanúgy szeretné felépíteni, mint egy alkalmazás felépítését.

  • A vezérlő csak meglévő összetevőkből áll.

  • Nem kell támogatnia az összetett testreszabást.

Származtatás a vezérlőből

A Control osztályból származik a meglévő WPF-vezérlők többsége által használt modell. Az Control osztálytól öröklő vezérlőelem létrehozásakor sablonokkal határozhatja meg annak megjelenését. Ezzel elválasztja az operatív logikát a vizuális ábrázolástól. A felhasználói felület és a logika leválasztását úgy is biztosíthatja, hogy parancsokat és kötéseket használ az események helyett, és lehetőség szerint elkerüli a hivatkozási elemeket a ControlTemplate. Ha a vezérlő felhasználói felülete és logikája megfelelően leválasztva van, akkor a vezérlő felhasználója újradefiniálhatja a vezérlő ControlTemplate-áját a megjelenés testreszabásához. Bár az egyéni Control létrehozása nem olyan egyszerű, mint egy UserControllétrehozása, az egyéni Control biztosítja a legnagyobb rugalmasságot.

A vezérlésből származó előnyök

Fontolja meg a Control osztály származtatását a UserControl használata helyett, ha a következők bármelyike érvényes:

  • Azt szeretné, ha a vezérlő megjelenése a ControlTemplatesegítségével testre szabható lenne.

  • Azt szeretné, hogy a vezérlő különböző témákat támogatjon.

Származtatás a FrameworkElementből

A UserControl-ból vagy Control-ből származó vezérlők meglévő elemek összetevésére támaszkodnak. Sok esetben ez egy elfogadható megoldás, mivel bármely FrameworkElement-ból örökölt objektum lehet egy ControlTemplate. Vannak azonban olyan esetek, amikor egy vezérlő megjelenése többre van szükség, mint az egyszerű elemösszetétel funkciói. Ezekben a forgatókönyvekben egy összetevőt FrameworkElement-ra alapozni a helyes választás.

A FrameworkElement-alapú összetevők létrehozásának két szabványos módszere van: a közvetlen renderelés és az egyéni elemösszetétel. A közvetlen renderelés magában foglalja a OnRenderFrameworkElement módszerének felülírását, valamint az összetevők vizualizációit explicit módon meghatározó DrawingContext műveletek biztosítását. Ez Image és Borderáltal használt módszer. Az egyéni elemösszetétel magában foglalja, hogy Visual típusú objektumokat használ az összetevő megjelenésének megírásához. Példa: DrawingVisual Objects használata. Track a WPF egyéni elemösszetételt használó vezérlőjének példája. A közvetlen renderelés és az egyéni elemösszetétel is keverhető ugyanabban a vezérlőben.

A FrameworkElementből származó előnyök

Fontolja meg a FrameworkElement származtatását, ha az alábbiak bármelyike érvényes:

  • Az egyszerű elemösszeállítás által biztosítottnál nagyobb mértékben szeretné szabályozni a vezérlő megjelenését.

  • A vezérlő megjelenését saját renderelési logika definiálásával szeretné meghatározni.

  • A meglévő elemeket olyan újszerű módon szeretné összeállítani, amelyek túllépnek a UserControl és Controllehetőségen.

A szerzői alapismeretek szabályozása

Ahogy korábban már említettem, a WPF egyik leghatékonyabb funkciója, hogy túlléphet a vezérlő alapvető tulajdonságainak beállításán a megjelenésének és viselkedésének módosításához, de még mindig nem kell egyéni vezérlőt létrehoznia. A stílus- és adatkötési és triggerfunkciókat a WPF tulajdonságrendszer és a WPF eseményrendszer teszi lehetővé. A következő szakaszok ismertetik azokat a gyakorlatokat, amelyeket követnie kell, függetlenül attól, hogy milyen modellt használ az egyéni vezérlő létrehozásához, hogy az egyéni vezérlő felhasználói ugyanúgy használják ezeket a funkciókat, mint a WPF-ben található vezérlők esetében.

Függőségi tulajdonságok használata

Ha egy tulajdonság függőségi tulajdonság, a következőket teheti:

  • Állítsa be a tulajdonságot egy stílusban.

  • Kösse össze a tulajdonságot egy adatforrással.

  • Használjon dinamikus erőforrást a tulajdonság értékeként.

  • Animálja a tulajdonságot.

Ha azt szeretné, hogy a vezérlő egy tulajdonsága támogassa bármelyik funkciót, akkor azt függőségi tulajdonságként kell implementálnia. Az alábbi példa egy Value nevű függőségi tulajdonságot határoz meg a következők végrehajtásával:

  • Definiáljon egy DependencyProperty nevű ValueProperty azonosítót publicstaticreadonly mezőként.

  • Regisztrálja a tulajdonság nevét a tulajdonságrendszerben a DependencyProperty.Registermeghívásával a következők megadásához:

    • A tulajdonság neve.

    • A tulajdonság típusa.

    • A tulajdonjog típusa.

    • A tulajdonság metaadatai. A metaadatok tartalmazzák a tulajdonság alapértelmezett értékét, egy CoerceValueCallback és egy PropertyChangedCallback.

  • A Value és a get tartozékok implementálásával definiáljon egy setnevű CLR burkolótulajdonságot, amely ugyanaz a név, mint a függőségi tulajdonság regisztrálásához használt név. Vegye figyelembe, hogy a get és set hozzáférők csak a GetValue és a SetValue hívását végzik el. Javasoljuk, hogy a függőségi tulajdonságok tartozékai ne tartalmazzanak további logikát, mert az ügyfelek és a WPF megkerülhetik a tartozékokat, és közvetlenül meghívhatják GetValue és SetValue. Ha például egy tulajdonság egy adatforráshoz van kötve, a tulajdonság set tartozéka nem lesz meghívva. Ahelyett, hogy további logikát adna a beolvasáshoz és a beállításhoz, használja a ValidateValueCallback, a CoerceValueCallbackés a PropertyChangedCallback delegáltakat, hogy reagáljanak az értékre, vagy ellenőrizzék az értéket, amikor az megváltozik. További információ ezekről a visszahívásokról: Függőségi tulajdonság visszahívásai és érvényesítése.

  • Adjon meg egy metódust a CoerceValueCallbacknevű CoerceValue számára. CoerceValue biztosítja, hogy Value nagyobb vagy egyenlő legyen MinValue-nél, és kisebb vagy egyenlő MaxValue-nál.

  • Határozza meg a PropertyChangedCallbackmetódust OnValueChangednéven. OnValueChanged létrehoz egy RoutedPropertyChangedEventArgs<T> objektumot, és előkészíti a ValueChanged irányított eseményt. Az irányított eseményeket a következő szakaszban tárgyaljuk.

/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(
        "Value", typeof(decimal), typeof(NumericUpDown),
        new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
                                      new CoerceValueCallback(CoerceValue)));

/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{
    get { return (decimal)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static object CoerceValue(DependencyObject element, object value)
{
    decimal newValue = (decimal)value;
    NumericUpDown control = (NumericUpDown)element;

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));

    return newValue;
}

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    NumericUpDown control = (NumericUpDown)obj;			

    RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
        (decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
    control.OnValueChanged(e);
}
''' <summary>
''' Identifies the Value dependency property.
''' </summary>
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))

''' <summary>
''' Gets or sets the value assigned to the control.
''' </summary>
Public Property Value() As Decimal
    Get
        Return CDec(GetValue(ValueProperty))
    End Get
    Set(ByVal value As Decimal)
        SetValue(ValueProperty, value)
    End Set
End Property

Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
    Dim newValue As Decimal = CDec(value)
    Dim control As NumericUpDown = CType(element, NumericUpDown)

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))

    Return newValue
End Function

Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
    Dim control As NumericUpDown = CType(obj, NumericUpDown)

    Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
    control.OnValueChanged(e)
End Sub

További információ: Egyéni függőség tulajdonságai.

Irányított események használata

Ahogyan a függőségi tulajdonságok további funkciókkal bővítik a CLR-tulajdonságok fogalmát, az irányított események kiterjesztik a standard CLR-események fogalmát. Új WPF-vezérlő létrehozásakor az is ajánlott, hogy az eseményt átirányított eseményként implementálja, mert egy irányított esemény a következő viselkedést támogatja:

  • Az események több vezérlő szülőjén kezelhetők. Ha egy esemény buborékos esemény, az elemfa egyetlen szülője előfizethet az eseményre. Ezután az alkalmazáskészítők egy kezelő használatával válaszolhatnak több vezérlő eseményére. Ha például a vezérlő minden ListBox elem részét képezi (mivel egy DataTemplaterésze), az alkalmazásfejlesztő a vezérlő eseménykezelőjét a ListBox-re határozhatja meg. Amikor az esemény bármelyik vezérlőn bekövetkezik, a rendszer meghívja az eseménykezelőt.

  • Az irányított események egy EventSetter-ban használhatók, amely lehetővé teszi, hogy az alkalmazásfejlesztők megadják egy esemény kezelőjét egy stíluson belül.

  • Az irányított események egy EventTrigger-ban használhatók, amely hasznos a tulajdonságok XAML használatával történő animálásához. További információ: Animáció áttekintése.

Az alábbi példa egy irányított eseményt határoz meg a következő lépésekkel:

  • Definiáljon egy RoutedEvent nevű ValueChangedEvent azonosítót publicstaticreadonly mezőként.

  • Regisztrálja az irányított eseményt a EventManager.RegisterRoutedEvent metódus meghívásával. A példa a következő információkat adja meg, amikor meghívja RegisterRoutedEvent:

    • Az esemény neve ValueChanged.

    • Az útválasztási stratégia Bubble, ami azt jelenti, hogy először a forrás eseménykezelőjét (az eseményt indító objektumot) hívja meg a rendszer, majd a forrás szülőelemeinek eseménykezelőit egymás után hívja meg, kezdve a legközelebbi szülőelem eseménykezelőjével.

    • Az eseménykezelő típusa RoutedPropertyChangedEventHandler<T>, amelyet Decimal típusból építettek fel.

    • Az esemény tulajdonosi típusa a(z) NumericUpDown.

  • Deklaráljon egy ValueChanged nevű nyilvános eseményt, amely esemény-kiegészítő deklarációkat tartalmaz. A példa meghívja AddHandler a add kiegészítő deklarációban, és RemoveHandler a remove kiegészítő deklarációban a WPF-eseményszolgáltatások használatára.

  • Hozzon létre egy OnValueChanged nevű védett virtuális metódust, amely létrehozza a ValueChanged eseményt.

/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
    "ValueChanged", RoutingStrategy.Bubble,
    typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
    add { AddHandler(ValueChangedEvent, value); }
    remove { RemoveHandler(ValueChangedEvent, value); }
}

/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
    RaiseEvent(args);
}
''' <summary>
''' Identifies the ValueChanged routed event.
''' </summary>
Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))

''' <summary>
''' Occurs when the Value property changes.
''' </summary>
Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
    AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.AddHandler(ValueChangedEvent, value)
    End AddHandler
    RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.RemoveHandler(ValueChangedEvent, value)
    End RemoveHandler
    RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
    End RaiseEvent
End Event

''' <summary>
''' Raises the ValueChanged event.
''' </summary>
''' <param name="args">Arguments associated with the ValueChanged event.</param>
Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
    MyBase.RaiseEvent(args)
End Sub

További információ: Irányított események áttekintése és Egyéni irányított esemény létrehozása.

Kötés használata

A vezérlő felhasználói felületének a logikától való leválasztásához fontolja meg az adatkötés használatát. Különösen fontos, ha a vezérlő megjelenését a ControlTemplatehasználatával adja meg. Adatkötés használatakor előfordulhat, hogy nem szükséges a felhasználói felület adott részeire hivatkoznia a kódból. Érdemes elkerülni a ControlTemplate hivatkozó elemekre való hivatkozásokat, mert ha a kód a ControlTemplate lévő elemekre hivatkozik, és a ControlTemplate megváltozik, a hivatkozott elemet az új ControlTemplatekell tartalmaznia.

Az alábbi példa frissíti a TextBlock vezérlőelem NumericUpDown-ját, nevet rendel hozzá, és név szerint hivatkozik a szövegmezőre a kódban.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
  <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
private void UpdateTextBlock()
{
    valueText.Text = Value.ToString();
}
Private Sub UpdateTextBlock()
    valueText.Text = Value.ToString()
End Sub

Az alábbi példa kötést használ ugyanahhoz a feladathoz.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

    <!--Bind the TextBlock to the Value property-->
    <TextBlock 
        Width="60" TextAlignment="Right" Padding="5"
        Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type local:NumericUpDown}}, 
                       Path=Value}"/>

</Border>

További információ az adatkötésről: Adatkötés áttekintése.

Tervezés tervezőknek

Ha támogatást szeretne kapni az egyéni WPF-vezérlőkhöz a WPF Designer for Visual Studio alkalmazásban (például a Tulajdonságok ablak tulajdonságszerkesztésében), kövesse az alábbi irányelveket. További információkért a WPF Designer fejlesztéséhez lásd: XAML tervezése a Visual Studio.

Függőség tulajdonságai

Mindenképpen implementálja a CLR get és set lekérdezési és beállítási metódusokat a korábban ismertetett "Függőségi tulajdonságok használata" című szakaszban leírtak szerint. A tervezők használhatják a burkolót arra, hogy észleljék egy függőségi tulajdonság jelenlétét, de mint ahogy a WPF és a vezérlő ügyfelei is, nem kell meghívniuk a metódusokat, amikor lekérik vagy beállítják a tulajdonságot.

Csatolt tulajdonságok

A csatolt tulajdonságokat az alábbi irányelvek szerint kell implementálnia az egyéni vezérlőkön:

  • Az public metódussal létrehozott staticreadonly formájú DependencyPropertyPropertyRegisterAttached. A RegisterAttached átadott tulajdonságnévnek meg kell egyeznie PropertyName.

  • public static és SetPropertyNamenevű Get CLR-metóduspár implementálása. Mindkét metódusnak el kell fogadnia a DependencyProperty alapján származtatott osztályt első argumentumként. A SetPropertyName metódus olyan argumentumot is elfogad, amelynek típusa megegyezik a tulajdonság regisztrált adattípusával. A GetPropertyName metódusnak azonos típusú értéket kell visszaadnia. Ha a SetPropertyName metódus hiányzik, a tulajdonság olvasásra korlátozottként van megjelölve.

  • Set PropertyName és GetPropertyName közvetlenül a célfüggőségi objektum GetValue és SetValue metódusaira kell, hogy irányuljanak. A tervezők a metódusburkolón keresztüli hívással vagy a cél függőségi objektum közvetlen hívásával érhetik el a csatolt tulajdonságot.

További információ a csatolt tulajdonságokról: Csatolt tulajdonságok áttekintése.

Megosztott erőforrások definiálása és használata

A vezérlőt ugyanabban a szerelvényben is felveheti, mint az alkalmazás, vagy egy külön szerelvénybe csomagolhatja a vezérlőt, amely több alkalmazásban is használható. A témakörben tárgyalt információk többnyire a használt módszertől függetlenül érvényesek. Van azonban egy különbség, amit érdemes megjegyezni. Amikor egy vezérlőt ugyanabban a szerelvényben helyez el, mint egy alkalmazás, globális erőforrásokat adhat hozzá az App.xaml fájlhoz. A csak vezérlőket tartalmazó szerelvényhez azonban nincs társítva Application objektum, ezért nem érhető el App.xaml-fájl.

Amikor egy alkalmazás erőforrást keres, az alábbi sorrendben három szintet tekint meg:

  1. Az elem szintje.

    A rendszer az erőforrásra hivatkozó elemet használja, majd megkeresi a logikai szülő erőforrásait, és így tovább, amíg el nem éri a gyökérelemet.

  2. Az alkalmazás szintje.

    Az Application objektum által definiált erőforrások.

  3. A téma szintje.

    A témaszintű szótárak egy Témák nevű almappában vannak tárolva. A Témák mappában lévő fájlok témáknak felelnek meg. Lehet például Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml stb. A generic.xaml nevű fájlt is használhatja. Amikor a rendszer a témák szintjén keres egy erőforrást, először a témaspecifikus fájlban keresi, majd a generic.xaml fájlban keresi.

Ha a vezérlő az alkalmazástól független szerelvényben van, a globális erőforrásokat az elem szintjén vagy a téma szintjén kell elhelyeznie. Mindkét módszernek megvannak az előnyei.

Erőforrások meghatározása elemszinten

A megosztott erőforrásokat elemszinten úgy határozhatja meg, hogy létrehoz egy egyéni erőforrásszótárat, és egyesíti azt a vezérlő erőforrás-szótárával. Ha ezt a módszert használja, az erőforrásfájlt bárminek elnevezheti, és ugyanabban a mappában lehet, mint a vezérlők. Az elemszintű erőforrások egyszerű sztringeket is használhatnak kulcsként. Az alábbi példa létrehoz egy LinearGradientBrush Dictionary1.xaml nevű erőforrásfájlt.

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <LinearGradientBrush 
    x:Key="myBrush"  
    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />
  </LinearGradientBrush>
  
</ResourceDictionary>

Miután definiálta a szótárt, egyesítenie kell azt a vezérlő erőforrás-szótárával. Ezt XAML vagy kód használatával teheti meg.

Az alábbi példa egy erőforrás-szótárat egyesít az XAML használatával.

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

Ennek a megközelítésnek a hátránya, hogy minden hivatkozáskor létrejön egy ResourceDictionary objektum. Ha például 10 egyéni vezérlő van a könyvtárában, és az XAML használatával egyesíti az egyes vezérlők megosztott erőforrás-szótárait, 10 azonos ResourceDictionary objektumot hoz létre. Ezt elkerülheti egy statikus osztály létrehozásával, amely egyesíti az erőforrásokat a kódban, és visszaadja az eredményül kapott ResourceDictionary.

Az alábbi példa létrehoz egy osztályt, amely egy megosztott ResourceDictionaryad vissza.

internal static class SharedDictionaryManager
{
    internal static ResourceDictionary SharedDictionary
    {
        get
        {
            if (_sharedDictionary == null)
            {
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml",
                                    System.UriKind.Relative);

                _sharedDictionary =
                    (ResourceDictionary)Application.LoadComponent(resourceLocater);
            }

            return _sharedDictionary;
        }
    }

    private static ResourceDictionary _sharedDictionary;
}

Az alábbi példa egyesíti a megosztott erőforrást a vezérlő konstruktorában lévő egyéni vezérlő erőforrásaival, mielőtt meghívja InitializeComponent. Mivel a SharedDictionaryManager.SharedDictionary statikus tulajdonság, a ResourceDictionary csak egyszer jön létre. Mivel az erőforrásszótárat a InitializeComponent hívása előtt egyesítették, az erőforrások az XAML-fájlban érhetők el a vezérlő számára.

public NumericUpDown()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();
}

Erőforrások meghatározása témaszinten

A WPF lehetővé teszi, hogy különböző Windows-témákhoz hozzon létre erőforrásokat. Vezérlőelem-szerzőként meghatározhat egy erőforrást egy adott témához, amely a használt téma függvényében módosítja a vezérlő megjelenését. Például egy Button megjelenése a Klasszikus Windows-témában (a Windows 2000 alapértelmezett témája) eltér a Windows Luna téma Button (a Windows XP alapértelmezett témája), mert a Button minden témához más ControlTemplate használ.

A témára jellemző erőforrások egy adott fájlnévvel rendelkező erőforrás-szótárban vannak tárolva. Ezeknek a fájloknak egy Themes nevű mappában kell lenniük, amely a vezérlőt tartalmazó mappa almappája. Az alábbi táblázat az egyes fájlokhoz társított erőforrás-szótárfájlokat és témát sorolja fel:

Erőforrás-szótár fájlneve Windows-téma
Classic.xaml Klasszikus Windows 9x/2000 megjelenés Windows XP rendszeren
Luna.NormalColor.xaml Alapértelmezett kék téma Windows XP rendszeren
Luna.Homestead.xaml Olíva téma Windows XP rendszeren
Luna.Metallic.xaml Ezüst téma Windows XP rendszeren
Royale.NormalColor.xaml Alapértelmezett téma a Windows XP Media Center Editionben
Aero.NormalColor.xaml Alapértelmezett téma Windows Vista rendszeren

Nem kell minden témához erőforrást definiálnia. Ha egy erőforrás nincs definiálva egy adott témához, akkor a vezérlő a Classic.xaml-ra ellenőrzi az erőforrást. Ha az erőforrás nincs definiálva az aktuális témának megfelelő fájlban vagy Classic.xaml, a vezérlő az általános erőforrást használja, amely egy generic.xamlnevű erőforrás-szótárfájlban található. A generic.xaml fájl ugyanabban a mappában található, mint a témaspecifikus erőforrás-szótárfájlok. Bár generic.xaml nem felel meg egy adott Windows-témának, továbbra is témaszintű szótár.

A C# vagy Visual Basic NumericUpDown egyéni vezérlő téma és felhasználói felület automatizálási támogatási mintája két erőforrásszótárat tartalmaz a NumericUpDown vezérlőelemhez: az egyik a generic.xaml, a másik a Luna.NormalColor.xaml.

Amikor elhelyez egy ControlTemplate-t a témaspecifikus erőforrás-szótárfájlok bármelyikében, létre kell hoznia egy statikus konstruktort a vezérlőhöz, majd meghívni a OverrideMetadata(Type, PropertyMetadata) metódust a DefaultStyleKey-n, ahogy az alábbi példában látható.

static NumericUpDown()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Kulcsok definiálása és hivatkozása témaerőforrásokhoz

Amikor elemszinten definiál egy erőforrást, hozzárendelhet egy sztringet kulcsként, és a sztringen keresztül hozzáférhet az erőforráshoz. Amikor a téma szintjén definiál egy erőforrást, kulcsként egy ComponentResourceKey kell használnia. Az alábbi példa egy erőforrást határoz meg a generic.xaml fájlban.

<LinearGradientBrush 
     x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter}, 
                                  ResourceId=MyEllipseBrush}"  
                                  StartPoint="0,0" EndPoint="1,0">
    <GradientStop Color="Blue" Offset="0" />
    <GradientStop Color="Red" Offset="0.5" />
    <GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>

Az alábbi példa az erőforrásra hivatkozik a kulcsként megadott ComponentResourceKey megadásával.

<RepeatButton 
    Grid.Column="1" Grid.Row="0"
    Background="{StaticResource {ComponentResourceKey 
                        TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                        ResourceId=ButtonBrush}}">
    Up
</RepeatButton>
<RepeatButton 
    Grid.Column="1" Grid.Row="1"
    Background="{StaticResource {ComponentResourceKey 
                    TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                    ResourceId=ButtonBrush}}">
    Down
 </RepeatButton>
A témaerőforrások helyének megadása

A vezérlő erőforrásainak megkereséséhez az üzemeltetési alkalmazásnak tudnia kell, hogy a szerelvény vezérlőspecifikus erőforrásokat tartalmaz. Ezt úgy teheti meg, hogy hozzáadja a ThemeInfoAttribute a vezérlőt tartalmazó szerelvényhez. A ThemeInfoAttribute egy GenericDictionaryLocation tulajdonságot tartalmaz, amely meghatározza az általános erőforrások helyét, valamint egy ThemeDictionaryLocation tulajdonságot, amely meghatározza a témaspecifikus erőforrások helyét.

Az alábbi példa a GenericDictionaryLocation és ThemeDictionaryLocation tulajdonságokat SourceAssemblyértékre állítja, hogy az általános és témaspecifikus erőforrások ugyanabban a szerelvényben legyenek, mint a vezérlő.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
           ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>

Lásd még