Dela via


Vad är format och mallar?

Windows Presentation Foundation (WPF) styling och templating refererar till en uppsättning funktioner som gör det möjligt för utvecklare och designers att skapa visuellt övertygande effekter och ett konsekvent utseende för sin produkt. När du anpassar utseendet på en app vill du ha en stark formaterings- och mallmodell som möjliggör underhåll och delning av utseende i och mellan appar. WPF tillhandahåller den modellen.

En annan funktion i WPF-formateringsmodellen är separationen av presentation och logik. Designers kan arbeta med utseendet på en app genom att bara använda XAML samtidigt som utvecklare arbetar med programmeringslogik med hjälp av C# eller Visual Basic.

Den här översikten fokuserar på design- och mallningsaspekterna i appen och diskuterar inte några databindningsbegrepp. Information om databindning finns i Översikt över databindning.

Det är viktigt att förstå resurser, vilket är det som gör att formatmallar och mallar kan återanvändas. Mer information om resurser finns i Översikt över XAML-resurser.

Exempel

Exempelkoden i den här översikten baseras på ett enkelt program för fotobläddring som visas i följande bild.

Stiliserad listvy

Det här enkla fotoexemplet använder formatering och mallning för att skapa en visuellt övertygande användarupplevelse. Exemplet har två TextBlock element och en ListBox kontroll som är bunden till en lista med bilder.

Det fullständiga exemplet finns i Introduktion till formatering och mallexempel.

Stilar

Du kan se en Style som ett bekvämt sätt att tillämpa en uppsättning egenskapsvärden på flera element. Du kan använda ett format på alla element som härleds från FrameworkElement eller FrameworkContentElement, till exempel en Window eller en Button.

Det vanligaste sättet att deklarera ett format är som en resurs i avsnittet Resources i en XAML-fil. Eftersom formatmallar är resurser följer de samma omfångsregler som gäller för alla resurser. Enkelt uttryckt, där du deklarerar ett format påverkar var formatet kan tillämpas. Om du till exempel deklarerar formatet i rotelementet i din appdefinitions XAML-fil kan formatet användas var som helst i din app.

Följande XAML-kod deklarerar till exempel två format för en TextBlock, en som automatiskt tillämpas på alla TextBlock element och en annan som uttryckligen måste refereras till.

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

Här är ett exempel på de formatmallar som deklareras ovan som används.

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

Formaterade textblock

Mer information finns i Skapa ett format för en kontroll.

ControlTemplates

I WPF definierar ControlTemplate för en kontroll kontrollens utseende. Du kan ändra strukturen och utseendet på en kontroll genom att definiera en ny ControlTemplate och tilldela den till en kontroll. I många fall ger mallar dig tillräckligt med flexibilitet så att du inte behöver skriva egna anpassade kontroller.

Varje kontroll har en standardmall tilldelad egenskapen Control.Template. Mallen ansluter kontrollens visuella presentation med kontrollens funktioner. Eftersom du definierar en mall i XAML kan du ändra kontrollens utseende utan att skriva någon kod. Varje mall är utformad för en specifik kontroll, till exempel en Button.

Vanligtvis deklarerar du en mall som en resurs i avsnittet Resources i en XAML-fil. Precis som med alla resurser gäller omfångsregler.

Kontrollmallar är mycket mer komplexa än en stil. Det beror på att kontrollmallen skriver om det visuella utseendet på hela kontrollen, medan ett format helt enkelt tillämpar egenskapsändringar på den befintliga kontrollen. Men eftersom mallen för en kontroll tillämpas genom att ange egenskapen Control.Template kan du använda ett format för att definiera eller ange en mall.

Med designers kan du vanligtvis skapa en kopia av en befintlig mall och ändra den. I Visual Studio WPF-designern väljer du till exempel en CheckBox kontroll och högerklickar och väljer sedan Redigera mall>Skapa en kopia. Det här kommandot genererar en stilmall som definierar en mall.

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

Att redigera en kopia av en mall är ett bra sätt att lära sig hur mallar fungerar. I stället för att skapa en ny tom mall är det enklare att redigera en kopia och ändra några aspekter av den visuella presentationen.

Ett exempel finns i Skapa en mall för en kontroll.

Mallbindning

