Passa ai contenuti principali

Appunti di WPF – Dodicesima Puntata – Le figure geometriche

In questo tutorial esamineremo le capacità di WPF nel disegno delle figure geometriche bi-dimensionali.

La classe base di tutte le figure geometriche presenti in WPF è la Shape che deriva da FrameworkElement secondo il seguente diagramma delle classi:

WPF_12_Shapes_Fig1

Quindi, le figure geometriche sono elementi grafici a tutti gli effetti (come Button o TextBox) e questo fatto comporta dei vantaggi che non abbiamo, ad esempio, nell’ambito delle Windows Forms:

· Ogni figura è in grado di auto-disegnarsi sollevandoci dall’incombenza di disegnare noi, via codice, la figura in caso di aggiornamento o ridimensionamento;

· Come tutti gli elementi grafici, le figure geometriche possono essere inserite in un qualsiasi contenitore;

· Le figure geometriche supportano gli eventi come tutti gli altri controlli senza dover implementare particolari workaround per gestire l’interazione con l’utente.

La classe Shape espone le proprietà comuni a tutte le figure geometriche. Le più interessanti sono:

· Fill : permette di impostare l’oggetto Brush con cui riempire la nostra figura;

· Stroke : permette di impostare l’oggetto Brush per il disegno del bordo;

· StrokeTickness : imposta la larghezza del bordo. Quando WPF disegna una linea spessa un certo numero di pixel, metà di questi viene disegnata all’esterno della figura e metà all’interno;

· StrokeDashArray; StrokeDashOffeset e StrokeDashCap : sono tre proprietà che permettono di disegnare il bordo con una linea discontinua. Ad esempio il seguente XAML:

  1. <Line StrokeThickness="5" Stroke="Blue" X1="0" X2="400" Y1="25" Y2="25"
  2.     StrokeDashCap="Flat" StrokeDashOffset="30" StrokeDashArray="2" />

disegna una riga tratteggiata di larghezza 5 pixel, colore blue e tratti ampi 10 pixel.

WPF_12_Shapes_Fig2 
· StrokeLineJoin e StrokeMiterLimit : permettono definire gli angoli delle figure geometriche. Queste proprietà agiscono sui vertici dei poligoni e non hanno effetti su cerchi ed ellissi;

· Stretch : permette di definire il comportamento della forma geometrica rispetto al contenitore che la contiene;

· DefiningGeometry : permette di accedere all’oggetto di classe Geometry della figura geometrica;

· GeometryTransform : Permette di applicare delle trasformazioni lineari alla figura geometrica in modo da poter ottenere effetti tipo rotazione, allungamento e così via.

Rettangolo

La classe Rectangle ci consente di disegnare un rettangolo.

Possiamo definire, oltre alle proprietà comuni a tutte le figure geometriche, anche l’aspetto degli angoli del rettangolo stesso. Utilizzando le proprietà RadiusX e RadiusY possiamo impostare gli assi, rispettivamente x e y, delle ellissi utilizzate per disegnare gli angoli del rettangolo.

Per capire meglio il funzionamento utilizziamo il seguente XAML:

  1. <Window x:Class="Rectangle"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Rectangle" Height="350" Width="500">
  5.     <Grid>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="40" />
  8.             <RowDefinition Height="40" />
  9.             <RowDefinition Height="*" />
  10.         </Grid.RowDefinitions>
  11.         <StackPanel Orientation="Horizontal" Grid.Row="0" VerticalAlignment="Center">
  12.             <Label>RadiusX =</Label>
  13.             <TextBox Text="{Binding ElementName=SliderX, Path=Value}" Width="50" />
  14.             <Slider Margin="5" Name="SliderX" Maximum="150" Minimum="0" Value="{Binding Path=RadiusX, ElementName=Rectangle}" Width="200"/>
  15.         </StackPanel>
  16.         <StackPanel Orientation="Horizontal" Grid.Row="1" VerticalAlignment="Center">
  17.             <Label>RadiusY =</Label>
  18.             <TextBox Text="{Binding ElementName=SliderY, Path=Value}" Width="50" />
  19.             <Slider Margin="5" Name="SliderY" Maximum="150" Minimum="0" Value="{Binding Path=RadiusY, ElementName=Rectangle}" Width="200"/>
  20.         </StackPanel>
  21.         <Rectangle Grid.Row="2" Margin="10" Stroke="black" StrokeThickness="2" Name="Rectangle">
  22.             <Rectangle.Fill>
  23.                 <RadialGradientBrush>
  24.                     <GradientStopCollection>
  25.                         <GradientStop Offset="0" Color="Green"></GradientStop>
  26.                         <GradientStop Offset="1" Color="GreenYellow"></GradientStop>
  27.                     </GradientStopCollection>
  28.                 </RadialGradientBrush>
  29.             </Rectangle.Fill>
  30.         </Rectangle>
  31.     </Grid>
  32. </Window>

