Styly a šablony (WPF .NET)

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ě atraktivní efekty a konzistentní vzhled svého 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ě najdete 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 najdete v tématu Přehled prostředků XAML.

Důležité

Dokumentace k desktopové příručce pro .NET 7 a .NET 6 se právě připravuje.

Vzorek

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

Styled 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 ListBox ovládací prvek, který je svázán se seznamem obrázků.

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

Styly

Můžete si představit Style jako pohodlný způsob použití sady hodnot vlastností na více prvků. Styl můžete použít u libovolného prvku, který je odvozen nebo FrameworkElementFrameworkContentElement například WindowButton.

Nejběžnější způsob, jak deklarovat styl, je jako prostředek v Resources oddílu v souboru XAML. Vzhledem k tomu, že styly jsou prostředky, dodržují stejná pravidla oborů, která platí pro všechny prostředky. 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.

Například následující kód XAML deklaruje dva styly pro jeden TextBlockautomaticky použitý pro všechny TextBlock prvky a druhý, který musí být explicitně odkazován.

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

Styled textblocks

Další informace naleznete v tématu Vytvoření stylu ovládacího prvku.

ControlTemplates

Ve WPF ControlTemplate definuje ovládací prvek vzhled ovládacího prvku. Strukturu a vzhled ovládacího prvku můžete změnit definováním nového ControlTemplate ovládacího prvku 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á výchozí šablonu přiřazenou k Control.Template vlastnost. Š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 Resources části souboru XAML. Stejně jako u všech prostředků platí pravidla oborů.

Šablony ovládacích prvků jsou mnohem více zapojené 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 použít styl k definování nebo nastavení šablony.

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 CheckBox ovládací prvek 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 tématu Vytvoření šablony pro ovládací prvek.

TemplateBinding

Možná jste si všimli, že prostředek šablony definovaný v předchozí části používá Rozšíření značek TemplateBinding. A 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 BorderThickness má například vlastnost. TemplateBinding Použití ke správě prvku v šabloně je ovlivněn tímto nastavením ovládacího prvku.

ContentControl a ItemsControl

Pokud je deklarován ContentPresenter v ControlTemplate objektu ContentControl, ContentPresenter automaticky se sváže s vlastnostmi a Content vlastnostmiContentTemplate. Podobně se objektItemsPresenter, který je v objektuControlTemplate, ItemsControl automaticky sváže s vlastnostmi a Items vlastnostmiItemTemplate.

DataTemplates

V této ukázkové aplikaci je ListBox ovládací prvek, který je svázán se seznamem fotek.

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

V současné době to ListBox vypadá takto.

ListBox before applying template

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 určení DataTemplate , jak data vypadají v vykreslené aplikaci.

V naší ukázkové aplikaci má Source každý vlastní Photo objekt vlastnost typu řetězec, který určuje cestu k souboru obrázku. 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 fotky zobrazovaly jako obrázky, vytvoříte jako DataTemplate prostředek.

<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 DataType vlastnost je podobná TargetType vlastnosti Style. Pokud se nacházíte DataTemplate v oddílu prostředků, při zadání DataType vlastnosti typu a vynechání x:Key, použije DataTemplate se při každém zobrazení daného typu. Vždy máte možnost přiřadit ho k objektu DataTemplatex:Key a nastavit ho jako StaticResource vlastnosti, které přebírají DataTemplate typy, jako ItemTemplate je například vlastnost nebo ContentTemplate vlastnost.

V podstatě výše DataTemplate uvedený příklad definuje, že pokaždé, když je Photo objekt, by měl vypadat jako Image uvnitř .Border Díky tomu DataTemplateteď naše aplikace vypadá takto.

Photo image

Model šablon dat poskytuje další funkce. Například pokud zobrazujete data shromažďování, která obsahují jiné kolekce pomocí HeaderedItemsControl typu, jako Menu je například nebo , TreeViewje HierarchicalDataTemplatetam . Další funkcí šablonování dat je DataTemplateSelectorfunkce , která umožňuje zvolit DataTemplate použití 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.

Aktivační události

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í Triggers vlastnost, která může obsahovat sadu triggerů. Existuje několik typů triggerů.

PropertyTriggers

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

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

<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 Trigger třída má EnterActions také vlastnosti, ExitActions které umožňují triggeru provádět akce.

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

Styled ListView

EventTriggery a scénáře

Dalším typem triggeru EventTriggerje trigger , 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 zadá ListBoxItem, MaxHeight vlastnost animace na hodnotu 90 za 0.2 druhé období. Když se myš přesune od položky, vrátí se vlastnost k původní hodnotě za období sekundy 1 . Všimněte si, že není nutné zadat To hodnotu 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.

Styling sample screenshot

MultiTriggery, DataTriggery a MultiDataTriggery

Kromě Trigger a EventTriggerexistují i další typy aktivačních událostí. MultiTrigger umožňuje nastavit hodnoty vlastností na základě více podmínek. Použijete DataTrigger a MultiDataTrigger kdy je vlastnost podmínky svázaná s daty.

Stavy vizuálů

Ovládací prvky jsou vždy v určitém stavu. Například když se myš pohybuje nad povrchem ovládacího prvku, považuje se ovládací prvek za společný stav MouseOver. Řízení bez konkrétního stavu se považuje za společné Normal . Státy jsou rozděleny do skupin a dříve uvedené stavy jsou součástí skupiny CommonStatesstátů . 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
Normální 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.
Rozostřený FocusStates Ovládací prvek nemá fokus.

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

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

<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 poskytuje podporu pro balení prostředků uživatelského rozhraní jako motiv pomocí slovníku prostředků, který je zapouzdřen jako ResourceDictionary třída.

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í obsahující ovládací prvky WPF jsou prostředky motivu v řadě souběžných sestavení.

Motiv se stane posledním místem, kde se má hledat styl prvku. Hledání obvykle začíná procházením stromu prvků, který hledá vhodný prostředek, pak se podívá do kolekce prostředků aplikace a nakonec se do systému dotazuje. 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 prostředků můžete definovat jako jednotlivé soubory, které umožňují opakovaně používat motiv napříč více aplikacemi. Můžete také vytvořit prohozené 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 soubor, který obsahuje odkaz na shared.xaml soubor.

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

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

Další informace najdete v tématu Slovníky sloučených prostředků.

Pokud vytváříte motiv pro vlastní ovládací prvek, přečtěte si část Definování prostředků na úrovni motivu v přehledu vytváření ovládacích prvků.

Viz také