Přehled tvorby ovládacího prvku

rozšiřitelnost modelu řízení Windows Presentation Foundation (WPF) významně snižuje nutnost vytvoření nového ovládacího prvku. V některých případech však můžete stále potřebovat 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ů v 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 si chtěli přizpůsobené prostředí získat z existujícího ovládacího prvku, bylo omezeno na změnu standardních vlastností ovládacího prvku, jako je barva pozadí, Šířka ohraničení a velikost písma. Pokud jste chtěli zvětšit vzhled nebo chování ovládacího prvku nad rámec těchto předdefinovaných parametrů, je nutné 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 vám umožní přizpůsobit existující ovládací prvky pomocí modelu bohatých obsahu, stylů, šablon a triggerů. Následující seznam obsahuje příklady, jak lze tyto funkce použít k vytvoření vlastních a konzistentních prostředí, aniž byste museli vytvořit nový ovládací prvek.

  • Bohatě bohatý obsah. Mnohé z standardních ovládacích prvků WPF podporují bohatou obsah. Například vlastnost Button Content typu je typu Object , takže teoreticky může být zobrazeno na Button . Chcete-li zobrazit tlačítko s obrázkem a textem, můžete přidat obrázek a TextBlock k StackPanel vlastnosti a přiřadit StackPanelContent ji. Vzhledem k tomu, že ovládací prvky mohou zobrazovat vizuální prvky WPF a libovolná data, je méně nutné vytvořit nový ovládací prvek nebo upravit existující ovládací prvek pro podporu komplexní vizualizace. Další informace o modelu obsahu pro Button a dalších modelech obsahu v technologii WPF naleznete v tématu model obsahu WPF.

  • Nadpis. StyleJe kolekce hodnot, které reprezentují vlastnosti ovládacího prvku. Pomocí stylů lze vytvořit opakovaně použitelná reprezentace 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 Font s velikostí písma 14. Můžete vytvořit styl jako prostředek a odpovídajícím způsobem nastavit odpovídající vlastnosti. Každý TextBlock , který přidáte do vaší aplikace, bude mít stejný vzhled.

  • Datové šablony. DataTemplateUmožňuje přizpůsobit způsob zobrazení dat na ovládacím prvku. Například DataTemplate lze použít k určení způsobu zobrazení dat v ListBox . Příklad najdete v tématu Přehled šablonování dat. Kromě přizpůsobení vzhledu dat DataTemplate může přidat prvky uživatelského rozhraní, které vám poskytnou značnou flexibilitu ve vlastních uživatelská rozhraní. Například DataTemplate pomocí můžete vytvořit ComboBox , kde každá položka obsahuje zaškrtávací políčko.

  • Šablony ovládacích prvků. Mnoho ovládacích prvků v 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ýznamně změnit tak, že ho převedete podle definice 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 kroužky, pouze jeden z nich lze rozsvítit současně. Po určité reflexi se můžete setkat s tím, že RadioButton nabízí funkce pouze jednoho výběru, ale výchozí vzhled RadioButton indikátoru nevypadá jako světla v semaforu. RadioButtonVzhledem k tomu, že používá šablonu ovládacího prvku k definování jeho vzhledu, je snadné ho předefinovat ControlTemplate tak, aby odpovídal požadavkům ovládacího prvku, a použít přepínače k tomu, aby byl semafor.

    Poznámka

    RadioButtonI když může používat DataTemplate , DataTemplate není v tomto příkladu dostačující. DataTemplateDefinuje vzhled obsahu ovládacího prvku. V případě a RadioButton se obsah zobrazuje napravo od kruhu, který označuje, zda RadioButton je vybrána položka. Přepínač v příkladu semaforu potřebuje jenom kruh, který může "světlo". Vzhledem k tomu, že je požadavek na zobrazení semaforu jiný než výchozí vzhled RadioButton , je nutné předefinovat ControlTemplate . Obecně DataTemplate se používá pro definování obsahu (nebo dat) ovládacího prvku a ControlTemplate slouží k definování způsobu, jakým je ovládací prvek strukturovaný.

  • Zpráv. TriggerUmožň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 byly položky v každém z nich ListBox tučné a červené při jejich výběru. První Instinct může být vytvořit třídu, která dědí z ListBox a přepsat OnSelectionChanged metodu pro změnu vzhledu vybrané položky, ale lepším přístupem je přidat Trigger do stylu ListBoxItem , který změní vzhled vybrané položky. Aktivační událost umožňuje změnit hodnoty vlastností nebo provést akce na základě hodnoty vlastnosti. EventTriggerUmožňuje provést akce, když dojde k události.