La finestra che ne deriva ci mette a disposizione due sliders per impostare le due proprietà per verificare il loro comportamento.

WPF_12_Shapes_Fig3WPF_12_Shapes_Fig4

Nel precedente XAML possiamo anche vedere un esempio di come impostare le proprietà Fill utilizzando un RadialGradientBrush.

Ellisse

Per disegnare una ellisse (o un cerchio, nel caso in cui i due assi siano uguali) possiamo utilizzare la classe Ellipse. Possiamo gestire la grandezza degli assi dell’ellisse utilizzando le proprietà Height e Width.

Nel seguente XAML. vediamo come disegnare un’ellisse che occupa l’intero spazio del suo contenitore e come colorare la stessa utilizzando un LinearGradientBrush le cui proprietà possono essere modificate utilizzando gli sliders:

  1. <Window x:Class="Ellipse"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Ellipse" Height="300" Width="500">
  5.     <Grid>
  6.         <Grid.RowDefinitions >
  7.             <RowDefinition Height="30" />
  8.             <RowDefinition Height="30" />
  9.             <RowDefinition Height="*" />
  10.         </Grid.RowDefinitions>
  11.         <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="2">
  12.             <Label Content="StartPoint" Width="100"/>
  13.             <Label Content="X"/>
  14.             <Slider Minimum="0" Maximum="1" Width="150" Name="sliderStartPointX" ValueChanged="slider_ValueChanged" />
  15.             <Label Content="Y"/>
  16.             <Slider Minimum="0" Maximum="1" Width="150" Name="sliderStartPointY" ValueChanged="slider_ValueChanged" />
  17.         </StackPanel>
  18.         <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="2">
  19.             <Label Content="EndPoint" Width="100"/>
  20.             <Label Content="X"/>
  21.             <Slider Minimum="0" Maximum="1" Width="150" Name="sliderEndPointX" ValueChanged="slider_ValueChanged" Value="1"/>
  22.             <Label Content="Y"/>
  23.             <Slider Minimum="0" Maximum="1" Width="150" Name="sliderEndPointY" ValueChanged="slider_ValueChanged" Value="1"/>
  24.         </StackPanel>
  25.         <Ellipse StrokeThickness="10" Margin="5" Grid.Row="2" Name="ellipse">
  26.             <Ellipse.Stroke>
  27.                 <LinearGradientBrush >
  28.                     <GradientStop Color="Red" Offset="1" />
  29.                     <GradientStop Color="green" Offset="0.75" />
  30.                     <GradientStop Color="Yellow" Offset="0.5" />
  31.                     <GradientStop Color="Blue" Offset="0.25" />
  32.                     <GradientStop Color="White" Offset="0" />
  33.                 </LinearGradientBrush>
  34.             </Ellipse.Stroke>
  35.             <Ellipse.Fill>
  36.                   <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
  37.                     <GradientStopCollection >
  38.                         <GradientStop Color="Red" Offset="0" />
  39.                         <GradientStop Color="green" Offset="0.25" />
  40.                         <GradientStop Color="Yellow" Offset="0.5" />
  41.                         <GradientStop Color="Blue" Offset="0.75" />
  42.                         <GradientStop Color="White" Offset="1" />
  43.                     </GradientStopCollection>
  44.                 </LinearGradientBrush>
  45.             </Ellipse.Fill>
  46.         </Ellipse>
  47.     </Grid>
  48.     </Window>