Du kanske har märkt att mallresursen som definierades i föregående avsnitt använder TemplateBinding Markup Extension. En TemplateBinding är en optimerad form av en bindning för mallscenarier, som motsvarar en bindning som konstruerats med {Binding RelativeSource={RelativeSource TemplatedParent}}. TemplateBinding är användbart för att binda delar av mallen till kontrollens egenskaper. Till exempel har varje kontroll en BorderThickness-egenskap. Använd en TemplateBinding för att hantera vilket element i mallen som påverkas av den här kontrollinställningen.

ContentControl och ItemsControl

Om en ContentPresenter deklareras i ControlTemplate för en ContentControlbinder ContentPresenter automatiskt till egenskaperna ContentTemplate och Content. På samma sätt binder en ItemsPresenter som finns i ControlTemplate för en ItemsControl automatiskt till egenskaperna ItemTemplate och Items.

DataTemplates

I den här exempelappen finns det en ListBox kontroll som är bunden till en lista med foton.

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

Den här ListBox ser för närvarande ut som följande.

ListBox innan mallen tillämpas

De flesta kontroller har någon typ av innehåll, och det innehållet kommer ofta från data som du binder till. I det här exemplet utgör datan listan över foton. I WPF använder du en DataTemplate för att definiera den visuella representationen av data. I grund och botten avgör vad du lägger i en DataTemplate hur data ser ut i den renderade appen.

I vår exempelapp har varje anpassat Photo-objekt en Source-egenskap av typen sträng som anger bildens filsökväg. För närvarande visas fotoobjekten som filsökvägar.

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

För att bilderna ska visas som bilder skapar du en DataTemplate som en resurs.

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

Observera att egenskapen DataType liknar egenskapen TargetType för Style. Om din DataTemplate finns i resursavsnittet, när du anger egenskapen DataType till en typ och utelämnar en x:Key, tillämpas DataTemplate när den typen visas. Du har alltid möjlighet att tilldela DataTemplate en x:Key och sedan ange den som en StaticResource för egenskaper som accepterar DataTemplate-typer, till exempel ItemTemplate-egenskapen eller ContentTemplate-egenskapen.

I princip definierar DataTemplate i exemplet ovan att när det finns ett Photo objekt bör det visas som en Image inom en Border. Med den här DataTemplateser appen nu ut så här.

Fotobild

Databearbetningsmodellen innehåller andra funktioner. Om du till exempel visar samlingsdata som innehåller andra samlingar med hjälp av en HeaderedItemsControl typ, till exempel en Menu eller en TreeView, finns det HierarchicalDataTemplate. En annan funktion för datakryptering är DataTemplateSelector, som gör att du kan välja en DataTemplate som ska användas baserat på anpassad logik. Mer information finns i Översikt över datamallar, som ger en mer djupgående diskussion om de olika funktionerna för datamallar.

Utlösare

En utlösare anger egenskaper eller startar åtgärder, till exempel en animering, när ett egenskapsvärde ändras eller när en händelse utlöses. Style, ControlTemplateoch DataTemplate alla har en Triggers egenskap som kan innehålla en uppsättning utlösare. Det finns flera typer av utlösare.

PropertyTriggers

En Trigger som anger egenskapsvärden eller startar åtgärder baserat på värdet för en egenskap kallas för egenskapsutlösare.

Om du vill visa hur du använder egenskapsutlösare kan du göra varje ListBoxItem delvis transparent om den inte har valts. Följande stil sätter värdet på Opacity för en ListBoxItem till 0.5. När egenskapen IsSelected är trueanges dock Opacity till 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>

I det här exemplet används en Trigger för att ange ett egenskapsvärde, men observera att klassen Trigger också har egenskaperna EnterActions och ExitActions som gör att en utlösare kan utföra åtgärder.

Observera att egenskapen MaxHeight för ListBoxItem är inställd på 75. I följande bild är det tredje objektet det markerade objektet.

Stiliserad listvy

EventTriggers och Storyboards

En annan typ av utlösare är EventTrigger, som startar en uppsättning åtgärder baserat på förekomsten av en händelse. Till exempel anger följande EventTrigger objekt att när muspekaren kommer in i ListBoxItem, animeras egenskapen MaxHeight till ett värde av 90 över en tidsperiod på 0.2 sekunder. När musen flyttas bort från objektet återgår egenskapen till det ursprungliga värdet under en period av 1 sekund. Observera att det inte är nödvändigt att ange ett To värde för den MouseLeave animeringen. Det beror på att animeringen kan hålla reda på det ursprungliga värdet.

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

För mer information, se översikten av Storyboards.

I följande bild pekar musen på det tredje objektet.

Skärmbild av formateringsexempel

MultiTriggers, DataTriggers och MultiDataTriggers

