Delen via


Wat zijn stijlen en sjablonen? (WPF .NET)

Windows Presentation Foundation (WPF) styling en templating verwijzen naar een suite met functies waarmee ontwikkelaars en ontwerpers visueel aantrekkelijke effecten en een consistent uiterlijk voor hun product kunnen creëren. Bij het aanpassen van het uiterlijk van een app wilt u een sterk stijl- en sjablonenmodel dat onderhoud en delen van het uiterlijk binnen en tussen apps mogelijk maakt. WPF biedt dat model.

Een andere functie van het WPF-stijlmodel is de scheiding van presentatie en logica. Ontwerpers kunnen werken aan het uiterlijk van een app door alleen XAML te gebruiken op hetzelfde moment dat ontwikkelaars aan de programmeerlogica werken met behulp van C# of Visual Basic.

Dit overzicht richt zich op de stijl- en sjabloonaspecten van de app en bespreekt geen gegevensbindingsconcepten. Zie Overzicht van gegevensbindingvoor meer informatie over gegevensbinding.

Het is belangrijk om inzicht te krijgen in resources. Dit zijn de hulpmiddelen waarmee stijlen en sjablonen opnieuw kunnen worden gebruikt. Zie Overzicht van XAML-resourcesvoor meer informatie over resources.

Voorbeeld

De voorbeeldcode in dit overzicht is gebaseerd op een eenvoudige toepassing voor het bladeren door foto's weergegeven in de volgende afbeelding.

Styled ListView

In dit eenvoudige fotovoorbeeld wordt gebruikgemaakt van styling en sjabloneren om een visueel aantrekkelijke beleving te creëren. Het voorbeeld heeft twee TextBlock elementen en een ListBox besturingselement dat is gebonden aan een lijst met afbeeldingen.

Zie voor het volledige voorbeeld Inleiding tot stijl- en sjabloneringvoorbeelden.

Stijlen

U kunt een Style beschouwen als een handige manier om een set eigenschapswaarden toe te passen op meerdere elementen. U kunt een stijl gebruiken voor elk element dat is afgeleid van FrameworkElement of FrameworkContentElement, zoals een Window of een Button.

De meest voorkomende manier om een stijl te declareren, is als een resource in de sectie Resources in een XAML-bestand. Omdat stijlen resources zijn, voldoen ze aan dezelfde bereikregels die van toepassing zijn op alle resources. Eenvoudig gezegd, waar u een stijl declareert, beïnvloedt waar de stijl kan worden toegepast. Als u bijvoorbeeld de stijl declareert in het hoofdelement van het XAML-bestand van uw app-definitie, kan de stijl overal in uw app worden gebruikt.

De volgende XAML-code declareert bijvoorbeeld twee stijlen voor een TextBlock, een die automatisch wordt toegepast op alle TextBlock elementen en een andere die expliciet moet worden verwezen.

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

Hier volgt een voorbeeld van de stijlen die hierboven zijn gedeclareerd.

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

gestileerde tekstblokken

Zie Een stijl maken voor een besturingselementvoor meer informatie.

ControlTemplates

In WPF definieert de ControlTemplate van een besturingselement het uiterlijk van het besturingselement. U kunt de structuur en het uiterlijk van een besturingselement wijzigen door een nieuwe ControlTemplate te definiëren en dit toe te wijzen aan een besturingselement. In veel gevallen bieden sjablonen u voldoende flexibiliteit, zodat u uw eigen aangepaste besturingselementen niet hoeft te schrijven.

Elk controle heeft een standaardsjabloon toegewezen aan de eigenschap Control.Template. De sjabloon verbindt de visuele presentatie van het besturingselement met de mogelijkheden van het besturingselement. Omdat u een sjabloon in XAML definieert, kunt u het uiterlijk van het besturingselement wijzigen zonder code te schrijven. Elke sjabloon is ontworpen voor een specifieke controle, zoals een Button.

Meestal declareert u een sjabloon als een resource in de sectie Resources van een XAML-bestand. Net als bij alle resources gelden omvangregels.

