Francesco Geri

Il blog di Francesco Geri
posts - 94, comments - 165, trackbacks - 2

venerdì 1 marzo 2013

Eliminazione dello sfarfallio (flickering) delle Finestre figlie di una MDI all’activate

Quando una Form MDI contiene delle form figlie aperte in modalità Maximized si uno sfarfallio al cambio delle finestra attiva.

Ciò succede quando si attiva la finestra da codice. Per evitare questo fastidioso comportamento si può come al solito ricorrere alle API Windows.

A tal proposito ho preparato un modulo MDIUtil che crea un Extension method per aggiungere alla form MDIParent il metodo MyActivateMdiChild con cui si può richiedere di attivare una form child evitando il flickering.

Il codice del modulo è il seguente:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
 
Public Module MDIUtil
 
#Region " MDI Activate Child "
 
    <System.Security.SuppressUnmanagedCodeSecurity>
    <System.Runtime.InteropServices.DllImport("user32.dll", CharSet:=System.Runtime.InteropServices.CharSet.Auto)>
    Public Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
    End Function
 
    Public Const WM_MDINEXT As Integer = &H224
 
    ''' <summary>
    ''' Attiva la form MDI child specificata eviatando i problemi di flicker/flashing
    ''' che si avrebbero usando semplicemente il metodo <see cref="Form.Activate">Activate</see>.
    ''' Vedi post http://www.codeproject.com/Articles/19524/Workaround-for-flicker-flashing-when-programmatica
    ''' </summary>
    ''' <param name="form">Form MDI partent</param>
    ''' <param name="childToActivate">Form MDI child da attivre</param>
    <System.Runtime.CompilerServices.Extension>
 Public Sub MyActivateMdiChild(form As Form, childToActivate As Form)
        If Not childToActivate Is form.ActiveMdiChild Then
            Dim mdiClient As MdiClient = GetMDIClient(form)
            If mdiClient Is Nothing Then Return
            Dim count As Integer = form.MdiChildren.Length
            Dim childForm As Control = Nothing
            ' next or previous MDIChild form
            Dim pos As Integer = mdiClient.Controls.IndexOf(childToActivate)
            If pos < 0 Then Return 'Throw New InvalidOperationException("MDIChild form not found")
            If mdiClient.Controls.Count <= 1 Then
                ' C'è un solo child, quindi attiva quello in modo classico
                childToActivate.Focus()
                Return
            End If
            If pos = 0 Then
                childForm = mdiClient.Controls(1)
            Else
                ' get next and activate previous
                childForm = mdiClient.Controls(pos - 1)
            End If
            ' get previous and activate next
 
            ' flag indicating whether to activate previous or next MDIChild
            Dim direction As New IntPtr(If(pos = 0, 1, 0))
 
            ' bada bing, bada boom
            SendMessage(mdiClient.Handle, WM_MDINEXT, childForm.Handle, direction)
        End If
    End Sub
 
    Public Function GetMDIClient(form As Form) As MdiClient
        For Each c As Control In form.Controls
            If TypeOf c Is MdiClient Then
                Return DirectCast(c, MdiClient)
            End If
        Next
        Return Nothing 'Throw New InvalidOperationException("No MDIClient")
    End Function
 
#End Region
 
End Module

 

A questo punto nella form parent MDI si deve importare il modulo e usare il metodo MyActivateMdiChild al posto di child.Activate() (o Focus()):

Imports itConsult.josh.joshDesigner.MDIUtil
 
 
...
 
Dim child As Form = getTheFormToActivate()
' La seguente istruzione sostisuice l'istruzione: child.Activate()
Me.MyActivateMdiChild(child)

 

Nei commenti del codice del modulo è indicata anche la fonte da cui ho liberamente preso quel codice, ovvero questo post.

posted @ lunedì 1 gennaio 0001 00:00 | Feedback (0) | Filed Under [ Tips .Net MDI ]

Rimuovere il bordo interno delle form MDI (sunken)

Una Form MDI di default ha un bordo interno (che credo sia di tipo sunken), ovvero del tipo:

image

Per rimuoverlo si può ricorrere alle API windows.

A tal proposito ho preparato un modulo MDIUtil che crea un Extension method per aggiungere alla form MDIParent il metodo SetBevel con cui si può richiedere di mostrare o non mostrare il bordo.

Il codice del modulo è il seguente:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
 
Public Module MDIUtil
 
#Region " Bordo interno (sunken) "
 
    <DllImport("user32.dll")> _
    Private Function GetWindowLong(hWnd As IntPtr, nIndex As Integer) As Integer
    End Function
 
    <DllImport("user32.dll")> _
    Private Function SetWindowLong(hWnd As IntPtr, nIndex As Integer, dwNewLong As Integer) As Integer
    End Function
 
    <DllImport("user32.dll", ExactSpelling:=True)> _
    Private Function SetWindowPos(hWnd As IntPtr, hWndInsertAfter As IntPtr, X As Integer, Y As Integer, cx As Integer, cy As Integer, _
        uFlags As UInteger) As Integer
    End Function
 
    Private Const GWL_EXSTYLE As Integer = -20
    Private Const WS_EX_CLIENTEDGE As Integer = &H200
    Private Const SWP_NOSIZE As UInteger = &H1
    Private Const SWP_NOMOVE As UInteger = &H2
    Private Const SWP_NOZORDER As UInteger = &H4
    Private Const SWP_NOREDRAW As UInteger = &H8
    Private Const SWP_NOACTIVATE As UInteger = &H10
    Private Const SWP_FRAMECHANGED As UInteger = &H20
    Private Const SWP_SHOWWINDOW As UInteger = &H40
    Private Const SWP_HIDEWINDOW As UInteger = &H80
    Private Const SWP_NOCOPYBITS As UInteger = &H100
    Private Const SWP_NOOWNERZORDER As UInteger = &H200
    Private Const SWP_NOSENDCHANGING As UInteger = &H400
 
    ''' <summary>
    ''' Consente di rimuovere il bordo 3D interno della form MDI (sunken).
    ''' Vedi post: http://stackoverflow.com/questions/7752696/how-to-remove-3d-border-sunken-from-mdiclient-component-in-mdi-parent-form
    ''' Oppure articolo: http://www.codeproject.com/Articles/8489/Getting-a-quot-Handle-quot-on-the-MDI-Client#Changing%20the%20Border%20Styles:
    ''' </summary>
    ''' <param name="form"></param>
    ''' <param name="show">False per rimuovere il sunken (3D interno), True per mostrarlo</param>
    ''' <returns></returns>
    <System.Runtime.CompilerServices.Extension>
 Public Function SetBevel(form As Form, show As Boolean) As Boolean
        For Each c As Control In form.Controls
            Dim client As MdiClient = TryCast(c, MdiClient)
            If client IsNot Nothing Then
                Dim windowLong As Integer = GetWindowLong(c.Handle, GWL_EXSTYLE)
 
                If show Then
                    windowLong = windowLong Or WS_EX_CLIENTEDGE
                Else
                    windowLong = windowLong And Not WS_EX_CLIENTEDGE
                End If
 
                SetWindowLong(c.Handle, GWL_EXSTYLE, windowLong)
 
                ' Update the non-client area.
                SetWindowPos(client.Handle, IntPtr.Zero, 0, 0, 0, 0, _
                    SWP_NOACTIVATE Or SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOOWNERZORDER Or SWP_FRAMECHANGED)
 
                Return True
            End If
        Next
        Return False
    End Function
 
#End Region
 
End Module

 

A questo punto nella form parent MDI si deve importare il modulo e usare il metodo SetBevel nella load, ovvero:

Imports itConsult.josh.joshDesigner.MDIUtil
 
Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.SetBevel(False)
End Sub

 

Nei commenti del codice del modulo sono indicate anche le fonti da cui ho liberamente preso quel codice, ovvero questo post e questo post.

posted @ lunedì 1 gennaio 0001 00:00 | Feedback (0) | Filed Under [ Tips .Net MDI ]

Powered by:
Powered By Subtext Powered By ASP.NET