Kontrolki WPF zostały przygotowane w ten sposób, aby łatwo można modyfikować ich wygląd. Definicję wyglądu oddzielono całkowicie od kodu definiującego funkcjonalność.
Szablony kontrolek tworzone są w XAML-u. Szablon zawiera kompletny opis wyglądu danej kontrolki.
Przykładowy szablon dla przycisku możemy zdefiniować następująco:
Code:
<Button>
<Button.Template>
<ControlTemplate>
<Rectangle Fill="Red" />
</ControlTemplate>
</Button.Template>
</Button>
Definicja szablonu zawarta jest w elemencie
<ControlTemplate>.
Ważnym aspektem jest umożliwienie nadawania zawartości (
Content) przycisku. Aby przywrócić tą funkcjonalność, należy w szablonie dodać specjalną kontrolkę przeznaczoną do wyświetlania zawartości -
ContentPresenter:
Code:
<Button Content="Button!">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="Red">
</Rectangle>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
W przypadku gdy korzystamy z kontrolki
ContentPresenter musimy w definicji
ControlTemplate umieścić dla jakiego typu definiujemy ContentPresenter-a.
Szablony najczęściej definiujemy jako zasoby (w celach użycia w wielu miejscach):
Code:
<Window.Resources>
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="Red">
</Rectangle>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Window.Resources>
<Grid>
<Button Content="Button!" Template="{StaticResource ButtonTemplate}" />
</Grid>
Triggery
Zmiana wyglądu kontrolki to nie wszystko. Kolejnym elementem podczas definiowania wyglądu kontrolki jest jej zachowanie się podczas interakcji z użytkownikiem. Jeżeli przyjrzymy się przyciskowi to w momencie gdy na niego najeżdżamy kursorem, zostaje podświetlony.
Zachowania tego typu definiują Triggery.
ControlTemplate zawiera kolekcję obiektów typu
Trigger.
Definicja Trigger-ów dla kontrolki przycisku:
Code:
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<Rectangle Name="rectangle" Fill="Red">
</Rectangle>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="rectangle" Value="Yellow" Property="Fill" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Do zapamiętania:
- Triggery odwołują się do konkretnych nazwanych elementów szablonu
- Szablon musi być zdefiniowany jako pierwszy w kodzie XAML
- Trigery mogą zawierać animacje
Warto w tym miejscu zauważyć jedną rzecz. Po napisaniu poniższego kodu:
Code:
<Button Content="Button!" Background="Blue">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="Red"></Rectangle>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"></ContentPresenter>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
przycisk nie zmienił koloru wypełnienia na niebieski. Aby możliwe było przypisanie koloru do naszego prostokąta należy skorzystać ze specjalnego rodzaju bindowania - bindowania do szablonu -
Template Binding. Składnia
{TemplateBinding <PropertyName>}. PropertyName - nazwa właściwości kontrolki. Tak więc
TemplateBinding przekazuje wartość ustawioną na kontrolce do właściwości w szablonie.
Poniżej przykład w jaki sposób sprawić żeby nasz klawisz miał kolor taki, jaki ustawimy we właściwości
Background:
Code:
<Button Content="Button!" Background="Blue">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="{TemplateBinding Background}"></Rectangle>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"></ContentPresenter>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
Efekt:
W przypadku
TemplateBinding istnieje problem z obiektami typu
Freezable (np. obiekt
Brush - pędzel). Aby ominąć ten problem można zastosować trik polegający na wykorzystaniu bindowania
RelativeSource:
Code:
<Button Content="Button!">
<Button.Background>
<LinearGradientBrush StartPoint="0, 0" EndPoint="1, 1">
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="Chartreuse" Offset="0.5" />
</LinearGradientBrush>
</Button.Background>
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle Name="recBackground" Fill="{TemplateBinding Background}" />
<Ellipse Name="ellipse" Fill="Red"></Ellipse>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ellipse" Property="Fill" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Dzięki temu zabiegowi elipsa po najechaniu będzie mieć taki sam kolor jak tło.
Dodatkowo możemy za pomocą stylów, nadać taki sam szablon dla wszystkich klawiszy w oknie:
Code:
<Style TargetType="{x:Type Button}">
<Setter Property="Template" Value="{StaticResource ButtonTemplate}" />
</Style>
Pobieranie stylów kontrolek WPF
Szablon kontrolki możemy definiować od początku, bądź też pobrać go w wersji domyślnej i dostosować do naszych potrzeb. Kod pozwalający pobrać szablon kontrolki:
Code:
using (var file = new FileStream("template.xml", FileMode.CreateNew))
{
XamlWriter.Save(btnMyButton.Template, file);
}
Brak komentarzy:
Prześlij komentarz