Další informace o stylech, šablonách a triggerech naleznete v tématu stylování and šablonování.

Obecně platí, že pokud váš ovládací prvek zrcadlí funkčnost stávajícího ovládacího prvku, ale chcete, aby ovládací prvek vypadal jinak, měli byste nejprve zvážit, zda můžete použít kteroukoli z metod popsaných v této části, chcete-li změnit vzhled stávajícího ovládacího prvku.

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

Bohatě vytvořený model obsahu, styly, šablony a triggery minimalizují nutnost vytvoření nového ovládacího prvku. Pokud však potřebujete vytvořit nový ovládací prvek, je důležité pochopit různé modely vytváření ovládacích prvků v WPF. WPF poskytuje tři obecné modely pro vytvoření ovládacího prvku, z nichž každý nabízí různé sady funkcí a úrovně flexibility. Základní třídy pro tři modely jsou UserControl , Control a FrameworkElement .

Odvození od prvku UserControl

Nejjednodušší způsob, jak vytvořit ovládací prvek v subsystému WPF, je odvozovat z UserControl . Při sestavování ovládacího prvku, který dědí z UserControl , můžete přidat existující součásti do UserControl , pojmenovat komponenty a odkazovat na obslužné rutiny událostí v subsystému WPF.

V případě, že je UserControl správně sestaven, může využít výhody bohatě formátovaného obsahu, stylů a triggerů. Nicméně pokud váš ovládací prvek dědí z UserControl , uživatelé, kteří používají ovládací prvek, nebudou moci použít DataTemplate nebo ControlTemplate k přizpůsobení jeho vzhledu. Aby bylo možné vytvořit vlastní ovládací prvek, který podporuje šablony, je nutné odvozovat z Control třídy nebo jedné z jeho odvozených tříd (jiné než UserControl ).

Výhody vyplývající z prvku UserControl

Zvažte odvození z UserControl následujících podmínek:

  • Chcete sestavit svůj ovládací prvek podobně jako při sestavování aplikace.

  • Váš ovládací prvek se skládá pouze ze stávajících součástí.

  • Nemusíte podporovat složité přizpůsobení.

Odvození od ovládacího prvku

Odvození od Control třídy je model používaný většinou z existujících ovládacích prvků WPF. Když vytvoříte ovládací prvek, který dědí z Control třídy, definujete jeho vzhled pomocí šablon. Díky tomu oddělíte provozní logiku od vizuální reprezentace. Můžete také zajistit oddálení uživatelského rozhraní a logiky pomocí příkazů a vazeb namísto událostí a vyhnutí se odkazování na prvky, kdykoli je ControlTemplate to možné. Pokud je uživatelské rozhraní a logika vašeho ovládacího prvku správně oddělitelné, uživatel vašeho ovládacího prvku může předefinovat svůj ovládací prvek ControlTemplate a přizpůsobit jeho vzhled. I když sestavování vlastního Control není jednoduché jako sestavování a UserControl , vlastní Control možnost poskytuje největší flexibilitu.

Výhody vyplývající z řízení

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

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

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

Odvození od prvku FrameworkElement

Ovládací prvky, které jsou odvozeny z UserControl nebo Control spoléhají na vytváření existujících prvků. Pro mnoho scénářů se jedná o přijatelné řešení, protože libovolný objekt, který dědí z FrameworkElement , může být v ControlTemplate . Existují však situace, kdy vzhled ovládacího prvku vyžaduje více než funkce jednoduchého složení prvku. V těchto scénářích je vhodná volba pro založení komponenty na FrameworkElement .

