Sdílet prostřednictvím


Styly a šablony ve WPF

Styling a šablonování Windows Presentation Foundation (WPF) odkazuje na sadu funkcí, které vývojářům a návrhářům umožňují vytvářet vizuálně působivé efekty a konzistentní vzhled jejich produktu. Při přizpůsobení vzhledu aplikace chcete použít silný model stylů a šablon, který umožňuje údržbu a sdílení vzhledu v aplikacích i mezi aplikacemi. WPF poskytuje tento model.

Další funkcí modelu stylů WPF je oddělení prezentace a logiky. Návrháři můžou pracovat na vzhledu aplikace jenom pomocí XAML současně, kdy vývojáři pracují na programovací logice pomocí jazyka C# nebo Visual Basic.

Tento přehled se zaměřuje na styly a šablonování aspektů aplikace a neprobírá žádné koncepty datových vazeb. Informace o datové vazbě naleznete v tématu Přehled datových vazeb.

Je důležité pochopit prostředky, které umožňují opakované použití stylů a šablon. Další informace o prostředcích naleznete v tématu prostředky XAML.

Ukázka

Ukázkový kód uvedený v tomto přehledu vychází z jednoduché aplikace pro procházení fotek znázorněné na následujícím obrázku.

Stylovaný ListView

Tato jednoduchá ukázka fotek používá styly a šablony k vytvoření vizuálně atraktivního uživatelského prostředí. Ukázka má dva TextBlock prvky a ovládací prvek ListBox, který je svázán se seznamem obrázků.

Kompletní ukázku najdete v tématu Úvod k ukázce stylů a šablon.

Styly

Style si můžete představit jako pohodlný způsob použití sady hodnot vlastností na více prvků. Styl můžete použít u libovolného prvku odvozeného z FrameworkElement nebo FrameworkContentElement, například Window nebo Button.

Nejběžnější způsob, jak deklarovat styl, je jako prostředek v oddílu Resources v souboru XAML. Vzhledem k tomu, že stylingové prvky jsou zdroje, dodržují stejná pravidla definičních oblastí, která platí pro všechny zdroje. Jednoduše řečeno, kde deklarujete styl, má vliv na to, kde se styl dá použít. Pokud například deklarujete styl v kořenovém prvku souboru XAML definice aplikace, můžete styl použít kdekoli v aplikaci.

Následující kód XAML například deklaruje dva styly pro TextBlock, jeden se automaticky použije pro všechny prvky TextBlock a druhý, na který se musí explicitně odkazovat.

<Window.Resources>
    <!-- .... other resources .... -->

    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
    
    <!--A Style that extends the previous TextBlock Style with an x:Key of TitleText-->
    <Style BasedOn="{StaticResource {x:Type TextBlock}}"
           TargetType="TextBlock"
           x:Key="TitleText">
        <Setter Property="FontSize" Value="26"/>
        <Setter Property="Foreground">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0.0" Color="#90DDDD" />
                        <GradientStop Offset="1.0" Color="#5BFFFF" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

Tady je příklad výše deklarovaných stylů.

<StackPanel>
    <TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

stylované bloky textu

ControlTemplates

Ve WPF definuje ControlTemplate ovládacího prvku vzhled ovládacího prvku. Strukturu a vzhled ovládacího prvku můžete změnit definováním nového ControlTemplate a jeho přiřazením k ovládacímu prvku. V mnoha případech vám šablony poskytují dostatečnou flexibilitu, abyste nemuseli psát vlastní ovládací prvky.

Každý ovládací prvek má k vlastnosti Control.Template přiřazenou výchozí šablonu. Šablona spojuje vizuální prezentaci ovládacího prvku s možnostmi ovládacího prvku. Vzhledem k tomu, že definujete šablonu v XAML, můžete změnit vzhled ovládacího prvku bez psaní kódu. Každá šablona je určena pro určitý ovládací prvek, například Button.

Obvykle deklarujete šablonu jako prostředek v části Resources souboru XAML. Stejně jako u všech zdrojů platí pravidla pro rozsah.

Šablony ovládacích prvků jsou mnohem složitější než styl. Důvodem je to, že šablona ovládacího prvku přepíše vzhled celého ovládacího prvku, zatímco styl jednoduše použije změny vlastností na existující ovládací prvek. Vzhledem k tomu, že se šablona ovládacího prvku použije nastavením vlastnosti Control.Template, můžete k definování nebo nastavení šablony použít styl.