Sjablonen voor besturingselementen zijn veel complexer dan een stijl. Dit komt doordat de besturingselementsjabloon het uiterlijk van het visuele element van het hele besturingselement herschrijft, terwijl een stijl gewoon eigenschapswijzigingen toepast op het bestaande besturingselement. Aangezien de sjabloon van een besturingselement wordt toegepast door de eigenschap Control.Template in te stellen, kan een stijl worden gebruikt om een sjabloon te definiëren of aan te passen.

Ontwerpers stellen u in het algemeen in staat om een kopie van een bestaande sjabloon te maken en deze te wijzigen. Selecteer bijvoorbeeld in de WPF-ontwerpfunctie van Visual Studio een CheckBox controle, klik met de rechtermuisknop en kies Sjabloon bewerken>Een kopie maken. Met deze opdracht wordt een stijl gegenereerd waarmee een sjabloon wordt gedefinieerd.

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

Het bewerken van een kopie van een sjabloon is een uitstekende manier om te leren hoe sjablonen werken. In plaats van een nieuwe lege sjabloon te maken, is het eenvoudiger om een kopie te bewerken en enkele aspecten van de visuele presentatie te wijzigen.

Zie bijvoorbeeld Een sjabloon maken voor een besturingselement.

TemplateBinding

Mogelijk hebt u gemerkt dat de sjabloonresource die in de vorige sectie is gedefinieerd, gebruikmaakt van de TemplateBinding Markup Extension. Een TemplateBinding is een geoptimaliseerde vorm van een binding voor sjabloonscenario's, vergelijkbaar met een binding die is samengesteld met {Binding RelativeSource={RelativeSource TemplatedParent}}. TemplateBinding is handig voor het binden van onderdelen van de sjabloon aan eigenschappen van het besturingselement. Elk besturingselement heeft bijvoorbeeld een eigenschap BorderThickness. Gebruik een TemplateBinding om te beheren welk element in de sjabloon wordt beïnvloed door deze besturingselementinstelling.

ContentControl en ItemsControl

Als een ContentPresenter wordt gedeclareerd in de ControlTemplate van een ContentControl, wordt de ContentPresenter automatisch gekoppeld aan de eigenschappen van de ContentTemplate en Content. Op dezelfde manier wordt een ItemsPresenter die zich in de ControlTemplate van een ItemsControl bevindt, automatisch gebonden aan de eigenschappen ItemTemplate en Items.

Gegevenssjablonen

In deze voorbeeld-app is er een ListBox bedieningselement dat is gekoppeld aan een lijst met foto's.

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

Deze ListBox ziet er momenteel als volgt uit.

Keuzelijst voordat u sjabloon toepast

De meeste besturingselementen hebben een bepaald type inhoud en die inhoud komt vaak van gegevens waarmee u verbinding maakt. In dit voorbeeld zijn de gegevens de lijst met foto's. In WPF gebruikt u een DataTemplate om de visuele weergave van gegevens te definiëren. Wat u in een DataTemplate plaatst, bepaalt in feite hoe de gegevens eruit zien in de gerenderde app.

In onze voorbeeld-app heeft elk aangepast Photo-object een Source-eigenschap van het type string die het bestandspad van de afbeelding specificeert. Momenteel worden de fotoobjecten weergegeven als bestandspaden.

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

Als u wilt dat de foto's worden weergegeven als afbeeldingen, maakt u een DataTemplate als een resource.

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

U ziet dat de eigenschap DataType vergelijkbaar is met de eigenschap TargetType van de Style. Als uw DataTemplate zich in de sectie Resources bevindt en u de eigenschap DataType aan een type toekent zonder een x:Keyop te geven, wordt de DataTemplate toegepast zodra dat type wordt weergegeven. U hebt altijd de mogelijkheid om de DataTemplate toe te wijzen met een x:Key en deze vervolgens in te stellen als een StaticResource voor eigenschappen die DataTemplate typen hebben, zoals de eigenschap ItemTemplate of de eigenschap ContentTemplate.

In wezen definieert het DataTemplate in het bovenstaande voorbeeld dat wanneer er een Photo object is, het als een Image binnen een Bordermoet worden weergegeven. Met deze DataTemplateziet onze app er nu als volgt uit.

