Jak utworzyć szablon dla kontrolki (WPF.NET)

Za pomocą Windows Presentation Foundation (WPF) można dostosować strukturę wizualizacji 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 być objęta zamiast tego tworzenie nowego szablonu dla istniejącej kontrolki.

Ważne

Dokumentacja przewodnika po pulpicie dla platform .NET 6 i .NET 5 (w tym .NET Core 3.1) jest w trakcie budowy.

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ę na .CheckBox 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, utwórz 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ść 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>

W 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 następująco:

WPF window with two unstyled buttons

Tworzenie kontrolkiTemplate

Najczęstszym sposobem deklarowania elementu ControlTemplate jest zasób w sekcji w Resources pliku XAML. Ponieważ szablony są zasobami, są zgodne z tymi samymi regułami określania zakresu, które mają zastosowanie do wszystkich zasobów. Po prostu deklarowanie szablonu ma wpływ na miejsce zastosowania szablonu. Jeśli na przykład zadeklarujesz szablon w elemecie 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ącymi właściwościami:

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

Ten szablon kontrolki będzie prosty:

  • element główny kontrolki, a Grid
  • , Ellipse aby narysować zaokrąglony wygląd przycisku
  • a 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>

SzablonBinding

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ę. W przypadku korzystania z szablonu TemplateBinding można 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 elementu elipsy> są powiązane z właściwościami i Background kontrolkiForeground.Stroke

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 ContentControlelementu . 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żna przedstawić za pomocą <elementu ContentPresenter>, co robi szablon.

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

Korzystanie z szablonu

Znajdź przyciski 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 roundbutton przycisku Template na 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żyłeś, ż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 width właściwości przycisku i height 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. Jednak być może zauważysz, że po przeniesieniu myszy nad przyciskiem wizualizacje przycisku nie zmieniają się. Te interakcje wizualne są definiowane przez szablon.

Dzięki dynamicznym systemom zdarzeń i właściwości, które zapewnia WPF, można obserwować określoną 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 <Ellipse> z nowym kolorem. Ten typ wyzwalacza jest znany jako WłaściwośćTrigger.

Aby można było to zrobić, musisz dodać nazwę do wielokropka>, do< którego możesz się odwoływać. Nadaj mu 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>

Uruchom projekt. Zwróć uwagę, że po przeniesieniu myszy nad przyciskiem kolor <wielokropka> zmienia się.

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. Zmiany właściwości można animować 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ść miała wartość 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 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 siatki> 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 elemecie VisualState> są stosowane po wyzwoleniu< tego stanu. Utwórz animacje 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).

  • Normalne

    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>

Uruchom projekt. Zwróć uwagę, że po przeniesieniu myszy nad przyciskiem kolor <wielokropka> animuje.

mouse moves over WPF button to change the fill color with a visual state

Następne kroki