Existují dvě standardní metody pro stavební FrameworkElement komponenty: přímé vykreslování a vlastní kompozici prvků. Přímé vykreslování zahrnuje přepsání OnRender metody FrameworkElement a poskytování DrawingContext operací, které explicitně definují vizuály komponenty. Toto je metoda, kterou používá Image a Border . Vlastní složení elementu zahrnuje použití objektů typu Visual k vytvoření vzhledu vaší komponenty. Příklad najdete v tématu použití objektů DrawingVisual. Track je příkladem ovládacího prvku v WPF, který používá vlastní kompozici prvků. Je také možné kombinovat přímé vykreslování a vlastní kompozici prvků ve stejném ovládacím prvku.

Výhody vyplývající z objektu FrameworkElement

Zvažte odvození z FrameworkElement , pokud platí některá z následujících podmínek:

  • Chcete mít přesnou kontrolu nad zobrazením ovládacího prvku nad rámec toho, co je k dispozici v jednoduchém složení prvku.

  • Chcete definovat vzhled ovládacího prvku definováním vlastní logiky vykreslování.

  • Chcete vytvořit existující prvky novými způsoby, které přesahují rámec toho, co je možné s UserControl a Control .

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

Jak už bylo zmíněno dříve, jedna z nejúčinnějších funkcí WPF je možnost jít nad rámec nastavení základních vlastností ovládacího prvku, aby se změnil jeho vzhled a chování, ale stále není nutné vytvářet vlastní ovládací prvek. Styly, datové vazby a aktivační funkce jsou umožněny 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žijete k vytvoření vlastního ovládacího prvku, aby uživatelé vlastního ovládacího prvku mohli tyto funkce používat stejně jako ovládací prvek, který je součástí WPF.

Použít vlastnosti závislosti

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

  • Nastavte vlastnost ve stylu.

  • Navažte vlastnost na zdroj dat.

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

  • Animovat vlastnost.

Pokud chcete, aby vlastnost vašeho ovládacího prvku podporovala tuto funkci, měli byste ji implementovat jako vlastnost závislosti. Následující příklad definuje vlastnost závislosti s názvem Value následujícím způsobem:

  • DependencyPropertyDefinujte identifikátor s názvem ValueProperty jako publicstaticreadonly pole.

  • Zaregistrujte název vlastnosti se systémem vlastností zavoláním metody DependencyProperty.Register , abyste určili 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í a set přístupových objektů vlastnosti get . Všimněte si, že přístupové objekty a set jsou volány SetValueGetValue pouze v get uvedeném pořadí. Doporučuje se, aby přistupující objekty vlastností závislosti neobsahovaly další logiku, protože klienti a WPF mohou obejít přístupové objekty a volat GetValue a SetValue přímo. Například pokud je vlastnost svázána se zdrojem dat, přístup k vlastnosti set není volán. Namísto přidání další logiky do přístupových objektů Get a set použijte ValidateValueCallback delegáty, CoerceValueCallback a PropertyChangedCallback k reakci na nebo kontrolu hodnoty, když se změní. Další informace o těchto zpětných voláních naleznete v tématu zpětná volání vlastností závislosti a ověřování.

  • Definujte metodu pro CoerceValueCallback pojmenovaný CoerceValue . CoerceValue zajistí, že Value je větší nebo rovno MinValue a menší nebo rovno MaxValue .

  • Definujte metodu pro PropertyChangedCallback , s názvem OnValueChanged . OnValueChangedRoutedPropertyChangedEventArgs<T>vytvoří objekt a připraví na vyvolání ValueChanged směrované události. Směrované události jsou popsány v následující čá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 najdete v tématu vlastnosti vlastních závislostí.

Použít směrované události

Stejně jako vlastnosti závislosti rozšíří pojem vlastností CLR s dalšími funkcemi, směrované události rozšíří 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 lze zpracovat v nadřazeném více ovládacích prvků. Pokud je událost probublávání události, může se jedna nadřazená položka ve stromu elementu přihlásit k odběru události. Autoři aplikací potom mohou použít jednu obslužnou rutinu k reakci na událost více ovládacích prvků. Například pokud je váš ovládací prvek součástí každé položky v ListBox (protože je součástí DataTemplate ), může vývojář aplikace definovat obslužnou rutinu události pro událost ovládacího prvku v ListBox . Pokaždé, když dojde k události v jakémkoli ovládacím prvku, je volána obslužná rutina události.

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

  • Směrované události lze použít v EventTrigger , což je užitečné pro animování vlastností pomocí jazyka XAML. Další informace najdete v tématu Přehled animací.