Návrháři obecně umožňují vytvořit kopii existující šablony a upravit ji. Například v návrháři WPF sady Visual Studio vyberte ovládací prvek CheckBox a potom klikněte pravým tlačítkem myši a vyberte Upravit šablonu>Vytvořit kopii. Tento příkaz vygeneruje styl , který definuje šablonu.

<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
    <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual1}"/>
    <Setter Property="Background" Value="{StaticResource OptionMark.Static.Background1}"/>
    <Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border1}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type CheckBox}">
                <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                        <Grid x:Name="markGrid">
                            <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="1" Opacity="0" Stretch="None"/>
                            <Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="2" Opacity="0"/>
                        </Grid>
                    </Border>
                    <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasContent" Value="true">
                        <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual1}"/>
                        <Setter Property="Padding" Value="4,-1,0,0"/>

... content removed to save space ...

Úprava kopie šablony je skvělý způsob, jak se naučit, jak šablony fungují. Místo vytvoření nové prázdné šablony je jednodušší upravit kopii a změnit několik aspektů vizuální prezentace.

Příklad najdete v Vytvoření šablony pro ovládací.

TemplateBinding

Možná jste si všimli, že prostředek šablony definovaný v předchozí části používá označovací rozšíření TemplateBinding. TemplateBinding je optimalizovaná forma vazby pro scénáře šablony, podobně jako vazby vytvořené pomocí {Binding RelativeSource={RelativeSource TemplatedParent}}. TemplateBinding je užitečné pro vazby částí šablony s vlastnostmi ovládacího prvku. Každý ovládací prvek má například vlastnost BorderThickness. Pomocí TemplateBinding můžete spravovat, na který prvek v šabloně má toto nastavení ovládacího prvku vliv.

ContentControl a ItemsControl

Pokud je ContentPresenter deklarován v ControlTemplateContentControl, ContentPresenter se automaticky naváže na vlastnosti ContentTemplate a Content. Podobně se ItemsPresenter, která je uvnitř ControlTemplate něčeho ItemsControl, automaticky sváže s vlastnostmi ItemTemplate a Items.

Šablony dat

V této ukázkové aplikaci je ovládací prvek ListBox, který je vázán na seznam fotek.

<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
         Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

Tento ListBox aktuálně vypadá takto.

ListBox před použitím šablony

Většina ovládacích prvků má určitý typ obsahu a tento obsah často pochází z dat, ke kterým vytváříte vazbu. V této ukázce jsou data seznamem fotek. Ve WPF použijete DataTemplate k definování vizuální reprezentace dat. V podstatě to, co vložíte do DataTemplate určuje, jak vypadají data v vykreslené aplikaci.

V naší ukázkové aplikaci má každý přizpůsobený objekt Photo vlastnost Source typu 'řetězec', která určuje cestu k souboru s obrázkem. V současné době se objekty fotografií zobrazují jako cesty k souborům.

public class Photo
{
    public Photo(string path)
    {
        Source = path;
    }

    public string Source { get; }

    public override string ToString() => Source;
}
Public Class Photo
    Sub New(ByVal path As String)
        Source = path
    End Sub

    Public ReadOnly Property Source As String

    Public Overrides Function ToString() As String
        Return Source
    End Function
End Class

Aby se fotografie zobrazovaly jako obrázky, vytvoříte DataTemplate jako zdroj.

<Window.Resources>
    <!-- .... other resources .... -->

    <!--DataTemplate to display Photos as images
    instead of text strings of Paths-->
    <DataTemplate DataType="{x:Type local:Photo}">
        <Border Margin="3">
            <Image Source="{Binding Source}"/>
        </Border>
    </DataTemplate>
</Window.Resources>

Všimněte si, že vlastnost DataType je podobná vlastnosti TargetTypeStyle. Pokud je DataTemplate v oddílu prostředků, při specifikování vlastnosti DataType pro typ a vynechání x:Keyse DataTemplate použije pokaždé, když se daný typ objeví. Vždy máte možnost přiřadit k DataTemplate hodnotu x:Key a pak ho nastavit jako StaticResource pro vlastnosti, které vyžadují typy DataTemplate, jako je vlastnost ItemTemplate nebo vlastnost ContentTemplate.

