Bagikan melalui


Gaya dan templat (WPF .NET)

Gaya dan templat Windows Presentation Foundation (WPF) mengacu pada serangkaian fitur yang memungkinkan pengembang dan desainer menciptakan efek yang menarik secara visual dan penampilan yang konsisten untuk produk mereka. Saat menyesuaikan tampilan aplikasi, Anda menginginkan model gaya dan templat yang kuat yang memungkinkan pemeliharaan dan berbagi tampilan di dalam dan di antara aplikasi. WPF menyediakan model tersebut.

Fitur lain dari model gaya WPF adalah pemisahan presentasi dan logika. Desainer dapat mengerjakan tampilan aplikasi hanya dengan menggunakan XAML pada saat yang sama ketika pengembang mengerjakan logika pemrograman dengan menggunakan C# atau Visual Basic.

Gambaran umum ini berfokus pada aspek gaya dan templat aplikasi dan tidak membahas konsep pengikatan data apa pun. Untuk informasi tentang pengikatan data, lihat Gambaran Umum Pengikatan Data.

Penting untuk memahami sumber daya, yang memungkinkan gaya dan templat digunakan kembali. Untuk informasi selengkapnya tentang sumber daya, lihat Gambaran Umum sumber daya XAML.

Penting

Dokumentasi Panduan Desktop untuk .NET 7 dan .NET 6 sedang dibangun.

Sampel

Kode sampel yang disediakan dalam gambaran umum ini didasarkan pada aplikasi penjelajahan foto sederhana yang ditunjukkan dalam ilustrasi berikut.

Styled ListView

Sampel foto sederhana ini menggunakan gaya dan templat untuk menciptakan pengalaman pengguna yang menarik secara visual. Sampel memiliki dua TextBlock elemen dan ListBox kontrol yang terikat ke daftar gambar.

Untuk sampel lengkapnya, lihat Pengenalan Styling dan Sampel Templat.

Gaya

Anda dapat menganggap Style sebagai cara mudah untuk menerapkan sekumpulan nilai properti ke beberapa elemen. Anda dapat menggunakan gaya pada elemen apa pun yang berasal dari FrameworkElement atau FrameworkContentElement seperti Window atau Button.

Cara paling umum untuk mendeklarasikan gaya adalah sebagai sumber daya di Resources bagian dalam file XAML. Karena gaya adalah sumber daya, gaya mematuhi aturan cakupan yang sama yang berlaku untuk semua sumber daya. Sederhananya, di mana Anda mendeklarasikan gaya memengaruhi di mana gaya dapat diterapkan. Misalnya, jika Anda mendeklarasikan gaya dalam elemen akar file XAML definisi aplikasi Anda, gaya dapat digunakan di mana saja di aplikasi Anda.

Misalnya, kode XAML berikut mendeklarasikan dua gaya untuk TextBlock, satu secara otomatis diterapkan ke semua TextBlock elemen, dan yang lain yang harus direferensikan secara eksplisit.

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

Berikut adalah contoh gaya yang dideklarasikan di atas yang digunakan.

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

Styled textblocks

Untuk informasi selengkapnya, lihat Membuat gaya untuk kontrol.

ControlTemplates

Di WPF, ControlTemplate kontrol mendefinisikan tampilan kontrol. Anda dapat mengubah struktur dan tampilan kontrol dengan menentukan yang baru ControlTemplate dan menetapkannya ke kontrol. Dalam banyak kasus, templat memberi Anda fleksibilitas yang cukup sehingga Anda tidak perlu menulis kontrol kustom Anda sendiri.

Setiap kontrol memiliki templat default yang ditetapkan ke properti Control.Template . Templat menyambungkan presentasi visual kontrol dengan kemampuan kontrol. Karena Anda menentukan templat di XAML, Anda dapat mengubah tampilan kontrol tanpa menulis kode apa pun. Setiap templat dirancang untuk kontrol tertentu, seperti Button.