Následující příklad definuje směrnou událost pomocí následujícího postupu:

  • RoutedEventDefinujte identifikátor s názvem ValueChangedEvent jako publicstaticreadonly 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 Bubble , což znamená, že obslužná rutina události na zdroji (objekt, který vyvolává událost) je volána jako první, a poté jsou obslužné rutiny události v nadřazených prvcích zdroje volány po úspěchu, počínaje obslužnou rutinou události u nejbližšího nadřazeného prvku.

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

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

  • Deklarace veřejné události s názvem ValueChanged a zahrnutí deklarací přistupujícího objektu na událost. Příklad volá AddHandleradd deklaraci přistupujícího objektu a RemoveHandler v remove deklaraci přistupujícího objektu pro použití služeb událostí WPF.

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

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

Chcete-li oddělit uživatelské rozhraní vašeho ovládacího prvku od jeho logiky, zvažte použití datové vazby. To je obzvláště důležité, pokud definujete vzhled ovládacího prvku pomocí ControlTemplate . Pokud používáte datovou vazbu, může být možné eliminovat potřebu odkazů na konkrétní části uživatelského rozhraní z kódu. Je vhodné vyhnout se odkazování na prvky, které jsou v rozhraní ControlTemplate , protože když kód odkazuje na elementy, které jsou v ControlTemplate a ControlTemplate , je nutné, aby byl odkazovaný element součástí nového ControlTemplate .

Následující příklad aktualizuje TextBlockNumericUpDown 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 tomu, aby provede stejnou věc.

<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é vazbě najdete v tématu Přehled datových vazeb.

Návrh pro návrháře

chcete-li získat podporu pro vlastní ovládací prvky 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 pro návrháře WPF naleznete v tématu Design XAML in Visual Studio.

Vlastnosti závislosti

Nezapomeňte implementovat CLR get a set přistupující objekty, jak je popsáno výše v části "použití vlastností závislosti". Návrháři mohou použít obálku k detekci přítomnosti vlastnosti závislosti, ale jako například WPF a klienti ovládacího prvku, nejsou při získávání nebo nastavování vlastnosti vyžadovány.

Přidružené vlastnosti

K vlastním ovládacím prvkům byste měli implementovat připojené vlastnosti pomocí následujících pokynů:

  • publicstaticDependencyPropertyreadonly Mít formu PropertyNameProperty , která byla vytvořena pomocí RegisterAttached metody. Název vlastnosti, která je předána RegisterAttached , musí odpovídat hodnotě PropertyName.

  • Implementujte dvojici publicstatic metod CLR s názvem SetPropertyName a GetPropertyName. Obě metody by měly přijmout třídu odvozenou z DependencyProperty jako jejich první argument. SetMetoda PropertyName také přijímá argument, jehož typ odpovídá zaregistrovanému datovému typu pro vlastnost. GetMetoda PropertyName by měla vracet hodnotu stejného typu. SetPokud metoda PropertyName chybí, vlastnost je označena jen pro čtení.

  • SetHodnoty PropertyName a GetPropertyName musí směrovat přímo GetValue na metody a SetValue cílového objektu závislosti v uvedeném pořadí. Návrháři mohou přistupovat k vlastnosti připojené voláním prostřednictvím obálky metody nebo přímým voláním cílového objektu závislosti.

Další informace o připojených vlastnostech najdete v tématu Přehled připojených vlastností.

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

Můžete zahrnout svůj ovládací prvek do stejného sestavení jako aplikaci nebo můžete zabalit ovládací prvek v samostatném sestavení, které lze použít ve více aplikacích. Ve většině případů se informace popsané v tomto tématu vztahují bez ohledu na metodu, kterou používáte. Je však potřeba poznamenat jednu rozdílovou hodnotu. Po vložení ovládacího prvku 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 Application , však nemá přidružený objekt, takže soubor App. XAML není k dispozici.

