Sdílet prostřednictvím


Přehled vytváření ovládacích prvků

Rozšiřitelnost modelu ovládacího prvku WINDOWS Presentation Foundation (WPF) výrazně snižuje potřebu vytvoření nového ovládacího prvku. V některých případech ale možná budete muset vytvořit vlastní ovládací prvek. Toto téma popisuje funkce, které minimalizují nutnost vytvořit vlastní ovládací prvek a různé modely vytváření ovládacích prvků ve Windows Presentation Foundation (WPF). Toto téma také ukazuje, jak vytvořit nový ovládací prvek.

Alternativy k psaní nového ovládacího prvku

Pokud jste chtěli získat přizpůsobené prostředí z existujícího ovládacího prvku, byli jste omezeni na změnu standardních vlastností ovládacího prvku, jako je barva pozadí, šířka ohraničení a velikost písma. Pokud chcete rozšířit vzhled nebo chování ovládacího prvku nad rámec těchto předdefinovaných parametrů, budete muset vytvořit nový ovládací prvek, obvykle děděním z existujícího ovládacího prvku a přepsáním metody zodpovědné za vykreslení ovládacího prvku. I když je to stále možnost, WPF umožňuje přizpůsobit existující ovládací prvky pomocí jeho bohaté kon režim stanu l, stylů, šablon a triggerů. Následující seznam uvádí příklady použití těchto funkcí k vytváření vlastních a konzistentních prostředí bez nutnosti vytvářet nový ovládací prvek.

  • Bohatý obsah. Mnoho standardních ovládacích prvků WPF podporuje bohatý obsah. Například vlastnost obsahu typu Button Object, takže teoreticky cokoli lze zobrazit na objektu Button. Pokud chcete, aby se na tlačítku zobrazil obrázek a text, můžete k StackPanel vlastnosti přidat obrázek a a TextBlock přiřadit StackPanel hoContent. Vzhledem k tomu, že ovládací prvky můžou zobrazovat vizuální prvky WPF a libovolná data, není nutné vytvořit nový ovládací prvek nebo upravit existující ovládací prvek tak, aby podporoval komplexní vizualizaci. Další informace o kon režim stanu l pro Button a další kon režim stanu ls v WPF naleznete v tématu WPF Content Model.

  • Styly. A Style je kolekce hodnot, které představují vlastnosti ovládacího prvku. Pomocí stylů můžete vytvořit opakovaně použitelnou reprezentaci požadovaného vzhledu a chování ovládacího prvku bez psaní nového ovládacího prvku. Předpokládejme například, že chcete, aby všechny TextBlock ovládací prvky měly červené písmo Arial s velikostí písma 14. Styl můžete vytvořit jako prostředek a odpovídajícím způsobem nastavit příslušné vlastnosti. TextBlock Každý, který přidáte do aplikace, bude mít stejný vzhled.

  • Šablony dat. A DataTemplate umožňuje přizpůsobit způsob zobrazení dat v ovládacím prvku. Lze například použít k určení způsobu DataTemplate zobrazení dat v .ListBox Příklad najdete v tématu Přehled šablon dat. Kromě přizpůsobení vzhledu dat DataTemplate může obsahovat prvky uživatelského rozhraní, které poskytují velkou flexibilitu ve vlastních uživatelských rozhraních. Můžete například vytvořitDataTemplateComboBox, ve kterém každá položka obsahuje zaškrtávací políčko.

  • Šablony ovládacích prvků Mnoho ovládacích prvků ve WPF používá ControlTemplate k definování struktury a vzhledu ovládacího prvku, který odděluje vzhled ovládacího prvku od funkce ovládacího prvku. Vzhled ovládacího prvku můžete výrazně změnit tak, že změníte jeho ControlTemplate. Předpokládejme například, že chcete ovládací prvek, který vypadá jako semafor. Tento ovládací prvek má jednoduché uživatelské rozhraní a funkce. Ovládací prvek je tři kruhy, pouze jeden z nich může být rozsvícen najednou. Po nějakém odrazu si můžete uvědomit, že RadioButton nabízí funkce jen jedné vybrané najednou, ale výchozí vzhled RadioButton vypadá jako světla na semaforu. Vzhledem k tomu, že RadioButton používá řídicí šablonu k definování jejího vzhledu, je snadné předefinovat ControlTemplate tak, aby vyhovoval požadavkům ovládacího prvku, a pomocí přepínačů nastavit zarážku.

    Poznámka:

    RadioButton I když může použít , DataTemplatenení v tomto příkladu DataTemplate dostačující. Definuje DataTemplate vzhled obsahu ovládacího prvku. V případě RadioButton, obsah je cokoli, co se zobrazí napravo od kruhu, který označuje, zda RadioButton je vybrán. V příkladu semaforu musí být přepínač jen kruh, který může "rozsvítit". Vzhledem k tomu, že požadavek na vzhled se stoperu liší od výchozího RadioButtonvzhledu , je nutné předefinovat ControlTemplate. Obecně DataTemplate se používá k definování obsahu (nebo dat) ovládacího prvku a ControlTemplate slouží k definování struktury ovládacího prvku.

  • Spouště. A Trigger umožňuje dynamicky měnit vzhled a chování ovládacího prvku bez vytvoření nového ovládacího prvku. Předpokládejme například, že máte v aplikaci více ListBox ovládacích prvků a chcete, aby položky v každé z nich ListBox byly při výběru tučné a červené. Vaším prvním instinktem může být vytvoření třídy, která dědí z ListBox metody a přepíše OnSelectionChanged způsob změny vzhledu vybrané položky, ale lepším přístupem je přidat trigger do stylu ListBoxItem , který změní vzhled vybrané položky. Trigger umožňuje změnit hodnoty vlastností nebo provádět akce na základě hodnoty vlastnosti. Umožňuje EventTrigger provádět akce, když dojde k události.

