Passa ai contenuti principali

Briciole di WPF: Finestre non rettangolari, WPF vs Windows Forms

Questa briciola vuole mettere in evidenza il completo supporto alla trasparenza presente in WPF.

Nel fare questo effettueremo un confronto con il supporto fornito nelle Windows Forms nella creazione di finestre non rettangolari.

Partiamo da WPF. Creare finestre non rettangolari in WPF è assolutamente banale. Prendiamo ad esempio il seguente XAML:

  1. <Window x:Class="MainWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="MainWindow" Height="350" Width="525" Background="Transparent" WindowStyle="None" AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown">
  5.     <Canvas>
  6.         <Canvas.Background>
  7.             <ImageBrush ImageSource="./images/Drawing.png"></ImageBrush>
  8.         </Canvas.Background>
  9.         <Button Canvas.Top="91" Canvas.Left="100" Height="31" Width="70" Click="Button_Click">Premi</Button>
  10.         <TextBlock Canvas.Left="258" Canvas.Top="165" Height="30" Name="TextBlock1" Width="175" TextAlignment="Center" FontSize="20"/>
  11.     </Canvas>
  12. </Window>

L’immagine utilizzata come sfondo per il canvas principale è un’immagine con la trasparenza (nel nostro caso un png).
Per fare in modo che la Window supporti completamente la trasparenza è necessario:

  1. Impostare il backgroung della window a Trasparent: 
         Background="Transparent"
  2. Impostare lo stile della finestra senza i bordi e la caption:
         WindowStyle="None"
  3. Ed attivare la trasparenza:
         AllowsTransparency="True"

In particolare, se impostiamo la proprietà AllowsTransparency a true, è fondamentale impostare la proprietà WindowStyle a None, altrimenti otteniamo un errore in fase di esecuzione.

In questo modo, otteniamo una finestra con una forma non rettangolare, ma non abbiamo la possibilità di spostare la finestra, in quanto non è presente la caption.

Esempio di finestra non rettangolare

Per poter attivare lo spostamento della finestra cliccando su qualsiasi punto della finestra e trascinando la stessa è sufficiente scrivere il seguente codice come gestore dell’evento MouseLeftButtonDown:

  1. Private Sub Window_MouseLeftButtonDown(ByVal sender As System.Object, _
  2.                                        ByVal e As System.Windows.Input.MouseButtonEventArgs)
  3.     Me.DragMove()
  4. End Sub

Grazie ai routed events di WPF, la finestra viene spostata anche se ci posizioniamo con il mouse su qualsiasi controllo della nostra interfaccia che non gestisce il MouseLeftButtonDown. Nella solution che riporto nel download in fondo al post abbiamo un textblock e un button: possiamo spostare la finestra se clicchiamo sul textblock ma non sul bottone (quest’ultimo ha il gestore dell’evento click).

Vediamo, ora, come realizzare la stessa interfaccia con una windows forms. Le tecniche che si possono utilizzare per realizzare una form non rettangolare con Windows Forms sono sostanzialmente due: disegnando tramite le classi Drawing la region dell’interfaccia grafica nel metodo OnPaint della finestra  oppure, più semplicemente, impostando nel seguente modo le proprietà della finestra:

  1. Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
  2.     Me.BackgroundImage = New Bitmap(My.Resources.Drawing, New Size(Me.Width, Me.Height))
  3.     Me.FormBorderStyle = FormBorderStyle.None
  4.     Me.TransparencyKey = Color.FromArgb(0, 255, 255)
  5. End Sub

In questo caso la nostra immagine è presente nelle risorse del progetto (ma potrebbe essere letta anche da file) ed ha un colore scelto da noi (celeste (0,255,255) in questo caso) per indicare la parte di finestra trasparente.

Il risultato è una finestra di questo tipo:

Windows Forms non rettangolareIl problema nasce quando vogliamo abilitare il trascinamento della finestra da parte dell’utente. In questo caso dobbiamo simulare ciò che accade quando è presente la caption e l’utente è in grado di cliccare con il mouse al di sopra della stessa e trascinare l’intera finestra tenendo premuto il tasto sinistro del mouse.

Per fare questo dobbiamo utilizzare P/Invoke ed importare le seguenti funzioni:

  1. <DllImport("user32.dll")> _
  2. Private Shared Function SendMessage( _
  3.                 ByVal hWnd As IntPtr, _
  4.                 ByVal Msg As UInteger, _
  5.                 ByVal wParam As IntPtr, _
  6.                 ByVal lParam As IntPtr) As IntPtr
  7. End Function
  8.  
  9. <DllImport("user32.dll")> _
  10. Private Shared Function ReleaseCapture() As Boolean
  11. End Function
  12.  
  13. Const WM_NCLBUTTONDOWN As UInteger = &HA1
  14. Const HTCAPTION As Integer = 2