Nel precedente esempio, la modifica del gradiente che colora l’ellisse viene eseguita nel code behind della finestra e, in particolare nel gestore dell’evento ValueChanged dello Slider:

  1. Private Sub slider_ValueChanged(ByVal sender As System.Object,
  2.                                 ByVal e As System.Windows.RoutedPropertyChangedEventArgs(Of System.Double))
  3.     If ellipse IsNot Nothing Then
  4.         Dim brush = CType(ellipse.Fill, LinearGradientBrush)
  5.         Dim startPoint = brush.StartPoint
  6.         Dim endPoint = brush.EndPoint
  7.         Select Case CType(sender, Control).Name
  8.             Case "sliderStartPointX"
  9.                 startPoint.X = e.NewValue
  10.             Case "sliderStartPointY"
  11.                 startPoint.Y = e.NewValue
  12.             Case "sliderEndPointX"
  13.                 endPoint.X = e.NewValue
  14.             Case "sliderEndPointY"
  15.                 endPoint.Y = e.NewValue
  16.         End Select
  17.         brush.StartPoint = startPoint
  18.         brush.EndPoint = endPoint
  19.     End If
  20. End Sub

Nel precedente codice recuperiamo il LinearGradientBrush con cui abbiamo colorato l’ellisse e modifichiamo il punto di inizio e di fine del segmento su cui sono disposti i vari Stop.

Linea

Disegnare linee in WPF è molto semplice, basta utilizzare la classe Line.

  1.  
  2. <Line StrokeThickness="10" Stroke="black" X1="0" X2="400" Y1="20" Y2="20" />

Le proprietà x1, y1 e x2, y2 definiscono, rispettivamente, il punto di partenza e di arrivo della linea.

Nel seguente esempio disegniamo due linee di larghezza 10 pixel parallele tra loro:

  1. <Window x:Class="Line"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Line" Height="100" Width="400" SnapsToDevicePixels="True">
  5.     <Grid>
  6.         <Line StrokeThickness="10" Stroke="black" X1="0" X2="400" Y1="20" Y2="20" />
  7.         <Line StrokeThickness="10" Stroke="black" X1="0" X2="400" Y1="40" Y2="40" />
  8.     </Grid>
  9. </Window>

WPF_12_Shapes_Fig5

Polyline

La classe Polyline permette di disegnare una linea spezzata composta da tanti segmenti attaccati tra loro.

Un esempio di polyline è il seguente:

  1. <Polyline Stroke="red" StrokeThickness="5"
  2. Points="0,10 100,110 100,10 200,110 200,10 300,110 200,210 100,210">
  3. </Polyline>

La collezione di punti presente nella proprietà Points definisce, a coppie, i punti iniziali e finali della spezzata:

WPF_12_Shapes_Fig6

La collezione dei punti può essere ovviamente espressa come PointCollection:

  1. <Polyline Stroke="red" StrokeThickness="5"
  2.             StrokeLineJoin="Bevel" >
  3.     <Polyline.Points>
  4.         <PointCollection>
  5.             <Point X="10" Y="0"></Point>
  6.             <Point X="110" Y="100"></Point>
  7.             <Point X="210" Y="0"></Point>
  8.             <Point X="310" Y="100"></Point>
  9.         </PointCollection>
  10.     </Polyline.Points>
  11. </Polyline>

Possiamo controllare il modo con cui i singoli segmenti si sovrappongono utilizzando la proprietà StrokeLineJoin:

WPF_12_Shapes_Fig7

Poligono