Další informace o stylech, šablonách a triggerech najdete v tématu Stylování a šablony.

Obecně platí, že pokud ovládací prvek zrcadlí funkčnost existujícího ovládacího prvku, ale chcete, aby ovládací prvek vypadal jinak, měli byste nejprve zvážit, jestli můžete použít některou z metod probíraných v této části ke změně vzhledu existujícího ovládacího prvku.

Modely pro vytváření ovládacích prvků

Bohaté kon režim stanu l, styly, šablony a triggery minimalizují potřebu vytvoření nového ovládacího prvku. Pokud ale potřebujete vytvořit nový ovládací prvek, je důležité pochopit různé modely vytváření ovládacích prvků ve WPF. WPF poskytuje tři obecné modely pro vytvoření ovládacího prvku, z nichž každá poskytuje jinou sadu funkcí a úroveň flexibility. Základní třídy pro tři modely jsou UserControl, Controla FrameworkElement.

Odvození z UserControl

Nejjednodušší způsob vytvoření ovládacího prvku ve WPF je odvozen od UserControl. Při vytváření ovládacího prvku, který dědí z UserControl, přidáte existující komponenty do UserControl, pojmenujte komponenty a odkazovat obslužné rutiny událostí v XAML. Potom můžete odkazovat na pojmenované elementy a definovat obslužné rutiny událostí v kódu. Tento vývojový model je velmi podobný modelu používanému pro vývoj aplikací ve WPF.

Pokud je sestavení správně sestavené, UserControl může využívat výhody bohatého obsahu, stylů a triggerů. Pokud však váš ovládací prvek dědí z UserControl, uživatelé, kteří používají váš ovládací prvek, nebudou moct použít DataTemplate ani ControlTemplate přizpůsobit jeho vzhled. K vytvoření vlastního ovládacího prvku, který podporuje šablony, je nutné odvodit z Control třídy nebo jedné z jejích odvozených tříd (kromě UserControl).

Výhody odvození z UserControl

Zvažte odvození ze UserControl všech následujících možností:

  • Chcete vytvořit ovládací prvek podobně jako při vytváření aplikace.

  • Váš ovládací prvek se skládá jenom z existujících součástí.

  • Nemusíte podporovat komplexní přizpůsobení.

Odvození z ovládacího prvku

Odvození z Control třídy je model používaný většinou existujících ovládacích prvků WPF. Při vytváření ovládacího prvku, který dědí z Control třídy, definujete jeho vzhled pomocí šablon. Tím oddělíte provozní logiku od vizuální reprezentace. Oddělení uživatelského rozhraní a logiky můžete také zajistit pomocí příkazů a vazeb místo událostí a vyhnout se odkazování na prvky v ControlTemplate případě, že je to možné. Pokud je uživatelské rozhraní a logika vašeho ovládacího prvku správně oddělené, může uživatel ovládacího prvku předefinovat ControlTemplate jeho vzhled. I když vytváření vlastního Control objektu není tak jednoduché jako sestavení UserControl, vlastní Control poskytuje největší flexibilitu.

