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:
- <Window x:Class="MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow" Height="350" Width="525" Background="Transparent" WindowStyle="None" AllowsTransparency="True" MouseLeftButtonDown="Window_MouseLeftButtonDown">
- <Canvas>
- <Canvas.Background>
- <ImageBrush ImageSource="./images/Drawing.png"></ImageBrush>
- </Canvas.Background>
- <Button Canvas.Top="91" Canvas.Left="100" Height="31" Width="70" Click="Button_Click">Premi</Button>
- <TextBlock Canvas.Left="258" Canvas.Top="165" Height="30" Name="TextBlock1" Width="175" TextAlignment="Center" FontSize="20"/>
- </Canvas>
- </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:
- Impostare il backgroung della window a Trasparent:
Background="Transparent" - Impostare lo stile della finestra senza i bordi e la caption:
WindowStyle="None" - 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.
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:
- Private Sub Window_MouseLeftButtonDown(ByVal sender As System.Object, _
- ByVal e As System.Windows.Input.MouseButtonEventArgs)
- Me.DragMove()
- 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:
- Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- Me.BackgroundImage = New Bitmap(My.Resources.Drawing, New Size(Me.Width, Me.Height))
- Me.FormBorderStyle = FormBorderStyle.None
- Me.TransparencyKey = Color.FromArgb(0, 255, 255)
- 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:
Il 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:
- <DllImport("user32.dll")> _
- Private Shared Function SendMessage( _
- ByVal hWnd As IntPtr, _
- ByVal Msg As UInteger, _
- ByVal wParam As IntPtr, _
- ByVal lParam As IntPtr) As IntPtr
- End Function
- <DllImport("user32.dll")> _
- Private Shared Function ReleaseCapture() As Boolean
- End Function
- Const WM_NCLBUTTONDOWN As UInteger = &HA1
- 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:
- Private Sub MoveWindow()
- Dim lngReturnValue As IntPtr
- Call ReleaseCapture()
- lngReturnValue = SendMessage(Me.Handle, WM_NCLBUTTONDOWN, New IntPtr(HTCAPTION), New IntPtr(0))
- 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).
- Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
- Handles Me.MouseMove
- If e.Button = System.Windows.Forms.MouseButtons.Left Then
- MoveWindow()
- End If
- End Sub
- Private Sub TextBlock1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
- Handles TextBlock1.MouseMove
- If e.Button = System.Windows.Forms.MouseButtons.Left Then
- MoveWindow()
- End If
- 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