Stijlen en sjablonen in WPF
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 sjabloonmodel dat het onderhoud en het 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 templateaspecten 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 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 stijlen en sjablonen om een visueel aantrekkelijke gebruikerservaring te creëren. Het voorbeeld heeft twee TextBlock elementen en een ListBox besturingselement dat is gebonden aan een lijst met afbeeldingen.
Voor het volledige voorbeeld, zie Inleiding tot stijling en templating voorbeeld.
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>
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 control 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 scope-regels.
Sjablonen voor bedieningselementen 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 echter wordt toegepast door de eigenschap Control.Template in te stellen, kunt u een stijl gebruiken om een sjabloon te definiëren of in te stellen.
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
-besturingselement, klik met de rechtermuisknop en kies vervolgens 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.
DataTemplates
In deze voorbeeldapp is er een ListBox controle die 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, waarmee het bestandspad van de afbeelding wordt gespecificeerd. 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, dan wordt DataTemplate toegepast telkens wanneer u de eigenschap DataType voor een type opgeeft en x:Key
weglaat. 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 sjabloonmodel voor gegevens biedt 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 sjabloneren van gegevens is de DataTemplateSelector, waarmee je een DataTemplate kunt kiezen op basis van aangepaste logica. Voor meer informatie, zie Overzicht van gegevenssjabloneren, dat een uitgebreidere bespreking van de verschillende functies voor gegevenssjabloneren biedt.
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.
EventTriggers 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 een animatie heeft naar een waarde van 90
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 styling voorbeeld
MultiTriggers, DataTriggers en MultiDataTriggers
Naast Trigger en EventTriggerzijn er andere typen triggers. Met MultiTrigger kunt u de waarde van eigenschappen 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 besturingselement beweegt, wordt het besturingselement beschouwd als in een standaardtoestand van MouseOver
. Een besturingselement zonder een specifieke toestand wordt beschouwd als in de gemeenschappelijke Normal
toestand. 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 |
De Control-toets is ingedrukt. |
Disabled |
CommonStates |
Het besturingselement is uitgeschakeld. |
Focused |
FocusStates |
Het besturingselement 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
declareert welke combinaties van VisualStateGroup en VisualState te bekijken. Wanneer het controle-element in een bewakingsstatus komt, wordt de animatie die is gedefinieerd door de VisualStateManager
gestart.
Met de volgende XAML-code wordt bijvoorbeeld de status van CommonStates.MouseOver
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 hulpmiddelen 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 resourcedictionaries. Deze resourcedictionaries 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 thema-bronnen in een reeks assembly's naast elkaar.
Het thema wordt de laatste plek om te zoeken bij het zoeken naar de stijl van een element. Normaal gesproken begint de zoekopdracht door binnen de elementenstructuur op zoek te gaan naar een geschikte resource, vervolgens de app-resourceverzameling te raadplegen en ten slotte het systeem te bevragen. 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 opnieuw definiëren van deze stijlen of andere resources op app-niveau is de aanbevolen benadering voor het aanpassen van de uitstraling 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 is het delen van shared.xaml
, dat zelf ResourceDictionary definieert die een set van stijl- en kwastresources bevat, waardoor de elementen in een app een consistent uiterlijk krijgen.
Voor meer informatie, zie samengevoegde bronnenwoordenlijsten.
Als u een thema voor uw aangepaste besturingselement maakt, raadpleegt u de Resources definiëren op themaniveau sectie van het Overzicht van het ontwerpen van besturingselementen.
Zie ook
.NET Desktop feedback