Výhody odvození z řízení

Zvažte odvození namísto Control použití UserControl třídy, pokud platí některá z následujících možností:

  • Chcete, aby byl vzhled ovládacího prvku přizpůsobitelný prostřednictvím ovládacího ControlTemplateprvku .

  • Chcete, aby ovládací prvek podporoval různé motivy.

Odvození z FrameworkElement

Ovládací prvky odvozené z UserControl existujících prvků nebo Control se na ně spoléhají. V mnoha scénářích je to přijatelné řešení, protože jakýkoli objekt, který dědí z FrameworkElement , může být v objektu ControlTemplate. Existují však časy, kdy vzhled ovládacího prvku vyžaduje více než funkce jednoduchého složení prvků. Pro tyto scénáře je správné nastavení komponenty FrameworkElement .

Existují dvě standardní metody pro sestavování FrameworkElementkomponent: přímé vykreslování a vlastní složení prvků. Přímé vykreslování zahrnuje přepsání OnRender metody FrameworkElement a poskytování DrawingContext operací, které explicitně definují vizuály komponent. Jedná se o metodu používanou metodou Image a Border. Vlastní složení elementů zahrnuje použití objektů typu Visual k vytvoření vzhledu komponenty. Příklad najdete v tématu Použití objektů DrawingVisual. Track je příkladem ovládacího prvku ve WPF, který používá vlastní složení elementů. Je také možné kombinovat přímé vykreslování a vlastní složení prvků ve stejném ovládacím prvku.

Výhody odvození z FrameworkElement

Zvažte odvození z FrameworkElement některé z následujících možností:

  • Chcete mít přesnou kontrolu nad vzhledem vašeho ovládacího prvku nad rámec toho, co je poskytováno jednoduchým složením prvků.

  • Vzhled ovládacího prvku chcete definovat definováním vlastní logiky vykreslování.

  • Chcete vytvořit existující prvky v nových způsobech, které přesahují to, co je možné s UserControl a Control.

Základy vytváření ovládacích prvků

Jak jsme už zmínili dříve, jednou z nejvýkonnějších funkcí WPF je schopnost překročit nastavení základních vlastností ovládacího prvku, aby se změnil vzhled a chování ovládacího prvku, ale stále není nutné vytvořit vlastní ovládací prvek. Styling, datová vazba a aktivační funkce jsou možné systémem vlastností WPF a systémem událostí WPF. Následující části popisují některé postupy, které byste měli dodržovat, bez ohledu na model, který používáte k vytvoření vlastního ovládacího prvku, aby uživatelé vašeho vlastního ovládacího prvku mohli tyto funkce používat stejně jako u ovládacího prvku, který je součástí WPF.

Použití vlastností závislosti

Pokud je vlastnost závislostí, je možné provést následující:

  • Nastavte vlastnost ve stylu.

  • Vytvořte vazbu vlastnosti na zdroj dat.

  • Jako hodnotu vlastnosti použijte dynamický prostředek.

  • Animujte vlastnost.

Pokud chcete, aby ovládací prvek podporoval některou z těchto funkcí, měli byste ji implementovat jako vlastnost závislosti. Následující příklad definuje vlastnost závislosti pojmenovanou Value následujícím způsobem:

  • Definujte identifikátor pojmenovaný DependencyProperty static ValueProperty public readonly jako pole.

  • Zaregistrujte název vlastnosti v systému vlastností voláním DependencyProperty.Register, zadejte následující:

  • Definujte vlastnost obálky CLR s názvem Value, což je stejný název, který se používá k registraci vlastnosti závislosti implementací vlastnosti a set přístupových objektů vlastnostiget. Všimněte si, že a get set přístupové objekty volají GetValue pouze a SetValue v uvedeném pořadí. Doporučuje se, aby přístupové objekty vlastností závislostí neobsahují další logiku, protože klienti a WPF mohou obejít přístupové objekty a volat GetValue a SetValue přímo. Pokud je například vlastnost svázaná se zdrojem dat, není volána přístupová set položka vlastnosti. Místo přidání další logiky do přístupových objektů get a set použijte ValidateValueCallbackpříkazy , CoerceValueCallbacka PropertyChangedCallback delegáty k odpovídání nebo kontrole hodnoty při změně. Další informace o těchto zpětných voláních naleznete v tématu Zpětné volání vlastností závislostí a ověřování.

  • Definujte metodu pro pojmenovanou CoerceValueCallback CoerceValue. CoerceValue zajišťuje, aby Value byla větší nebo rovna MinValue a menší než nebo rovna MaxValue.

  • Definujte metodu pro pojmenovanou PropertyChangedCallbackOnValueChanged. OnValueChangedRoutedPropertyChangedEventArgs<T> vytvoří objekt a připraví na vyvolání ValueChanged směrované události. Směrované události jsou popsány v další části.

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

