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. Większość scenariuszy, które wymagają utworzenia nowej kontrolki, może zamiast tego utworzyć nowy szablon dla istniejącej kontrolki.

W tym artykule zapoznasz się z tworzeniem nowej ControlTemplate kontrolki Button .

Kiedy utworzyć kontrolkęTemplate

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ć Foreground właściwość na niebieską i FontStyle kursywę CheckBoxna . Jeśli chcesz dostosować wygląd kontrolki poza ustawieniem innych właściwości kontrolki, możesz utworzyć element 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ę dziedziczą po przycisku lub odtworzyć funkcjonalność przycisku. 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 obiekt z żądanym układem wizualnym.

Z drugiej strony, jeśli potrzebujesz kontrolki z nowymi funkcjami, różnymi właściwościami i nowymi ustawieniami, utworzysz nowy UserControlelement .

Wymagania wstępne

Utwórz nową aplikację WPF i w pliku MainWindow.xaml (lub innym wybranym oknie) ustaw następujące właściwości w elemecie< Window>:

Właściwości Wartość
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Ustaw zawartość <elementu Window> 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 końcu plik MainWindow.xaml powinien wyglądać podobnie do następującego:

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

WPF window with two unstyled buttons

Tworzenie elementu ControlTemplate

Najbardziej typowym sposobem deklarowania elementu ControlTemplate jest jako zasób w Resources sekcji 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.

Aby rozpocząć od, dodaj Window.Resources element 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 nowy <element ControlTemplate> z następującym zestawem właściwości:

Właściwości Wartość
x:Key roundbutton
TargetType Button

Ten szablon kontrolki będzie prosty:

  • element główny dla kontrolki, a Grid
  • , Ellipse aby narysować zaokrąglony wygląd przycisku
  • , ContentPresenter aby wyświetlić zawartość przycisku określonego 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 ControlTemplateelementu nadal możesz chcieć 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 właściwości publicznej zdefiniowanej przez kontrolkę. Gdy używasz szablonu TemplateBinding, możesz włączyć właściwości kontrolki, 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 na nim właściwość TemplateBinding .

Elipsa

Zwróć uwagę, że Fill właściwości <i Stroke elementu Wielokropek> są powiązane z właściwościami i Background kontrolkiForeground.

Contentpresenter

Element <ContentPresenter> jest również dodawany do szablonu. 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, ponieważ zawartość może być prezentowana za pośrednictwem <elementu ContentPresenter>, co robi szablon.

Jeśli element ControlTemplate jest stosowany do ContentControl typu, takiego jak Button, element ContentPresenter jest wyszukiwany w drzewie elementów. Jeśli element ContentPresenter zostanie znaleziony, szablon automatycznie powiąże właściwość kontrolki Content z elementem ContentPresenter.

Użyj 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ść drugiego przycisku Template na roundbutton zasób:

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

WPF window with one template oval button

Być może zauważysz, że przycisk nie jest okręgiem, ale jest niesymetryczny. Ze względu na sposób <działania elementu elipsy> zawsze rozszerza się, aby wypełnić dostępne miejsce. Ustaw okrąg jako jednolity, zmieniając właściwości i height przycisku width 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>

WPF window with one template circular button

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 Click zdarzenie zostanie wyzwolony. 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ść przycisku IsMouseOver . Gdy mysz jest nad kontrolką, styl <wielokropka> z nowym kolorem. Ten typ wyzwalacza jest znany jako WłaściwośćTrigger.

Aby to zadziałało, należy dodać nazwę do <wielokropka> , do którego można się odwołać. Nadaj jej nazwę backgroundElement.

<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />

Następnie dodaj nowy Trigger element do kolekcji ControlTemplate.Triggers . Wyzwalacz będzie obserwować IsMouseOver zdarzenie dla 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 <element Setter> do <wyzwalacza>, który zmienia właściwość <>Fill wielokropka na nowy kolor.

<Trigger Property="IsMouseOver" Value="true">
    <Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>

Uruchamianie projektu. Zwróć uwagę, że po przesunięciu myszy na przycisk zmienia się kolor wielokropka>.<

mouse moves over WPF button to change the fill color

Korzystanie z elementu VisualState

Stany wizualne są definiowane i wyzwalane przez kontrolkę. Na przykład po przeniesieniu myszy na wierzchu kontrolki CommonStates.MouseOver stan jest wyzwalany. Możesz animować zmiany właściwości na podstawie bieżącego stanu kontrolki. W poprzedniej sekcji właściwośćTrigger> została użyta do zmiany pierwszego planu przycisku na AliceBlue wartość , gdy IsMouseOver właściwość to 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 wizualizacjiStates, zobacz Style i szablony w WPF.

Aby przekonwertować element 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 katalogu głównym grid> szablonu kontrolki dodaj< element VisualStateManager.VisualStateGroups> z elementem< VisualStateGroup> for 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 wizualizacji VisualState> są stosowane po wyzwoleniu <tego stanu. Tworzenie animacji dla każdego stanu. Animacje są umieszczane wewnątrz elementu scenorysu><. Aby uzyskać więcej informacji na temat scenorysów, zobacz Storyboards Overview (Omówienie scenorysów).

  • Normalna

    Ten stan animuje wypełnienie wielokropka, przywracając go do koloru kontrolki Background .

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
            To="{TemplateBinding Background}"
            Duration="0:0:0.3"/>
    </Storyboard>
    
  • Mouseover

    Ten stan animuje kolor wielokropka Background na nowy kolor: Yellow.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
            To="Yellow" 
            Duration="0:0:0.3"/>
    </Storyboard>
    

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

Uruchamianie projektu. Zwróć uwagę, że po przesunięciu myszy nad przyciskiem kolor <> wielokropka animuje.

mouse moves over WPF button to change visual state

Następne kroki