Come creare un modello per un controllo (WPF.NET)

Con Windows Presentation Foundation (WPF), è possibile personalizzare la struttura visiva e il comportamento di un controllo esistente con il proprio modello riutilizzabile. I modelli possono essere applicati a livello globale all'applicazione, alle finestre e alle pagine o direttamente ai controlli. La maggior parte degli scenari che richiedono la creazione di un nuovo controllo può essere coperta dalla creazione di un nuovo modello per un controllo esistente.

Importante

La documentazione di Desktop Guide per .NET 7 e .NET 6 è in fase di costruzione.

In questo articolo si esaminerà la creazione di un nuovo ControlTemplate oggetto per il Button controllo .

Quando creare un ControlTemplate

I controlli hanno molte proprietà, ad esempio Background, Foregrounde FontFamily. Queste proprietà controllano aspetti diversi dell'aspetto del controllo, ma le modifiche che è possibile apportare impostando queste proprietà sono limitate. Ad esempio, è possibile impostare la Foreground proprietà su blu e FontStyle su corsivo su un oggetto CheckBox. Quando si desidera personalizzare l'aspetto del controllo oltre all'impostazione delle altre proprietà nel controllo, è possibile creare un oggetto ControlTemplate.

Nella maggior parte delle interfacce utente, un pulsante ha lo stesso aspetto generale: un rettangolo con un testo. Se si desidera creare un pulsante arrotondato, è possibile creare un nuovo controllo che eredita dal pulsante o ricreare la funzionalità del pulsante. Inoltre, il nuovo controllo utente fornirà l'oggetto visivo circolare.

È possibile evitare di creare nuovi controlli personalizzando il layout visivo di un controllo esistente. Con un pulsante arrotondato, si crea un oggetto ControlTemplate con il layout visivo desiderato.

D'altra parte, se è necessario un controllo con nuove funzionalità, proprietà diverse e nuove impostazioni, si creerebbe un nuovo UserControloggetto .

Prerequisiti

