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.
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>
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.
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:Key
op 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.
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.
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 backgroundElement
te 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
.NET Desktop feedback