Další informace naleznete v tématu Vlastní vlastnosti závislostí.

Použití směrovaných událostí

Stejně jako vlastnosti závislostí rozšiřují pojem vlastností CLR o další funkce, směrované události rozšiřují pojem standardních událostí CLR. Při vytváření nového ovládacího prvku WPF je také vhodné implementovat událost jako směrovanou událost, protože směrovaná událost podporuje následující chování:

  • Události je možné zpracovat u nadřazeného prvku více ovládacích prvků. Pokud je událost bublinovou událostí, může se jedna nadřazená položka ve stromu elementu přihlásit k odběru události. Autoři aplikací pak můžou pomocí jedné obslužné rutiny reagovat na událost více ovládacích prvků. Pokud je například ovládací prvek součástí každé položky v objektu ListBox (protože je součástí DataTemplate), může vývojář aplikace definovat obslužnou rutinu události události pro událost ovládacího prvku v objektu ListBox. Pokaždé, když dojde k události u některého z ovládacích prvků, je volána obslužná rutina události.

  • Směrované události lze použít v objektu EventSetter, který vývojářům aplikací umožňuje určit obslužnou rutinu události ve stylu.

  • Směrované události lze použít v objektu EventTrigger, který je užitečný pro animaci vlastností pomocí XAML. Další informace najdete v přehledu animace.

Následující příklad definuje směrovanou událost následujícím způsobem:

  • Definujte identifikátor pojmenovaný RoutedEvent static ValueChangedEvent public readonly jako pole.

  • Zaregistrujte směrovanou událost voláním EventManager.RegisterRoutedEvent metody. Příklad určuje následující informace při volání RegisterRoutedEvent:

    • Název události je ValueChanged.

    • Strategie směrování je , což znamená, že obslužná rutina události ve zdroji (objekt, který vyvolá událost) je Bubblevolána jako první a potom obslužné rutiny událostí v nadřazených elementech zdroje jsou volány postupně, počínaje obslužnou rutinou události u nejbližšího nadřazeného elementu.

    • Typ obslužné rutiny události je RoutedPropertyChangedEventHandler<T>vytvořen s typem Decimal .

    • Vlastnící typ události je NumericUpDown.

  • Deklarujte veřejnou událost s názvem ValueChanged a zahrnuje deklarace objektu event-accessor. Příklad volání AddHandler v deklaraci přístupového objektu add a RemoveHandler v deklaraci přístupového objektu remove pro použití služby událostí WPF.

  • Vytvořte chráněnou virtuální metodu s názvem OnValueChanged , která vyvolá ValueChanged událost.

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

Další informace najdete v tématu Přehled směrovaných událostí a vytvoření vlastní směrované události.

Použití vazby

Pokud chcete oddělit uživatelské rozhraní ovládacího prvku od logiky, zvažte použití datové vazby. To je zvlášť důležité, pokud definujete vzhled ovládacího prvku pomocí .ControlTemplate Při použití datové vazby může být možné eliminovat potřebu odkazovat na konkrétní části uživatelského rozhraní z kódu. Je vhodné se vyhnout odkazování prvků, které jsou v ControlTemplate tom, protože když kód odkazuje na elementy, které jsou v ControlTemplate a je ControlTemplate změněn, odkazovaný prvek musí být zahrnut do nové ControlTemplate.

Následující příklad aktualizuje TextBlock NumericUpDown ovládací prvek, přiřadí mu název a odkazuje na textové pole podle názvu v kódu.

<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

Následující příklad používá vazbu k dosažení stejné věci.

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

Další informace o datových vazbách najdete v přehledu datových vazeb.

Návrh pro návrháře

Pokud chcete získat podporu vlastních ovládacích prvků WPF v Návrháři WPF pro Visual Studio (například úpravy vlastností pomocí okno Vlastnosti), postupujte podle těchto pokynů. Další informace o vývoji návrháře WPF naleznete v tématu Návrh XAML v sadě Visual Studio.