Se la classe PolyLine ci consente di disegnare una spezzata, la classe Polygon ci permette di tracciare un poligono. Il funzionamento della classe Polygon è molto simile a quello della classe Polyline, e come quest’ultima ha una proprietà che contiene un insieme di punti che definiscono i vertici del poligono. La classe Polygon congiunge automaticamente l’ultimo punto della collection Points con il primo per chiudere la figura geometrica.

  1. <Window x:Class="Polygon"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Polygon" Height="300" Width="450">
  5.     <Grid>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="200"></RowDefinition>
  8.                     <RowDefinition Height="*"></RowDefinition>
  9.         </Grid.RowDefinitions>
  10.         <Grid.ColumnDefinitions>
  11.             <ColumnDefinition Width="*"></ColumnDefinition>
  12.             <ColumnDefinition Width="*"></ColumnDefinition>
  13.         </Grid.ColumnDefinitions>
  14.         <Polygon Stroke="black"
  15.                  Points="50,50 100,0 200,100 100,200 0,100 50,50 150,50 150,150 50,150"
  16.                  Fill="green" FillRule="EvenOdd" HorizontalAlignment="Center" ></Polygon>
  17.         <Polygon Stroke="black"
  18.                  Points="50,50 100,0 200,100 100,200 0,100 50,50 150,50 150,150 50,150"
  19.                  Fill="green" FillRule="Nonzero" Grid.Column="1" HorizontalAlignment="Center" ></Polygon>
  20.         <TextBlock Grid.Row="1" HorizontalAlignment="Center"
  21.                    VerticalAlignment="Top" Margin="5" FontSize="20" >EvenOdd</TextBlock>
  22.         <TextBlock Grid.Row="1" HorizontalAlignment="Center" Grid.Column="1"
  23.                    VerticalAlignment="Top" Margin="5" FontSize="20" >NonZero</TextBlock>
  24.     </Grid>
  25. </Window>

WPF_12_Shapes_Fig8 
La proprietà FillRule consente di scegliere il modo con cui la figura geometrica identifica la sua parte interna (per esempio per colorarla con il brush contenuto nella proprietà Fill) e la sua parte esterna.

I valori possibili per la proprietà FillRule sono i seguenti:

· EvenOdd : in questa modalità il framework individua un punto come interno alla figura se, disegnando virtualmente dei raggi a partire dal punto stesso in ogni direzione, il numero di segmenti della figura incrociati è dispari.

· NonZero : il meccanismo si basa sulla direzione dei segmenti disegnati. In pratica si procede così:

Si disegna un qualsiasi raggio a partire dal punto;

Si comincia il conteggio da zero;

Si aggiunge 1 ogni volta che il raggio incrocia un segmento della figura disegnato da sinistra a destra;

Si sottrae 1 ogni volta che il raggio incrocia un segmento della figura disegnato da destra a sinistra;

Il punto è interno se il conteggio è diverso da zero, altrimenti è esterno.

Le figure seguenti illustrano i due procedimenti:

WPF_12_Shapes_Fig9

WPF_12_Shapes_Fig10

Path

La classe Path permette di realizzare curve complesse componendo segmenti semplici che possono essere lineari, archi o curve di Bezier.

Un esempio di utilizzo è il seguente:

  1. <Window x:Class="Path"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Path" Height="350" Width="350">
  5.     <Grid>
  6.         <Path Stroke="Black" StrokeThickness="3">
  7.             <Path.Data>
  8.                 <PathGeometry>
  9.                     <PathGeometry.Figures>
  10.                         <PathFigureCollection>
  11.                             <PathFigure StartPoint="10,100">
  12.                                 <PathFigure.Segments>
  13.                                     <PathSegmentCollection>
  14.                                         <BezierSegment Point1="100,0" Point2="200,200" Point3="300,100" />
  15.                                         <BezierSegment Point1="100,0" Point2="200,200" Point3="300,100" />
  16.                                         <BezierSegment Point1="300,100" Point2="200,200" Point3="100,200" />
  17.                                         <BezierSegment Point1="50,50" Point2="100,100" Point3="150,150" />
  18.                                     </PathSegmentCollection>
  19.                                 </PathFigure.Segments>
  20.                             </PathFigure>
  21.                         </PathFigureCollection>
  22.                     </PathGeometry.Figures>
  23.                 </PathGeometry>
  24.             </Path.Data>
  25.         </Path>
  26.     </Grid>
  27. </Window>

