Condividi tramite


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.

In questo articolo si esplora la creazione di un nuovo ControlTemplate elemento per il controllo Button.

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 proprietà Foreground su blu e FontStyle in corsivo su un CheckBox. Quando si desidera personalizzare l'aspetto del controllo oltre a quanto è possibile fare impostando le altre proprietà del controllo, si può creare un 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 dell'utente fornisce l'elemento visivo circolare.

È possibile evitare di creare nuovi controlli personalizzando il layout visivo di un controllo esistente. Per 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 crea un nuovo UserControloggetto .

Prerequisiti

Creare una nuova applicazione WPF. 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 della finestra <dell'elemento> sul seguente XAML:

<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 codice XAML 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'immagine è simile alla seguente:

Finestra WPF con due pulsanti senza stile

Creare un ControlTemplate

Il modo più comune per dichiarare un ControlTemplate è una risorsa nella sezione Resources in un file XAML. Poiché i modelli sono risorse, seguono le stesse regole di ambito di tutte le risorse. La posizione in cui si dichiara un modello influisce sulla posizione in cui è possibile applicarla. Ad esempio, se dichiari il modello nell'elemento radice del file XAML di definizione dell'applicazione, puoi usare il modello 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> e impostare le proprietà seguenti:

Proprietà Valore
x:Key roundbutton
TargetType Button

Questo modello di controllo è semplice:

  • un elemento radice per il controllo, un Grid
  • un Ellipse per disegnare l'aspetto arrotondato del pulsante
  • un ContentPresenter per 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 oggetto ControlTemplate, è 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 un TemplateBinding, si abilitano le proprietà del controllo affinché agiscano come parametri del modello. Quando si imposta una proprietà su un controllo, il valore passa all'elemento con TemplateBinding.

Ellisse

Le proprietà Fill e Stroke dell'elemento <Ellipse> sono associate alle proprietà del controllo Foreground e Background.

Presentatore di Contenuti

Il modello include anche un <elemento ContentPresenter> . Poiché questo modello è progettato per un pulsante, tenere presente che il pulsante eredita da ContentControl. Il pulsante visualizza il contenuto dell'elemento. È possibile impostare qualsiasi elemento all'interno del pulsante, ad esempio testo normale o anche un altro controllo. Entrambi gli esempi seguenti sono pulsanti 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 un <ContentPresenter>, che è ciò che fa il modello.

Se si applica il ControlTemplate a un tipo ContentControl, ad esempio un Button, il modello cerca un ContentPresenter nell'albero degli elementi. Se trova ContentPresenter, il modello associa automaticamente la proprietà del controllo Content 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à Template del secondo pulsante sulla risorsa roundbutton:

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

Finestra WPF con un pulsante ovale basato su modello

Si potrebbe notare che il pulsante non è un cerchio, ma è asimmetrico. A causa del modo in cui funziona l'elemento <Ellipse>, si espande sempre per riempire lo spazio disponibile. Rendere uniforme il cerchio modificando le proprietà width e height del pulsante allo 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>

Finestra WPF con un pulsante circolare del modello

Aggiungere un trigger

Anche se un pulsante con un modello applicato ha un aspetto diverso, si comporta come qualsiasi altro pulsante. Se premi il pulsante, l'evento Click si attiva. Tuttavia, è possibile notare che quando si sposta il mouse sul pulsante, gli oggetti visivi del pulsante non cambiano. Il modello definisce queste interazioni visive.

Usando i sistemi di eventi dinamici e proprietà forniti da WPF, è possibile monitorare una proprietà specifica per un valore e quindi ristilizzare il modello quando appropriato. In questo esempio si osserva la proprietà del IsMouseOver pulsante. Quando il mouse si trova sul controllo, dare uno stile al <Ellipse> con un nuovo colore. Questo tipo di trigger è noto come PropertyTrigger.

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

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

Quindi, aggiungi un nuovo Trigger alla raccolta ControlTemplate.Triggers. Il trigger monitora 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>

Aggiungi quindi un Setter <> al Trigger <> che cambia la proprietà Fill dell'Ellipse <> in un nuovo colore.

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

Esegui il progetto. Quando si sposta il mouse sul pulsante, il colore dell'ellisse<> cambia.

il mouse si sposta sul pulsante WPF per modificare il colore di riempimento

Usare un VisualState

Gli stati di visualizzazione vengono definiti e attivati da un controllo . Ad esempio, quando si sposta il mouse sul controllo, il controllo attiva lo CommonStates.MouseOver stato. È 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 visivo che anima il cambiamento del colore, fornendo una transizione uniforme. Per ulteriori informazioni su VisualStates, vedere Stili e modelli in WPF.

Per convertire PropertyTrigger<> in uno stato di visualizzazione animato, rimuovere 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>

Nel <Grid> radice del modello di controllo aggiungere quindi l'elemento <VisualStateManager.VisualStateGroups> con un <VisualStateGroup> per CommonStates. Definire due stati, Normal e 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>

Applicare tutte le animazioni definite in un <oggetto VisualState> quando viene attivato tale stato. Creare animazioni per ogni stato. Inserire animazioni all'interno di un <elemento Storyboard> . Per ulteriori informazioni sugli storyboard, vedere Panoramica di Storyboards.

  • Normale

    Questo stato anima il riempimento dell'ellisse, ripristinandolo al colore Background del controllo.

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

    Questo stato anima il colore dell'ellisse Background a 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 codice 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>

Esegui il progetto. Quando si sposta il mouse sul pulsante, il colore dell'ellisse<> si anima.

il mouse si sposta sul pulsante WPF per cambiare il colore di riempimento usando uno stato visivo

Passaggi successivi