Potete trovare maggiori riferimenti alle API utilizzate ai seguenti link: SendMessage e ReleaseCapture.

Utilizziamo queste API contenute nella user32.dll per simulare le azioni che esegue l’utente al di sopra della caption della finestra nel momento in cui decide di spostarla:

  1. Private Sub MoveWindow()
  2.     Dim lngReturnValue As IntPtr
  3.     Call ReleaseCapture()
  4.     lngReturnValue = SendMessage(Me.Handle, WM_NCLBUTTONDOWN, New IntPtr(HTCAPTION), New IntPtr(0))
  5. End Sub

Utilizziamo questa funzione all’interno del gestore di evento mousemove della form e di tutti i controlli che non hanno interazione diretta con l’utente (nel nostro caso la label).

  1. Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
  2.             Handles Me.MouseMove
  3.     If e.Button = System.Windows.Forms.MouseButtons.Left Then
  4.         MoveWindow()
  5.     End If
  6. End Sub
  7.  
  8. Private Sub TextBlock1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
  9.             Handles TextBlock1.MouseMove
  10.     If e.Button = System.Windows.Forms.MouseButtons.Left Then
  11.         MoveWindow()
  12.     End If
  13. End Sub

In definitiva possiamo osservare che, per sua natura intrinseca WPF ci consente di realizzare la form non rettangolare agendo semplicemente nello XAML e sfruttando le caratteristiche di trasparenza dei formati grafici, inoltre ci semplifica la vita nella gestione del drag and drop della finestra non costringendoci all’utilizzo di P/Invoke (che tra le altre cose rende l’applicazione poco portabile).

Commenti

Post popolari in questo blog

VB for Dummies: La serializzazione – parte 2

Questo post è la continuazione del precedente post sulla serializzazione. In particolare vedremo la serializzazione SOAP e quella JSON.   Serializzazione SOAP La serializzazione SOAP è demandata alla classe SOAPFormatter contenuta nel namespace System.Runtime.Serialization.Formatters.Soap contenuto nella libreria omonima. Il formatter SOAP risale alle primissime versioni del framework e, purtroppo, da un certo punto in poi, pur non essendo stato dichiarato obsoleto, non è stato portato avanti nello sviluppo e non supporta alcuni tipi di dati usatissimi nel mondo .NET quali i generici e i nullable. Per questo motivo, non possiamo serializzare in formato SOAP (utilizzando il SOAPFormatter) la nostra Fattura (vedi post precedente) poichè questa ha una proprietà di tipo List(Of DettagliFattura) (generico).   Serializzazione JSON Il formato di serializzazione JSON (maggiori info qui ) è un formato testuale molto in voga nelle applicazioni AJAX. Si trata di un mod...

Recensione: Windows Runtime via C#

Può sembrare strano che un VB-ista legga un libro su C#, ma come diceva Sun Tzu: “ Se conosci il tuo nemico, conosci te stesso ”. A parte gli scherzi, il libro vale la pena di essere letto a prescindere dal linguaggio .NET con cui si lavora. Oltre  290 pagine dedicate agli aspetti fondamentali dello sviluppo con Windows Runtime per le Windows Store App. I prerequisiti per poter leggere il libro sono la conoscenza di C# e di Visual Studio e gli autori non danno per scontato quasi nulla partendo dai concetti di base quali il type system e i suoi principi (argomento che di solito è saltato a piedi pari da chi si avvicina al mondo WinRT dal framework completo e che, se non compreso, può dare problemi nello sviluppo quotidiano). Tra i concetti che possiamo definire “core”, troviamo anche i capitoli dedicati all’app packaging e al process model, entrambi ben strutturati e chiari. Già solo questi tre capitoli iniziali giustificherebbero, in un certo qual modo, l’acquisto del libro, ...

VB.NET for Dummies: Gli Eventi

Vorrei iniziare con questo post una serie dedicata ad aspetti di VB.NET di base che possono essere utile a coloro che si avvicinano al mondo .NET e che, in genere, non vengono trattati a livello base. La serie di post non ha la pretesa di essere assolutamente esaustivi sugli argomenti che tratterò In questo primo post parleremo degli eventi. Cosa sono e a cosa servono Un evento è la “notifica” dell’accadimento di qualcosa. Quando, ad esempio, premiamo un bottone della finestra di un’applicazione, dietro le quinte, il bottone stesso “notifica” al mondo circostante che qualcuno, in quell’istante, lo ha premuto. Sta, poi, al mondo circostante preoccuparsi di “intercettare” l’evento stesso per gestirlo (tramite un gestore di evento). Attenzione a non confondere la pressione del tasto con la “notifica” della pressione del tasto: l’evento è la “notifica” dell’accadimento, non l’accadimento stesso. Ma a cosa serve tutto ciò? Utilizzare gli eventi è un modo per disaccoppiare ...