fotoafbeelding

Het model voor gegevenssjabloonvorming voorziet in andere functies. Als u bijvoorbeeld verzamelingsgegevens weergeeft die andere verzamelingen bevatten met behulp van een HeaderedItemsControl type, zoals een Menu of een TreeView, is er de HierarchicalDataTemplate. Een andere functie voor het temperen van gegevens is de DataTemplateSelector, waarmee u een DataTemplate kunt kiezen op basis van aangepaste logica. Voor meer informatie, zie Overzicht van gegevenssjablonering, dat een uitgebreidere bespreking biedt van de verschillende functies voor gegevenssjablonering.

Aanleidingen

Met een trigger worden eigenschappen ingesteld of acties gestart, zoals een animatie, wanneer een eigenschapswaarde wordt gewijzigd of wanneer een gebeurtenis wordt gegenereerd. Style, ControlTemplateen DataTemplate hebben allemaal een Triggers eigenschap die een set triggers kan bevatten. Er zijn verschillende soorten triggers.

PropertyTriggers

Een Trigger waarmee eigenschapswaarden worden ingesteld of acties worden gestart op basis van de waarde van een eigenschap, wordt een eigenschapstrigger genoemd.

Als u wilt laten zien hoe u eigenschaptriggers gebruikt, kunt u elke ListBoxItem gedeeltelijk transparant maken, tenzij deze is geselecteerd. Met de volgende stijl wordt de Opacity waarde van een ListBoxItem ingesteld op 0.5. Wanneer de eigenschap IsSelected echter is true, wordt de Opacity ingesteld op 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>

In dit voorbeeld wordt een Trigger gebruikt om een eigenschapswaarde in te stellen, maar de klasse Trigger heeft ook de eigenschappen EnterActions en ExitActions waarmee een trigger acties kan uitvoeren.

U ziet dat de eigenschap MaxHeight van de ListBoxItem is ingesteld op 75. In de volgende afbeelding is het derde item het geselecteerde item.

Styled ListView-

Gebeurtenistriggers en Storyboards

Een ander type trigger is de EventTrigger, waarmee een set acties wordt gestart op basis van het optreden van een gebeurtenis. In de volgende EventTrigger objecten wordt bijvoorbeeld aangegeven dat wanneer de muisaanwijzer de ListBoxItembinnenkomt, de eigenschap MaxHeight naar een waarde van 90 animeren gedurende een periode van 0.2 seconden. Wanneer de muis van het item afschuift, wordt de eigenschap teruggegaan naar de oorspronkelijke waarde gedurende een periode van 1 seconde. U hoeft geen To waarde op te geven voor de MouseLeave animatie. Dit komt doordat de animatie de oorspronkelijke waarde kan bijhouden.

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

Zie het overzicht van Storyboardsvoor meer informatie.

In de volgende afbeelding wijst de muis naar het derde item.

Schermopname van voorbeeld stijl

MultiTriggers, DataTriggers en MultiDataTriggers

Naast Trigger en EventTriggerzijn er andere typen triggers. Met MultiTrigger kunt u de eigenschapswaarden instellen op basis van meerdere voorwaarden. U gebruikt DataTrigger en MultiDataTrigger wanneer de eigenschap van uw voorwaarde gegevensgebonden is.

Visuele Toestanden

Besturingselementen hebben altijd een specifieke status. Wanneer de muis bijvoorbeeld over het oppervlak van een bedieningselement beweegt, wordt het bedieningselement beschouwd als in een algemene toestand van MouseOver. Een besturingselement zonder een specifieke status wordt beschouwd als een algemene Normal status. Staten worden onderverdeeld in groepen en de eerder genoemde staten maken deel uit van de staatsgroep CommonStates. De meeste besturingselementen hebben twee statusgroepen: CommonStates en FocusStates. Van elke statusgroep die op een besturingselement wordt toegepast, heeft een besturingselement altijd één status van elke groep, zoals CommonStates.MouseOver en FocusStates.Unfocused. Een besturingselement kan zich echter niet in twee verschillende statussen binnen dezelfde groep bevinden, zoals CommonStates.Normal en CommonStates.Disabled. Hier volgt een tabel met statussen die de meeste besturingselementen herkennen en gebruiken.

