Udostępnij za pośrednictwem


Jak utworzyć szablon dla kontrolki (WPF.NET)

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 przedstawiono tworzenie nowej ControlTemplate 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ę dziedziczącą po przycisku lub odtworzyć funkcjonalność przycisku. Ponadto nowa kontrolka użytkownika udostępnia wizualizację cykliczną.

Możesz uniknąć tworzenia nowych kontrolek, dostosowując układ wizualny istniejącej kontrolki. W przypadku przycisku zaokrąglonego należy utworzyć obiekt ControlTemplate z żądanym układem wizualnym.

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

Wymagania wstępne

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

Majątek 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 końcu plik MainWindow.xaml powinien wyglądać podobnie do następującego kodu XAML:

<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 jak na poniższej ilustracji:

Okno WPF z dwoma niestylowymi przyciskami

Utwórz ControlTemplate

Najczęstszym sposobem deklarowania ControlTemplate jest jako zasób w sekcji Resources w pliku XAML. Ponieważ szablony są zasobami, są zgodne z tymi samymi regułami określania zakresu co wszystkie zasoby. Miejsce, w którym deklarujesz szablon, wpływa na to, gdzie możesz go zastosować. Jeśli na przykład zadeklarujesz szablon w elemecie głównym pliku XAML definicji aplikacji, możesz użyć szablonu w dowolnym miejscu w aplikacji. Jeśli zdefiniujesz szablon w oknie, tylko kontrolki w tym oknie mogą używać szablonu.

Aby rozpocząć, 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 nową <kontrolkę ControlTemplate> i ustaw następujące właściwości:

Majątek Wartość
x:Key roundbutton
TargetType Button

Ten szablon kontrolki jest 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 ControlTemplateelementu możesz nadal 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 z publiczną właściwością definiowaną przez kontrolkę. W przypadku używania TemplateBindingmożna włączyć właściwości kontrolki tak, aby działały jako parametry szablonu. Po ustawieniu właściwości kontrolki wartość jest przekazywana do elementu, który ma właściwość TemplateBinding.

Elipsa

Właściwości Fill i Stroke elementu Ellipse są powiązane z właściwościami kontrolki .

Prezenter treści

Szablon zawiera <również element ContentPresenter> . Ponieważ ten szablon jest przeznaczony dla przycisku, należy pamiętać, że przycisk dziedziczy z ContentControl. Przycisk wyświetla zawartość elementu. Możesz ustawić dowolne elementy wewnątrz przycisku, takie jak zwykły tekst, a nawet inna kontrolka. Oba poniższe przykłady są prawidłowymi przyciskami:

<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 zastosujesz ControlTemplate na typ ContentControl, taki jak Button, szablon szuka ContentPresenter w drzewie elementów. Jeśli znajdzie ContentPresenterelement , szablon automatycznie powiąże właściwość kontrolki Content z elementem 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ść 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.

Okno WPF z jednym szablonowym przyciskiem owalnym

Możesz zauważyć, ż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>

Okno WPF z jednym przyciskiem o okrągłym kształcie według szablonu

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. Szablon definiuje te interakcje wizualne.

Korzystając z dynamicznych systemów zdarzeń i właściwości zapewnianych przez 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 obserwujesz właściwość przycisku IsMouseOver . Gdy mysz znajduje się nad kontrolką, zmień styl <elipsy> na nowy kolor. Ten typ wyzwalacza jest znany jako PropertyTrigger.

Aby ta funkcja działała, należy dodać nazwę do <elipsy>, do której można się odwołać. Nadaj mu nazwę backgroundElement.

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

Następnie dodaj nową Trigger do kolekcji ControlTemplate.Triggers. Wyzwalacz obserwuje 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>

Uruchamianie projektu. Po przeniesieniu myszy na przycisk zmienia się kolor <elipsy>.

mysz przesuwa się przez przycisk WPF, aby zmienić kolor wypełnienia

Korzystanie z elementu VisualState

Stany wizualne są definiowane i wyzwalane przez kontrolkę. Na przykład, po przeniesieniu myszy nad elementem sterującym, element sterujący wyzwala CommonStates.MouseOver stan. Możesz animować zmiany właściwości na podstawie bieżącego stanu kontrolki. W poprzedniej sekcji użyto <PropertyTrigger>, aby zmienić tło przycisku na AliceBlue, gdy właściwość IsMouseOver była ustawiona na 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ć element <PropertyTrigger> na animowany stan wizualizacji, 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>

Zastosuj wszelkie animacje zdefiniowane w <VisualState>, gdy ten stan zostanie wyzwolony. Tworzenie animacji dla każdego stanu. Umieść animacje wewnątrz elementu scenorysu<>. Aby uzyskać więcej informacji na temat scenorysów, zobacz Storyboards Overview.

  • Standardowy

    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>
    

Element <ControlTemplate> powinien teraz wyglądać podobnie do poniższego kodu.

<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. Po przeniesieniu myszy na przycisku kolor <Elipsy> animuje.

najechanie myszą na przycisk WPF, aby zmienić kolor wypełnienia przy użyciu stanu wizualnego

Dalsze kroki