Biasanya Anda mendeklarasikan templat sebagai sumber daya di bagian Resources file XAML. Seperti semua sumber daya, aturan cakupan berlaku.

Templat kontrol jauh lebih terlibat daripada gaya. Ini karena templat kontrol menulis ulang tampilan visual seluruh kontrol, sementara gaya hanya menerapkan perubahan properti ke kontrol yang ada. Namun, karena templat kontrol diterapkan dengan mengatur properti Control.Template , Anda bisa menggunakan gaya untuk menentukan atau mengatur templat.

Desainer umumnya memungkinkan Anda membuat salinan templat yang sudah ada dan memodifikasinya. Misalnya, di perancang Visual Studio WPF, pilih CheckBox kontrol, lalu klik kanan dan pilih Edit templat>Buat salinan. Perintah ini menghasilkan gaya yang menentukan templat.

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

Mengedit salinan templat adalah cara yang bagus untuk mempelajari cara kerja templat. Alih-alih membuat templat kosong baru, lebih mudah untuk mengedit salinan dan mengubah beberapa aspek presentasi visual.

Misalnya, lihat Membuat templat untuk kontrol.

Pengikatan Templat

Anda mungkin telah memperhatikan bahwa sumber daya templat yang ditentukan di bagian sebelumnya menggunakan TemplateBinding Markup Extension. TemplateBinding adalah bentuk pengikatan yang dioptimalkan untuk skenario templat, dianalogikan dengan pengikatan yang dibangun dengan {Binding RelativeSource={RelativeSource TemplatedParent}}. TemplateBinding berguna untuk mengikat bagian templat ke properti kontrol. Misalnya, setiap kontrol memiliki BorderThickness properti . TemplateBinding Gunakan untuk mengelola elemen mana dalam templat yang dipengaruhi oleh pengaturan kontrol ini.

ContentControl dan ItemsControl

ContentPresenter Jika dideklarasikan dalam ContentControlControlTemplate , ContentPresenter akan secara otomatis mengikat properti ContentTemplate dan Content . Demikian juga, ItemsPresenter yang ada di ControlTemplate dalam ItemsControl akan secara otomatis mengikat properti ItemTemplate dan Items .

DataTemplates

Dalam aplikasi sampel ini, ada ListBox kontrol yang terikat ke daftar foto.

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

Ini saat ini ListBox terlihat seperti berikut ini.

ListBox before applying template

Sebagian besar kontrol memiliki beberapa jenis konten, dan konten tersebut sering berasal dari data yang Anda ikat. Dalam sampel ini, data adalah daftar foto. Di WPF, Anda menggunakan DataTemplate untuk menentukan representasi visual data. Pada dasarnya, apa yang Anda masukkan ke dalam DataTemplate menentukan seperti apa data di aplikasi yang dirender.

Di aplikasi sampel kami, setiap objek kustom Photo memiliki Source properti string jenis yang menentukan jalur file gambar. Saat ini, objek foto muncul sebagai jalur file.

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

Agar foto muncul sebagai gambar, Anda membuat DataTemplate sebagai sumber daya.

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

Perhatikan bahwa DataType properti mirip TargetType dengan properti .Style Jika Anda DataTemplate berada di bagian sumber daya, saat Anda menentukan DataType properti ke jenis dan menghilangkan x:Key, diterapkan setiap kali jenis tersebut DataTemplate muncul. Anda selalu memiliki opsi untuk menetapkan DataTemplate dengan dan x:Key kemudian mengaturnya sebagai StaticResource untuk properti yang mengambil DataTemplate jenis, seperti ItemTemplate properti atau ContentTemplate properti .

Pada dasarnya, DataTemplate dalam contoh di atas mendefinisikan bahwa setiap kali ada Photo objek, itu akan muncul sebagai Image dalam Border. Dengan ini DataTemplate, aplikasi kami sekarang terlihat seperti ini.