Vlastnosti závislosti

Nezapomeňte implementovat CLR get a set přístupové objekty, jak je popsáno výše v části Použití vlastností závislosti. Návrháři mohou obálku použít ke zjištění přítomnosti vlastnosti závislosti, ale nemusí, jako je WPF a klienti ovládacího prvku, volat přístupové objekty při získávání nebo nastavení vlastnosti.

Přidružené vlastnosti

Připojené vlastnosti byste měli implementovat ve vlastních ovládacích prvcích pomocí následujících pokynů:

  • public static DependencyProperty readonly Mít formulář PropertyNameProperty, který byl vytvářen pomocí RegisterAttached metody. Název vlastnosti, který je předán RegisterAttached , musí odpovídat PropertyName.

  • Implementujte dvojici public static metod CLR s názvemSet PropertyName a GetPropertyName. Obě metody by měly přijmout třídu odvozenou z DependencyProperty prvního argumentu. Metoda SetPropertyName také přijímá argument, jehož typ odpovídá registrovanému datovému typu pro vlastnost. Metoda GetPropertyName by měla vrátit hodnotu stejného typu. SetPokud metoda PropertyName chybí, je vlastnost označena jen pro čtení.

  • SetPropertyName a GetPropertyName musí směrovat přímo na GetValue objekt cílové závislosti a SetValue metody. Návrháři mohou přistupovat k připojené vlastnosti voláním obálky metody nebo provedením přímého volání cílového objektu závislosti.

Další informace o připojených vlastnostech naleznete v části Přehled připojených vlastností.

Definování a používání sdílených prostředků

Ovládací prvek můžete zahrnout do stejného sestavení jako vaše aplikace, nebo můžete ovládací prvek zabalit do samostatného sestavení, které lze použít ve více aplikacích. Ve většině případů platí informace popsané v tomto tématu bez ohledu na metodu, kterou používáte. Je však třeba poznamenat jeden rozdíl. Když umístíte ovládací prvek do stejného sestavení jako aplikace, můžete do souboru App.xaml přidat globální prostředky. Sestavení, které obsahuje pouze ovládací prvky, ale nemá Application k němu přidružený objekt, takže soubor App.xaml není k dispozici.

Když aplikace hledá prostředek, podívá se na tři úrovně v následujícím pořadí:

  1. Úroveň elementu.

    Systém začíná prvkem, který odkazuje na prostředek, a pak prohledá prostředky logického nadřazeného objektu a tak dále, dokud nebude dosaženo kořenového prvku.

  2. Úroveň aplikace.

    Prostředky definované objektem Application .

  3. Úroveň motivu.

    Slovníky na úrovni motivu se ukládají do podsložky s názvem Motivy. Soubory ve složce Motivy odpovídají motivům. Můžete mít například Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml atd. Můžete mít také soubor s názvem generic.xaml. Když systém hledá prostředek na úrovni motivů, nejprve ho vyhledá v souboru specifickém pro motiv a pak ho vyhledá v generic.xaml.

Pokud je ovládací prvek v sestavení, které je oddělené od aplikace, musíte globální prostředky umístit na úroveň prvku nebo na úrovni motivu. Obě metody mají své výhody.

Definování prostředků na úrovni elementu

Sdílené prostředky můžete definovat na úrovni elementu tak, že vytvoříte vlastní slovník prostředků a sloučíte ho se slovníkem prostředků ovládacího prvku. Když použijete tuto metodu, můžete soubor prostředků pojmenovat libovolným způsobem a může být ve stejné složce jako ovládací prvky. Prostředky na úrovni elementu mohou také používat jednoduché řetězce jako klíče. Následující příklad vytvoří LinearGradientBrush soubor prostředku s názvem Dictionary1.xaml.

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

Jakmile slovník definujete, musíte ho sloučit se slovníkem prostředků ovládacího prvku. Můžete to provést pomocí XAML nebo kódu.

Následující příklad sloučí slovník prostředků pomocí XAML.

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

Nevýhodou tohoto přístupu je, že se ResourceDictionary objekt vytvoří pokaždé, když na něj odkazujete. Pokud máte například v knihovně 10 vlastních ovládacích prvků a sloučíte slovníky sdílených prostředků pro každý ovládací prvek pomocí XAML, vytvoříte 10 identických ResourceDictionary objektů. Můžete tomu zabránit vytvořením statické třídy, která sloučí prostředky v kódu a vrátí výsledek ResourceDictionary.