Když aplikace vyhledá prostředek, prohledá tři úrovně v tomto pořadí:

  1. Úroveň elementu.

    Systém se spustí s prvkem, který odkazuje na prostředek, a poté prohledá prostředky logického nadřazeného objektu a tak dále, dokud není dosaženo kořenového prvku.

  2. Úroveň aplikace.

    Prostředky definované Application objektem.

  3. Úroveň motivu.

    Slovníky na úrovni motivu jsou uloženy v podsložce s názvem themes. Soubory ve složce Themes odpovídají motivům. Například můžete mít Aero. NormalColor. XAML, Luna. NormalColor. XAML, Royale. NormalColor. XAML a tak dále. Můžete mít také soubor s názvem Generic. XAML. Když systém vyhledá prostředek na úrovni motivů, nejprve ho vyhledá v souboru specifickém pro motiv a pak ho vyhledá v souboru Generic. XAML.

Když je ovládací prvek v sestavení, které je oddělené od aplikace, je nutné umístit globální prostředky na úrovni 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 vytvořením vlastního slovníku prostředků a jeho sloučením do slovníku prostředků vašeho ovládacího prvku. Když použijete tuto metodu, můžete si pojmenovat soubor prostředků cokoli, co potřebujete, a může být ve stejné složce jako vaše ovládací prvky. Prostředky na úrovni elementu můžou jako klíče používat taky jednoduché řetězce. Následující příklad vytvoří LinearGradientBrush soubor prostředků 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>

Po definování slovníku je potřeba ho sloučit se slovníkem prostředků vašeho 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í jazyka XAML.

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

Nevýhodou tohoto přístupu je, že ResourceDictionary objekt je vytvořen pokaždé, když na něj odkazujete. Například pokud máte v knihovně 10 vlastních ovládacích prvků a sloučíte sdílené slovníky prostředků pro každý ovládací prvek pomocí jazyka XAML, vytvoříte 10 identických ResourceDictionary objektů. K tomu je možné se vyhnout 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í Shared 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 sloučí 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.SharedDictionaryVzhledem k tomu, že je statická vlastnost, ResourceDictionary je vytvořena pouze jednou. Vzhledem k tomu, že byl slovník prostředků sloučen před InitializeComponent voláním, jsou prostředky k dispozici pro ovládací prvek v 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é Windows motivy. Jako autor ovládacího prvku můžete definovat prostředek pro konkrétní motiv, který změní vzhled ovládacího prvku v závislosti na tom, jaký motiv se používá. například vzhled Button v motivu Windows Classic (výchozí motiv pro Windows 2000) se liší od a Button v motivu Windows Luna (výchozí motiv pro Windows XP), Button protože používá jiný ControlTemplate pro každý motiv.

Prostředky, které jsou specifické pro motiv, jsou uchovávány ve slovníku prostředků s určitý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. V následující tabulce jsou uvedeny soubory slovníku prostředků a motiv, který je k jednotlivým souborům přidružen:

Název souboru slovníku prostředků motiv Windows
Classic.xaml klasické Windows 9x/2000 – podívejte se na Windows XP
Luna.NormalColor.xaml výchozí modrý motiv na Windows XP
Luna.Homestead.xaml motiv olivového oleje na Windows XP
Luna.Metallic.xaml motiv stříbrného v Windows XP
Royale.NormalColor.xaml výchozí motiv v Windows XP Media Center Edition
Aero.NormalColor.xaml výchozí motiv v 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 . generic.xamlSoubor je umístěn 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 stále slovník na úrovni motivu.

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

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

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

Při definování prostředku na úrovni prvku můžete jako svůj klíč přiřadit řetězec a přistupovat k prostředku prostřednictvím řetězce. Při definování prostředku na úrovni motivu je nutné použít ComponentResourceKey jako klíč. Následující příklad definuje prostředek v souboru 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 ComponentResourceKey zadáním jako klíč.

<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

Chcete-li najít prostředky pro ovládací prvek, hostující aplikace musí znát, že sestavení obsahuje prostředky specifické pro ovládací prvek. To lze provést přidáním ThemeInfoAttribute do sestavení, které obsahuje ovládací prvek. ThemeInfoAttributeGenericDictionaryLocation 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.

V následujícím příkladu je nastavena GenericDictionaryLocation vlastnost a ThemeDictionaryLocation vlastností na SourceAssembly , k určení, zda jsou prostředky obecné a specifické pro motiv ve stejném sestavení jako ovládací prvek.

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

Viz také