Tworzenie szablonu dla kontrolki
Za pomocą programu Windows Presentation Foundation (WPF) można dostosować strukturę i zachowanie istniejącej kontrolki przy użyciu własnego szablonu wielokrotnego użytku. Szablony można stosować globalnie do aplikacji, okien i stron lub bezpośrednio do kontrolek. W większości scenariuszy, które wymagają utworzenia nowej kontrolki, można zamiast tego utworzyć nowy szablon dla istniejącej kontrolki.
W tym artykule dowiesz się, jak utworzyć nową ControlTemplate dla kontrolki Button.
Kiedy utworzyć szablon kontrolki
Kontrolki mają wiele właściwości, takich jak Background, Foregroundi FontFamily. Te właściwości kontrolują różne aspekty wyglądu kontrolki, ale zmiany, które można wprowadzić, ustawiając te właściwości, są ograniczone. Można na przykład ustawić właściwość Foreground na niebieską i FontStyle kursywą na CheckBox. Jeśli chcesz dostosować wygląd kontrolki poza ustawieniem innych właściwości kontrolki, możesz utworzyć ControlTemplate.
W większości interfejsów użytkownika przycisk ma ten sam ogólny wygląd: prostokąt z tekstem. Jeśli chcesz utworzyć przycisk zaokrąglony, możesz utworzyć nową kontrolkę, która dziedziczy po przycisku, lub odtworzyć jego funkcjonalność. Ponadto nowa kontrolka użytkownika zapewni wizualizację cykliczną.
Możesz uniknąć tworzenia nowych kontrolek, dostosowując układ wizualny istniejącej kontrolki. Za pomocą zaokrąglonego przycisku utworzysz ControlTemplate z żądanym układem wizualnym.
Z drugiej strony, jeśli potrzebujesz kontrolki z nowymi funkcjami, różnymi właściwościami i nowymi ustawieniami, utwórz nowy UserControl.
Warunki wstępne
Utwórz nową aplikację WPF i w MainWindow.xaml (lub innym wybranym oknie) ustaw następujące właściwości dla elementu <>:
Własność | Wartość |
---|---|
Title | Template Intro Sample |
SizeToContent | WidthAndHeight |
MinWidth | 250 |
Ustaw zawartość elementu okna <> na następujący kod XAML:
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
Na koniec plik MainWindow.xaml powinien wyglądać podobnie do poniższego:
<Window x:Class="IntroToStylingAndTemplating.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:IntroToStylingAndTemplating"
mc:Ignorable="d"
Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
</Window>
Jeśli uruchomisz aplikację, wygląda to następująco:
Utwórz ControlTemplate
Najczęstszym sposobem deklarowania ControlTemplate jest jako zasób w sekcji Resources
w pliku XAML. Ponieważ szablony są zasobami, przestrzegają tych samych reguł określania zakresu, które mają zastosowanie do wszystkich zasobów. Mówiąc prościej, gdzie deklarujesz szablon, ma wpływ na to, gdzie można zastosować szablon. Jeśli na przykład zadeklarujesz szablon w elemercie głównym pliku XAML definicji aplikacji, szablon może być używany w dowolnym miejscu w aplikacji. Jeśli zdefiniujesz szablon w oknie, tylko kontrolki w tym oknie mogą używać szablonu.
Na początek dodaj element Window.Resources
do pliku MainWindow.xaml.
<Window x:Class="IntroToStylingAndTemplating.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:IntroToStylingAndTemplating"
mc:Ignorable="d"
Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
<Window.Resources>
</Window.Resources>
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
</Window>
Utwórz nową <ControlTemplate> z następującym zestawem właściwości:
Własność | Wartość |
---|---|
x:Key | roundbutton |
TargetType | Button |
Ten szablon kontrolki będzie prosty:
- element główny dla kontrolki, Grid
- Ellipse do rysowania zaokrąglonego wyglądu przycisku
- ContentPresenter do wyświetlania zawartości przycisku wskazanego przez użytkownika
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
Powiązanie szablonu
Podczas tworzenia nowego ControlTemplatenadal możesz użyć właściwości publicznych, aby zmienić wygląd kontrolki. Rozszerzenie znaczników TemplateBinding wiąże właściwość elementu, który znajduje się w ControlTemplate z właściwością publiczną zdefiniowaną przez kontrolkę. W przypadku używania TemplateBindingmożna włączyć właściwości kontrolki tak, aby działały jako parametry szablonu. Oznacza to, że gdy właściwość kontrolki jest ustawiona, ta wartość jest przekazywana do elementu, który ma TemplateBinding.
Elipsa
Zwróć uwagę, że właściwości Fill i Stroke elementu <wielokropka> są powiązane z właściwościami Foreground i Background kontrolki.
ContentPresenter
Do szablonu jest również dodawany element <ContentPresenter>. Ponieważ ten szablon jest przeznaczony dla przycisku, należy wziąć pod uwagę, że przycisk dziedziczy z ContentControl. Przycisk przedstawia zawartość elementu. Możesz ustawić dowolny element wewnątrz przycisku, na przykład zwykły tekst, a nawet inną kontrolkę. Oba poniższe przyciski są prawidłowe:
<Button>My Text</Button>
<!-- and -->
<Button>
<CheckBox>Checkbox in a button</CheckBox>
</Button>
W obu poprzednich przykładach tekst i pole wyboru są ustawione jako właściwość Button.Content. Niezależnie od tego, co jest ustawione jako zawartość, może być ona prezentowana za pomocą <ContentPresenter>, co robi właśnie szablon.
Jeśli ControlTemplate jest stosowany do typu ContentControl, takiego jak Button
, to w drzewie elementów wyszukiwany jest ContentPresenter. Jeśli ContentPresenter
zostanie znaleziony, szablon automatycznie powiąże właściwość Content kontrolki z ContentPresenter
.
Korzystanie z szablonu
Znajdź przyciski, które zostały zadeklarowane na początku tego artykułu.
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
Ustaw właściwość Template drugiego przycisku na zasób roundbutton
:
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>
Jeśli uruchomisz projekt i przyjrzysz się wynikowi, zobaczysz, że przycisk ma zaokrąglone tło.
Być może zauważysz, że przycisk nie jest okręgiem, ale jest niesymetryczny. Ze względu na sposób działania elementu <wielokropka> zawsze rozszerza się, aby wypełnić dostępne miejsce. Ustaw okrąg jako jednolity, zmieniając width i height właściwości przycisku na tę samą wartość:
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button Template="{StaticResource roundbutton}" Width="65" Height="65">Button 2</Button>
</StackPanel>
Dodawanie wyzwalacza
Mimo że przycisk z zastosowanym szablonem wygląda inaczej, zachowuje się tak samo jak każdy inny przycisk. Po naciśnięciu przycisku zostanie wywołane zdarzenie Click. Można jednak zauważyć, że po przeniesieniu myszy nad przyciskiem wizualizacje przycisku nie ulegają zmianie. Wszystkie te interakcje wizualne są definiowane przez szablon.
Dzięki dynamicznym systemom zdarzeń i właściwości zapewnianym przez WPF można obserwować konkretną właściwość dla wartości, a następnie w razie potrzeby ponownie stylować szablon. W tym przykładzie zobaczysz właściwość IsMouseOver przycisku. Gdy mysz znajduje się nad kontrolką, zmień styl <elipsy> na nowy kolor. Ten typ wyzwalacza jest znany jako PropertyTrigger.
Aby to zadziałało, należy dodać nazwę do elipsy <>, aby można się było do niej odwoływać. Nadaj mu nazwę backgroundElement.
<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
Następnie dodaj nową Trigger do kolekcji ControlTemplate.Triggers. Wyzwalacz będzie monitorować zdarzenie IsMouseOver
pod kątem wartości true
.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Następnie dodaj <Setter> do <wyzwalacza>, który zmienia właściwość Fill obiektu <elipsa> na nowy kolor.
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>
Uruchom projekt. Zwróć uwagę, że po przesunięciu myszy na przycisk zmienia się kolor <wielokropka>.
Korzystanie z elementu VisualState
Stany wizualne są definiowane i wyzwalane przez kontrolkę. Na przykład, gdy myszka jest przesuwana nad kontrolką, wyzwolony zostanie stan CommonStates.MouseOver
. Możesz animować zmiany właściwości na podstawie bieżącego stanu kontrolki. W poprzedniej sekcji użyto <PropertyTrigger>, aby zmienić kolor pierwszego planu przycisku na AliceBlue
, gdy właściwość IsMouseOver
była true
. Zamiast tego utwórz stan wizualny, który animuje zmianę tego koloru, zapewniając płynne przejście. Aby uzyskać więcej informacji na temat VisualStates, zobacz WPF Style i szablony.
Aby przekonwertować <PropertyTrigger> na animowany stan wizualizacji, najpierw usuń element <ControlTemplate.Triggers> z szablonu.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
Następnie w <Grid> głównej części szablonu kontrolki dodaj element <VisualStateManager.VisualStateGroups> z <VisualStateGroup> dla CommonStates
. Zdefiniuj dwa stany, Normal
i MouseOver
.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
</VisualState>
<VisualState Name="MouseOver">
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
Wszystkie animacje zdefiniowane w <VisualState> są stosowane, gdy ten stan zostanie wyzwolony. Tworzenie animacji dla każdego stanu. Animacje są umieszczane wewnątrz elementu <Storyboard>. Aby uzyskać więcej informacji na temat scenorysów, zobacz Storyboards Overview.
Normalny
Ten stan animuje wypełnienie elipsy, przywracając kolor
Background
do kontrolki.<Storyboard> <ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="{TemplateBinding Background}" Duration="0:0:0.3"/> </Storyboard>
Najechanie kursorem
Ten stan animuje elipsę
Background
, zmieniając jej kolor na:Yellow
.<Storyboard> <ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="Yellow" Duration="0:0:0.3"/> </Storyboard>
<ControlTemplate> powinny teraz wyglądać następująco.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<Storyboard>
<ColorAnimation Storyboard.TargetName="backgroundElement"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="{TemplateBinding Background}"
Duration="0:0:0.3"/>
</Storyboard>
</VisualState>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="backgroundElement"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="Yellow"
Duration="0:0:0.3"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
Uruchom projekt. Zwróć uwagę, że po najechaniu myszą na przycisk kolor elipsy <> animuje się.
Następne kroki
.NET Desktop feedback