WPF_12_Shapes_Fig11

 

Scarica la versione PDF dell'articolo. Scarica la versione Amazon Kindle dell'articolo.

Commenti

Post popolari in questo blog

MVP Reconnect …… ovvero quando entri nella “famigghia” resti sempre nella “famigghia”!!!

Ma di che “famigghia” stiamo parlando!!!!

Fermi tutti, non si tratta di robe strane o sette segrete o affari malavitosi….stiamo parlando della grande famiglia dei Microsoft MVP.

Per chi non sapesse cosa sono i Microsoft MVP, vi consiglio di fare un giro sul sito ufficiale del programma (link), ma, volendolo spiegare in pochisime parole, si tratta di un riconoscimento che Microsoft da a persone che si distinguono per il loro impegno, aiutando gli altri ad ottenere il massimo grazie alle tecnologie Microsoft. Si tratta di persone, non dipendenti Microsoft, che mettono la loro passione, il loro tempo, la loro buona volontà per la divulgazione e la condivisione della conoscenza. Non necessariamente (come qualcuno erroneamente sostiene, evidentemente non conoscendo le basi del programma) si tratta di professionisti nel termine letterale del termine ma si tratta comunque di un gruppo di persone che sacrifica un pò del suo tempo (e, a volte, vi assicuro neanche pò!!!) per la sua passione.

Pe…

Nuova versione del Band SDK

E’ di ieri l’annuncio del rilascio della nuova versione dell’SDK per il Microsoft Band.
Si tratta della versione 1.3.10417 (la precedente e, prima della serie, era la 1.3.10219 preview).
Maggiori informazioni, download dell’SDK per le tre piattaforme Windows Phone, iOS e Android all’indirizzo http://developer.microsoftband.com/.
Allo stesso indirizzo potrete trovare anche la documentazione.
Nei mesi scorsi mi sono gia’ occupato della precedente versione e questi sono i post che ne parlano:
Microsoft Band SDK Preview - First LookMicrosoft Band SDK Preview - ”Hello Band”Microsoft Band SDK Preview - Accesso ai sensoriMicrosoft Band SDK Preview - TileMicrosoft Band SDK Preview - NotificheMicrosoft Band SDK Preview - Personalizzazione
Gli argomenti trattati e il codice proposto dovrebbe, ad una prima lettura delle nuove funzionalita’ inserite, essere ancora valido e funzionante ma nei prossimi giorni prendero’ in esame tutti gli argomenti dei precedenti post e vedremo cosa cambia e cosa e’ …

Template di progetto per sviluppare applicazioni WPF con Intel® RealSense™

E’ disponibile, nella gallery di Visual Studio, la prima versione del mio template di progetto per applicazioni WPF scritte in C# che permette di realizzare applicazioni con l’SDK di Intel® RealSense™.Il template si può scaricare direttamente all’interno Visual Studio utilizzando il tool “Extensions and Updates”oppure all’indirizzo https://visualstudiogallery.msdn.microsoft.com/1c36ecfd-8c00-4aee-b20c-a1726ab6424dIl template esegue le seguenti operazioni per voi:Aggiunge la reference all’assembly libpxcclr.cs.dll (nelle due distinte versioni per x86 e x64);Aggiunge lo script di post build per copiare la libreria libpxccpp2c.dll dalla cartella dell’SDK alla cartella bin del vostro progetto.Una volta creato il progetto dovete rimuovere la configurazione di compilazione AnyCPU (che non ha più senso) dalla vostra solution e sarete pronti per sviluppare con Intel® RealSense™.Ovviamente dovete installare l’SDK che potete scaricare all’indirizzo https://software.intel.com/en-us/intel-realsen…