Photo image

Model templat data menyediakan fitur lain. Misalnya, jika Anda menampilkan data pengumpulan yang berisi koleksi lain menggunakan HeaderedItemsControl jenis seperti Menu atau TreeView, ada HierarchicalDataTemplate. Fitur templat data lain adalah DataTemplateSelector, yang memungkinkan Anda memilih DataTemplate untuk digunakan berdasarkan logika kustom. Untuk informasi selengkapnya, lihat Gambaran Umum Templat Data, yang menyediakan diskusi yang lebih mendalam tentang berbagai fitur templat data.

Memicu

Pemicu mengatur properti atau memulai tindakan, seperti animasi, saat nilai properti berubah atau saat peristiwa dinaikkan. Style, ControlTemplate, dan DataTemplate semua memiliki Triggers properti yang dapat berisi sekumpulan pemicu. Ada beberapa jenis pemicu.

PropertyTriggers

Trigger yang menetapkan nilai properti atau memulai tindakan berdasarkan nilai properti disebut pemicu properti.

Untuk menunjukkan cara menggunakan pemicu properti, Anda dapat membuat masing-masing ListBoxItem transparan sebagian kecuali dipilih. Gaya berikut mengatur Opacity nilai menjadi ListBoxItem0.5. Namun, ketika IsSelected properti adalah true, Opacity diatur ke 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>

Contoh ini menggunakan Trigger untuk mengatur nilai properti, tetapi perhatikan bahwa Trigger kelas juga memiliki EnterActions properti dan ExitActions yang memungkinkan pemicu untuk melakukan tindakan.

Perhatikan bahwa MaxHeight properti diatur ListBoxItem ke 75. Dalam ilustrasi berikut, item ketiga adalah item yang dipilih.

Styled ListView

EventTriggers dan Storyboards

Jenis pemicu lainnya adalah EventTrigger, yang memulai serangkaian tindakan berdasarkan terjadinya suatu peristiwa. Misalnya, objek berikut EventTrigger menentukan bahwa ketika penunjuk mouse memasukkan ListBoxItem, MaxHeight properti dianimasikan ke nilai 90 selama 0.2 periode kedua. Saat mouse menjauh dari item, properti akan kembali ke nilai asli selama periode 1 detik. Perhatikan bagaimana tidak perlu menentukan To nilai untuk MouseLeave animasi. Ini karena animasi dapat melacak nilai aslinya.

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

Untuk informasi selengkapnya, lihat Gambaran umum Storyboards.

Dalam ilustrasi berikut, mouse menunjuk ke item ketiga.

Styling sample screenshot

MultiTriggers, DataTriggers, dan MultiDataTriggers

Selain Trigger dan EventTrigger, ada jenis pemicu lainnya. MultiTrigger memungkinkan Anda mengatur nilai properti berdasarkan beberapa kondisi. Anda menggunakan DataTrigger dan MultiDataTrigger saat properti kondisi Anda terikat data.

Status Visual

Kontrol selalu dalam keadaan tertentu. Misalnya, ketika mouse bergerak di atas permukaan kontrol, kontrol dianggap dalam keadaan umum .MouseOver Kontrol tanpa status tertentu dianggap berada dalam keadaan umum Normal . Status dipecah menjadi grup, dan status yang disebutkan sebelumnya adalah bagian dari grup CommonStatesstatus . Sebagian besar kontrol memiliki dua grup status: CommonStates dan FocusStates. Dari setiap grup status yang diterapkan ke kontrol, kontrol selalu dalam satu status setiap grup, seperti CommonStates.MouseOver dan FocusStates.Unfocused. Namun, kontrol tidak dapat berada di dua status berbeda dalam grup yang sama, seperti CommonStates.Normal dan CommonStates.Disabled. Berikut adalah tabel status yang paling banyak dikenali dan digunakan kontrol.