VisualState-naam VisualStateGroup-naam Beschrijving
Normal CommonStates De standaardstatus.
MouseOver CommonStates De muisaanwijzer bevindt zich boven het besturingselement.
Pressed CommonStates Het besturingselement wordt ingedrukt.
Disabled CommonStates Het besturingselement is uitgeschakeld.
Focused FocusStates Het controle-element heeft de focus.
Unfocused FocusStates Het besturingselement heeft geen focus.

Door een System.Windows.VisualStateManager te definiëren op het hoofdelement van een besturingselementsjabloon, kunt u animaties activeren wanneer een besturingselement een specifieke status invoert. De VisualStateManager bepaalt welke combinaties van VisualStateGroup en VisualState om in de gaten te houden. Wanneer het besturingselement in een bewaakte status verkeert, wordt de animatie die door de VisualStateManager is gedefinieerd gestart.

Met de volgende XAML-code wordt bijvoorbeeld de CommonStates.MouseOver-status gecontroleerd om de vulkleur van het element backgroundElementte animeren. Wanneer het besturingselement terugkeert naar de status CommonStates.Normal, wordt de opvulkleur van het element met de naam backgroundElement hersteld.

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

        ...

Zie Storyboards Overviewvoor meer informatie over storyboards.

Gedeelde middelen en thema's

Een typische WPF-app kan meerdere UI-resources hebben die in de app worden toegepast. Gezamenlijk kan deze set resources worden beschouwd als het thema voor de app. WPF biedt ondersteuning voor het verpakken van UI-resources als thema met behulp van een resourcewoordenlijst die is ingekapseld als de ResourceDictionary-klasse.

WPF-thema's worden gedefinieerd met behulp van het stijl- en sjabloonmechanisme dat WPF beschikbaar maakt voor het aanpassen van de visuals van elk element.

WPF-themaresources worden opgeslagen in ingesloten resourcewoordenlijsten. Deze resourcewoordenlijsten moeten worden ingesloten in een ondertekende assembly en kunnen worden ingesloten in dezelfde assembly als de code zelf of in een side-by-side assembly. Voor PresentationFramework.dllbevindt de assembly die WPF-besturingselementen bevat zich samen met themabronnen in een reeks naast elkaar geplaatste assembly's.

Het thema wordt de laatste plek om te zoeken bij het zoeken naar de stijl van een element. Normaal gesproken begint de zoekopdracht met het doorlopen van de elementstructuur om een geschikte resource te vinden, vervolgens doorzoekt het de appbronnenverzameling en ten slotte raadpleegt het systeem. Hierdoor kunnen app-ontwikkelaars de stijl voor elk object op structuur- of app-niveau opnieuw definiëren voordat ze het thema bereiken.

U kunt resourcewoordenlijsten definiëren als afzonderlijke bestanden waarmee u een thema in meerdere apps opnieuw kunt gebruiken. U kunt ook wisselbare thema's maken door meerdere resourcewoordenlijsten te definiëren die dezelfde typen resources bieden, maar met verschillende waarden. Het herdefiniëren van deze stijlen of andere resources op app-niveau is de aanbevolen benadering voor het skinnen van een app.

Als u een set resources, inclusief stijlen en sjablonen, wilt delen in verschillende apps, kunt u een XAML-bestand maken en een ResourceDictionary definiëren die verwijzingen naar een shared.xaml-bestand bevat.

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

Het delen van shared.xaml, dat zelf een ResourceDictionary definieert die een set stijl- en kwastbronnen bevat, stelt de elementen in een app in staat een consistente uitstraling te hebben.

Voor meer informatie, zie samengevoegde resourcewoordenboeken.

Als u een thema voor uw aangepaste bediening maakt, raadpleegt u de sectie Resources definiëren op themaniveau van het Overzicht van het ontwerpen van besturingselementen.

Zie ook