Následující příklad vytvoří třídu, která vrací sdílený ResourceDictionary.

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;
}

Následující příklad slučuje sdílený prostředek s prostředky vlastního ovládacího prvku v konstruktoru ovládacího prvku před voláním InitializeComponent. SharedDictionaryManager.SharedDictionary Vzhledem k tomu, že je statická vlastnost, je ResourceDictionary vytvořena pouze jednou. Vzhledem k tomu, že se slovník prostředků před InitializeComponent zavolání sloučil, jsou prostředky k dispozici ovládacímu prvku v jeho souboru XAML.

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

Definování prostředků na úrovni motivu

WPF umožňuje vytvářet prostředky pro různé motivy Windows. Jako autor ovládacího prvku můžete definovat prostředek pro konkrétní motiv a změnit vzhled ovládacího prvku v závislosti na použitém motivu. Vzhled klasického Button motivu Systému Windows (výchozí motiv pro Systém Windows 2000) se například liší od Button motivu Windows Luna (výchozí motiv pro Systém Windows XP), protože Button pro každý motiv se používá jiný ControlTemplate motiv.

Prostředky specifické pro motiv se uchovávají ve slovníku prostředků s konkrétním názvem souboru. Tyto soubory musí být ve složce s názvem Themes , která je podsložkou složky, která obsahuje ovládací prvek. Následující tabulka uvádí soubory slovníku prostředků a motiv přidružený k jednotlivým souborům:

Název souboru slovníku prostředků Motiv Windows
Classic.xaml Klasický vzhled systému Windows 9x/2000 v systému Windows XP
Luna.NormalColor.xaml Výchozí modrý motiv v systému Windows XP
Luna.Homestead.xaml Olivový motiv v systému Windows XP
Luna.Metallic.xaml Stříbrný motiv ve Windows XP
Royale.NormalColor.xaml Výchozí motiv v systému Windows XP Media Center Edition
Aero.NormalColor.xaml Výchozí motiv v systému Windows Vista

Nemusíte definovat prostředek pro každý motiv. Pokud prostředek není definován pro konkrétní motiv, ovládací prvek zkontroluje Classic.xaml prostředek. Pokud prostředek není definován v souboru, který odpovídá aktuálnímu motivu nebo v Classic.xaml, ovládací prvek používá obecný prostředek, který je v souboru slovníku prostředků s názvem generic.xaml. Soubor generic.xaml se nachází ve stejné složce jako soubory slovníku prostředků specifické pro motiv. I když generic.xaml neodpovídá konkrétnímu motivu Windows, je to stále slovník na úrovni motivu.

Vlastní ovládací prvek C# nebo Visual Basic NumericUpDown s motivem a ukázkou podpory automatizace uživatelského rozhraní obsahuje dva slovníky NumericUpDown prostředků ovládacího prvku: jeden je v generic.xaml a druhý je v Luna.NormalColor.xaml.

Když vložíte ControlTemplate do některého ze souborů slovníku prostředků specifických pro motiv, musíte vytvořit statický konstruktor pro váš ovládací prvek a volat metodu OverrideMetadata(Type, PropertyMetadata) na , jak je znázorněno v následujícím příkladu DefaultStyleKey.

static NumericUpDown()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Definování a odkazování klíčů pro prostředky motivu

Když definujete prostředek na úrovni elementu, můžete řetězec přiřadit jako jeho klíč a získat přístup k prostředku prostřednictvím řetězce. Když definujete prostředek na úrovni motivu, musíte jako klíč použít ComponentResourceKey . Následující příklad definuje prostředek v generic.xaml.

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

Následující příklad odkazuje na prostředek zadáním ComponentResourceKey klíče.

<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>
Určení umístění prostředků motivu

Pokud chcete najít prostředky pro ovládací prvek, musí hostitelská aplikace vědět, že sestavení obsahuje prostředky specifické pro řízení. Toho lze dosáhnout přidáním ThemeInfoAttribute do sestavení, které obsahuje ovládací prvek. GenericDictionaryLocationThemeInfoAttribute vlastnost, která určuje umístění obecných prostředků a ThemeDictionaryLocation vlastnost, která určuje umístění prostředků specifických pro motiv.

Následující příklad nastaví GenericDictionaryLocation a ThemeDictionaryLocation vlastnosti na SourceAssembly, určit, že obecné a motiv-specifické prostředky jsou ve stejném sestavení jako ovládací prvek.

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

Viz také