V podstatě DataTemplate ve výše uvedeném příkladu definuje, že když je objekt Photo, měl by se objevit jako Image v rámci Border. S tímto DataTemplateteď naše aplikace vypadá takto.

fotografie

Model šablon dat poskytuje další funkce. Pokud například zobrazujete data kolekce, která obsahují jiné kolekce pomocí typu HeaderedItemsControl, například Menu nebo TreeView, je tam HierarchicalDataTemplate. Další funkcí šablonování dat je DataTemplateSelector, která umožňuje zvolit, které DataTemplate použít na základě vlastní logiky. Další informace najdete v tématu Přehled šablon dat, který poskytuje podrobnější diskuzi o různých funkcích šablon dat.

Spouštěče

Trigger nastaví vlastnosti nebo spustí akce, jako je animace, když se změní hodnota vlastnosti nebo při vyvolání události. Style, ControlTemplatea DataTemplate všechny mají vlastnost Triggers, která může obsahovat sadu triggerů. Existuje několik typů triggerů.

PropertyTriggers

Trigger, která nastavuje hodnoty vlastností nebo spouští akce na základě hodnoty vlastnosti, se nazývá aktivační událost vlastnosti.

Abyste ukázali, jak používat triggery vlastností, můžete každou ListBoxItem udělat částečně průhlednou, pokud není vybraná. Následující styl nastaví hodnotu Opacity u ListBoxItem na 0.5. Pokud je vlastnost IsSelectedtrue, je však Opacity nastavena na 1.0.

<Window.Resources>
    <!-- .... other resources .... -->

    <Style TargetType="ListBoxItem">
        <Setter Property="Opacity" Value="0.5" />
        <Setter Property="MaxHeight" Value="75" />
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Trigger.Setters>
                    <Setter Property="Opacity" Value="1.0" />
                </Trigger.Setters>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

Tento příklad používá Trigger k nastavení hodnoty vlastnosti, ale všimněte si, že třída Trigger má také EnterActions a ExitActions vlastnosti, které umožňují triggeru provádět akce.

Všimněte si, že vlastnost MaxHeight objektu ListBoxItem je nastavena na 75. Na následujícím obrázku je třetí položka vybranou položkou.

Stylizovaný ListView

Spouštěče událostí a scénáře

Dalším typem triggeru je EventTrigger, který spouští sadu akcí na základě výskytu události. Například následující EventTrigger objekty určují, že když ukazatel myši vstoupí do ListBoxItem, vlastnost MaxHeight se mění na hodnotu 90 během období 0.2 sekund. Když se myš přesune od položky, vrátí se vlastnost k původní hodnotě za období 1 sekundu. Všimněte si, že není nutné zadat hodnotu To animace MouseLeave. Je to proto, že animace dokáže sledovat původní hodnotu.

<Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
        <Trigger.Setters>
            <Setter Property="Opacity" Value="1.0" />
        </Trigger.Setters>
    </Trigger>
    <EventTrigger RoutedEvent="Mouse.MouseEnter">
        <EventTrigger.Actions>
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                        Duration="0:0:0.2"
                        Storyboard.TargetProperty="MaxHeight"
                        To="90"  />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Mouse.MouseLeave">
        <EventTrigger.Actions>
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                        Duration="0:0:1"
                        Storyboard.TargetProperty="MaxHeight"  />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger.Actions>
    </EventTrigger>
</Style.Triggers>

Další informace najdete v přehledu scénářů .

Na následujícím obrázku myš ukazuje na třetí položku.

ukázkový snímek obrazovky s ukázkou stylů

MultiSpouštěče, DataSpouštěče a MultiDataSpouštěče

Kromě Trigger a EventTriggerexistují i další typy triggerů. MultiTrigger umožňuje nastavit hodnoty vlastností na základě více podmínek. Pokud je vlastnost podmínky svázaná s daty, použijete DataTrigger a MultiDataTrigger.

Vizuální stavy

Ovládací prvky jsou vždy ve stavu . Například když se myš pohybuje nad povrchem ovládacího prvku, je ovládací prvek považován za běžný stav MouseOver. Ovládací prvek bez konkrétního stavu se považuje za ten, který je ve stavu Normal, označovaném jako společný. Státy jsou rozděleny do skupin a dříve uvedené státy jsou součástí skupiny států CommonStates. Většina ovládacích prvků má dvě skupiny stavů: CommonStates a FocusStates. Z každé skupiny stavů použité na ovládací prvek je ovládací prvek vždy v jednom stavu každé skupiny, například CommonStates.MouseOver a FocusStates.Unfocused. Ovládací prvek však nemůže být ve dvou různých stavech ve stejné skupině, například CommonStates.Normal a CommonStates.Disabled. Tady je tabulka stavů, které většina ovládacích prvků rozpozná a používá.