Creare una nuova applicazione WPF e in MainWindow.xaml (o in un'altra finestra di propria scelta) impostare le proprietà seguenti nell'elemento< Window>:

Proprietà valore
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Impostare il contenuto dell'elemento <Window> sul codice XAML seguente:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

Alla fine, il file MainWindow.xaml dovrebbe essere simile al seguente:

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

Se si esegue l'applicazione, l'applicazione avrà un aspetto simile al seguente:

WPF window with two unstyled buttons

Creare un ControlTemplate

Il modo più comune per dichiarare un ControlTemplate oggetto è rappresentato da una risorsa nella Resources sezione in un file XAML. Poiché i modelli sono risorse, obbediscono alle stesse regole di ambito applicabili a tutte le risorse. È sufficiente, dove si dichiara un modello influisce sulla posizione in cui è possibile applicare il modello. Ad esempio, se dichiari il modello nell'elemento radice del file XAML di definizione dell'applicazione, il modello può essere usato in qualsiasi punto dell'applicazione. Se si definisce il modello in una finestra, solo i controlli in tale finestra possono usare il modello.

Per iniziare, aggiungere un Window.Resources elemento al file 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>

Creare un nuovo <ControlTemplate> con le proprietà seguenti impostate:

Proprietà valore
x:Key roundbutton
TargetType Button

Questo modello di controllo sarà semplice:

  • un elemento radice per il controllo , un Grid
  • un Ellipse oggetto per disegnare l'aspetto arrotondato del pulsante
  • per ContentPresenter visualizzare il contenuto del pulsante specificato dall'utente
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBinding

Quando si crea un nuovo ControlTemplateoggetto , è comunque possibile usare le proprietà pubbliche per modificare l'aspetto del controllo. L'estensione di markup TemplateBinding associa una proprietà di un elemento incluso in ControlTemplate a una proprietà pubblica definita dal controllo . Quando si usa templateBinding, si abilitano le proprietà del controllo per agire come parametri per il modello. Ovvero, quando si imposta una proprietà per un controllo, tale valore viene passato all'elemento che dispone del TemplateBinding.

Ellisse

Si noti che le proprietà e Stroke dell'elemento <Ellipse> sono associate alle proprietà e Background del Foreground controllo.Fill

ContentPresenter

Viene aggiunto anche un <elemento ContentPresenter> al modello. Poiché questo modello è progettato per un pulsante, prendere in considerazione che il pulsante eredita da ContentControl. Il pulsante presenta il contenuto dell'elemento. È possibile impostare qualsiasi elemento all'interno del pulsante, ad esempio testo normale o anche un altro controllo. Entrambi i pulsanti seguenti sono validi:

<Button>My Text</Button>

<!-- and -->

<Button>
    <CheckBox>Checkbox in a button</CheckBox>
</Button>

In entrambi gli esempi precedenti il testo e la casella di controllo vengono impostati come proprietà Button.Content . Qualsiasi elemento impostato come contenuto può essere presentato tramite contentPresenter<>, ovvero ciò che fa il modello.

Se l'oggetto ControlTemplate viene applicato a un ContentControl tipo, ad esempio Button, un ContentPresenter oggetto viene cercato nell'albero degli elementi. ContentPresenter Se viene trovato , il modello associa automaticamente la proprietà del Content controllo a ContentPresenter.

Usare il modello

Trovare i pulsanti dichiarati all'inizio di questo articolo.

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

Impostare la proprietà del Template secondo pulsante sulla roundbutton risorsa:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>

Se si esegue il progetto e si esamina il risultato, si noterà che il pulsante ha uno sfondo arrotondato.

WPF window with one template oval button

Potresti aver notato che il pulsante non è un cerchio, ma è asimmetrico. A causa del funzionamento dell'elemento <Ellipse> , si espande sempre per riempire lo spazio disponibile. Rendere uniforme il cerchio modificando le proprietà e height del width pulsante sullo stesso valore:

<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

Aggiungere un trigger

Anche se un pulsante con un modello applicato ha un aspetto diverso, si comporta come qualsiasi altro pulsante. Se si preme il pulsante, viene generato l'evento Click . Tuttavia, potresti aver notato che quando si sposta il mouse sul pulsante, gli oggetti visivi del pulsante non cambiano. Queste interazioni visive sono tutte definite dal modello.

Con i sistemi dinamici di eventi e proprietà forniti da WPF, è possibile controllare una proprietà specifica per un valore e quindi eseguire il layout del modello quando appropriato. In questo esempio si osserverà la proprietà del IsMouseOver pulsante. Quando il mouse si trova sul controllo, applicare uno stile all'ellisse ><con un nuovo colore. Questo tipo di trigger è noto come PropertyTrigger.

Per consentire il funzionamento, è necessario aggiungere un nome all'ellisse ><a cui è possibile fare riferimento. Assegnare il nome di backgroundElement.

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

Aggiungere quindi un nuovo Trigger oggetto all'insieme ControlTemplate.Triggers . Il trigger controlla l'evento IsMouseOver per il valore 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>

Aggiungere quindi un <setter> al< trigger> che modifica la proprietà Fill dell'ellisse <> in un nuovo colore.

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

Eseguire il progetto. Si noti che quando si sposta il mouse sul pulsante, il colore dell'ellisse <> cambia.

mouse moves over WPF button to change the fill color

Usare un oggetto VisualState

Gli stati di visualizzazione vengono definiti e attivati da un controllo . Ad esempio, quando il mouse viene spostato sopra il controllo, lo CommonStates.MouseOver stato viene attivato. È possibile animare le modifiche delle proprietà in base allo stato corrente del controllo. Nella sezione precedente è stato usato propertyTrigger>< per modificare lo sfondo del pulsante in AliceBlue quando la IsMouseOver proprietà era .true Creare invece uno stato di visualizzazione che anima la modifica di questo colore, fornendo una transizione uniforme. Per altre informazioni su VisualStates, vedere Stili e modelli in WPF.

Per convertire PropertyTrigger >< in uno stato di visualizzazione animato, rimuovere innanzitutto l'elemento <ControlTemplate.Triggers> dal modello.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

Nella radice Grid> del modello di controllo aggiungere quindi l'elemento <VisualStateManager.VisualStateGroups> con visualStateGroup >< per .CommonStates< Definire due stati e NormalMouseOver.

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

Tutte le animazioni definite in un <oggetto VisualState> vengono applicate quando viene attivato tale stato. Creare animazioni per ogni stato. Le animazioni vengono inserite all'interno di un <elemento Storyboard> . Per altre informazioni sugli storyboard, vedere Cenni preliminari sugli storyboard.

  • Normale

    Questo stato anima il riempimento con i puntini di sospensione, ripristinandolo nel colore del Background controllo.

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

    Questo stato anima il colore dell'ellisse Background in un nuovo colore: Yellow.

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

ControlTemplate ><dovrebbe ora essere simile al seguente.

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

Eseguire il progetto. Si noti che quando si sposta il mouse sul pulsante, il colore dell'ellisse <> si anima.

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

Passaggi successivi