Förutom Trigger och EventTriggerfinns det andra typer av utlösare. Med MultiTrigger kan du ange egenskapsvärden baserat på flera villkor. Du använder DataTrigger och MultiDataTrigger när egenskapen för ditt villkor är databunden.

Visuella tillstånd

Kontrollerna är alltid i ett specifikt tillstånd . När musen till exempel flyttas över en kontrolls yta, betraktas kontrollen vara i ett vanligt tillstånd av MouseOver. En kontroll utan specifikt tillstånd anses vara i det allmänna Normal-tillståndet. Tillstånd är uppdelade i grupper och de tidigare nämnda tillstånden är en del av tillståndsgruppen CommonStates. De flesta kontroller har två tillståndsgrupper: CommonStates och FocusStates. För varje tillståndsgrupp som tillämpas på en kontroll är en kontroll alltid i ett tillstånd för varje grupp, till exempel CommonStates.MouseOver och FocusStates.Unfocused. En kontroll kan dock inte finnas i två olika tillstånd i samma grupp, till exempel CommonStates.Normal och CommonStates.Disabled. Här är en tabell med tillstånd som de flesta kontroller känner igen och använder.

VisualState-namn VisualStateGroup-namn Beskrivning
Normal CommonStates Standardtillståndet.
MouseOver CommonStates Muspekaren är placerad över kontrollen.
Pressed CommonStates Kontrollknappen trycks in.
Disabled CommonStates Kontrollen är inaktiverad.
Focused FocusStates Kontrollen har fokus.
Unfocused FocusStates Kontrollen saknar fokus.

Genom att definiera en System.Windows.VisualStateManager på rotelementet i en kontrollmall kan du utlösa animeringar när en kontroll anger ett visst tillstånd. VisualStateManager anger vilka kombinationer av VisualStateGroup och VisualState som ska övervakas. När kontrollen anger ett övervakat tillstånd startas animeringen som definieras av VisualStateManager.

Följande XAML-kod övervakar till exempel CommonStates.MouseOver tillstånd för att animera fyllningsfärgen för elementet med namnet backgroundElement. När kontrollen återgår till CommonStates.Normal tillstånd återställs fyllningsfärgen för elementet med namnet 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>

        ...

Mer information om storyboards finns i Storyboards Overview.

Delade resurser och teman

En typisk WPF-app kan ha flera gränssnittsresurser som tillämpas i hela appen. Tillsammans kan den här uppsättningen resurser betraktas som temat för appen. WPF ger stöd för paketering av användargränssnittsresurser som ett tema med hjälp av en resursordlista som är inkapslad som ResourceDictionary-klassen.

WPF-teman definieras med hjälp av den formaterings- och mallmekanism som WPF exponerar för att anpassa visuella objekt för alla element.

WPF-temaresurser lagras i inbäddade resursordlistor. Dessa resursordlistor måste bäddas in i en signerad sammansättning och kan antingen bäddas in i samma sammansättning som själva koden eller i en sida vid sida-sammansättning. För PresentationFramework.dll, sammansättningen som innehåller WPF-kontroller, finns temaresurser i en serie sammansättningar sida vid sida.

Temat blir den sista platsen att titta på när du söker efter formatet för ett element. Vanligtvis börjar sökningen genom att gå upp i elementträdet och söka efter en lämplig resurs och sedan titta i appresurssamlingen och slutligen fråga systemet. Detta ger apputvecklare en chans att omdefiniera formatmallen för alla objekt på träd- eller appnivå innan de når temat.

Du kan definiera resursordlistor som enskilda filer som gör att du kan återanvända ett tema i flera appar. Du kan också skapa utbytbara teman genom att definiera flera resursordlistor som tillhandahåller samma typer av resurser men med olika värden. Att omdefiniera dessa stilar eller andra resurser på appnivå är den rekommenderade metoden för att anpassa utseendet på en app.

Om du vill dela en uppsättning resurser, inklusive formatmallar och mallar, mellan appar, kan du skapa en XAML-fil och definiera en ResourceDictionary som innehåller referens till en shared.xaml fil.

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

Det är genom delningen av shared.xaml, som i sig definierar en ResourceDictionary som innehåller en uppsättning stil- och penselresurser, som gör att kontrollerna i en app kan ha ett enhetligt utseende.

Mer information finns i sammanfogade resursordlistor.

Om du skapar ett tema för din anpassade kontroll kan du läsa avsnittet Definiera resurser på temanivå i Control-redigeringsöversikten.

Se även