Název VisualState Název skupiny VisualStateGroup Popis
Normal CommonStates Výchozí stav.
MouseOver CommonStates Ukazatel myši se umístí nad ovládací prvek.
Pressed CommonStates Ovládací prvek se stiskne.
Disabled CommonStates Ovládací prvek je zakázaný.
Focused FocusStates Ovládací prvek má fokus.
Unfocused FocusStates Ovládací prvek nemá fokus.

Definováním System.Windows.VisualStateManager na kořenovém prvku šablony ovládacího prvku můžete aktivovat animace, když ovládací prvek přejde do určitého stavu. VisualStateManager deklaruje, které kombinace VisualStateGroup a VisualState je třeba sledovat. Když ovládací prvek přejde do sledovaného stavu, spustí se animace definovaná VisualStateManager.

Například následující kód XAML sleduje stav CommonStates.MouseOver a animuje barvu výplně prvku s názvem backgroundElement. Když se ovládací prvek vrátí do CommonStates.Normal stavu, obnoví se barva výplně prvku s názvem backgroundElement.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <ColorAnimation Storyboard.TargetName="backgroundElement"
                                    Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                                    To="{TemplateBinding Background}"
                                    Duration="0:0:0.3"/>
                </VisualState>
                <VisualState Name="MouseOver">
                    <ColorAnimation Storyboard.TargetName="backgroundElement"
                                    Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                                    To="Yellow"
                                    Duration="0:0:0.3"/>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        ...

Další informace o scénářích najdete v tématu Přehled scénářů.

Sdílené prostředky a motivy

Typická aplikace WPF může mít více prostředků uživatelského rozhraní, které se použijí v celé aplikaci. Společně lze tuto sadu prostředků považovat za motiv aplikace. WPF umožňuje zabalování prostředků uživatelského rozhraní jako motiv pomocí slovníku prostředků, který je v podobě třídy ResourceDictionary.

Motivy WPF jsou definovány pomocí mechanismu stylů a šablon, který WPF zveřejňuje pro přizpůsobení vizuálů libovolného prvku.

Prostředky motivu WPF se ukládají do vložených slovníků prostředků. Tyto slovníky prostředků musí být vloženy do podepsaného sestavení a mohou být vloženy do stejného sestavení jako samotný kód nebo do souběžného sestavení. Pro PresentationFramework.dll, sestavení, které obsahuje ovládací prvky WPF, jsou prostředky motivu v řadě vedlejších sestavení.

Motiv se stane posledním místem, kde se má hledat styl prvku. Obvykle hledání začíná procházením stromu prvků kvůli nalezení vhodného prostředku, poté se podívá do kolekce prostředků aplikace a nakonec se dotazuje systému. Vývojáři aplikací tak můžou před dosažením motivu předefinovat styl libovolného objektu na úrovni stromu nebo aplikace.

Slovníky zdrojů můžete definovat jako jednotlivé soubory, které vám umožní opakovaně použít rozhraní napříč více aplikacemi. Můžete také vytvořit přepínatelné motivy definováním více slovníků prostředků, které poskytují stejné typy prostředků, ale s různými hodnotami. Předefinování těchto stylů nebo jiných prostředků na úrovni aplikace je doporučeným přístupem pro úpravu aplikace.

Pokud chcete sdílet sadu prostředků, včetně stylů a šablon, napříč aplikacemi, můžete vytvořit soubor XAML a definovat ResourceDictionary, který obsahuje odkaz na soubor shared.xaml.

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>

Jedná se o sdílení shared.xaml, který sám definuje ResourceDictionary, jež obsahuje sadu stylů a prostředků štětců, které umožňují ovládacím prvkům v aplikaci mít konzistentní vzhled.

Další informace najdete v tématu sloučené slovníky zdrojů.

Pokud vytváříte motiv pro vlastní ovládací prvek, podívejte se na část Definování prostředků na úrovni motivu přehledu Přehled vytváření ovládacích prvků.

Viz také