Nama VisualState Nama VisualStateGroup Deskripsi
Normal CommonStates Status default.
MouseOver CommonStates Penunjuk mouse diposisikan di atas kontrol.
Ditekan CommonStates Kontrol ditekan.
Nonaktif CommonStates Kontrol dinonaktifkan.
Terfokus FocusStates Kontrol memiliki fokus.
Tidak fokus FocusStates Kontrol tidak memiliki fokus.

Dengan menentukan System.Windows.VisualStateManager pada elemen akar templat kontrol, Anda dapat memicu animasi saat kontrol memasuki status tertentu. Mendeklarasikan VisualStateManager kombinasi mana dari VisualStateGroup dan VisualState untuk menonton. Saat kontrol memasuki status ditonton, animasi yang ditentukan oleh VisualStateManager dimulai.

Misalnya, kode XAML berikut mengawasi CommonStates.MouseOver status untuk menganimasikan warna isian elemen bernama backgroundElement. Saat kontrol kembali ke status CommonStates.Normal , warna isian elemen bernama backgroundElement dipulihkan.

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

        ...

Untuk informasi selengkapnya tentang papan cerita, lihat Gambaran Umum Papan Cerita.

Sumber daya dan tema bersama

Aplikasi WPF umum mungkin memiliki beberapa sumber daya UI yang diterapkan di seluruh aplikasi. Secara kolektif, set sumber daya ini dapat dianggap sebagai tema untuk aplikasi. WPF menyediakan dukungan untuk mengemas sumber daya UI sebagai tema dengan menggunakan kamus sumber daya yang dienkapsulasi sebagai ResourceDictionary kelas .

Tema WPF didefinisikan dengan menggunakan mekanisme gaya dan templat yang diekspos WPF untuk menyesuaikan visual elemen apa pun.

Sumber daya tema WPF disimpan dalam kamus sumber daya yang disematkan. Kamus sumber daya ini harus disematkan dalam rakitan yang ditandatangani, dan dapat disematkan dalam rakitan yang sama dengan kode itu sendiri atau di rakitan berdampingan. Untuk PresentationFramework.dll, rakitan yang berisi kontrol WPF, sumber daya tema berada dalam serangkaian rakitan berdampingan.

Tema ini menjadi tempat terakhir untuk dilihat saat mencari gaya elemen. Biasanya, pencarian akan dimulai dengan menelusuri pohon elemen yang mencari sumber daya yang sesuai, lalu melihat dalam koleksi sumber daya aplikasi dan akhirnya mengkueri sistem. Ini memberi pengembang aplikasi kesempatan untuk menentukan ulang gaya untuk objek apa pun di tingkat pohon atau aplikasi sebelum mencapai tema.

Anda dapat menentukan kamus sumber daya sebagai file individual yang memungkinkan Anda menggunakan kembali tema di beberapa aplikasi. Anda juga dapat membuat tema yang dapat ditukar dengan menentukan beberapa kamus sumber daya yang menyediakan jenis sumber daya yang sama tetapi dengan nilai yang berbeda. Mendefinisikan ulang gaya ini atau sumber daya lain di tingkat aplikasi adalah pendekatan yang direkomendasikan untuk menguliti aplikasi.

Untuk berbagi sekumpulan sumber daya, termasuk gaya dan templat, di seluruh aplikasi, Anda dapat membuat file XAML dan menentukan ResourceDictionary yang menyertakan referensi ke shared.xaml file.

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

Ini adalah berbagi , shared.xamlyang sendiri mendefinisikan ResourceDictionary yang berisi sekumpulan gaya dan sumber daya kuas, yang memungkinkan kontrol dalam aplikasi untuk memiliki tampilan yang konsisten.

Untuk informasi selengkapnya, lihat Kamus sumber daya gabungan.

Jika Anda membuat tema untuk kontrol kustom Anda, lihat bagian Menentukan sumber daya di bagian tingkat tema dari gambaran umum penulisan kontrol.

Baca juga