Suite et fin de transparence & mobilite de controles


Description

Le problème de la précédente version était que l'on ne pouvait pas saisir ou sélectionner dans un contrôle transparent. C'est je crois résolu dans cette version.

Vous pouvez ...
- créer des contrôles réagissant différemment et indépendamment à l'opacification
- bouger ceux-ci dans ou en dehors de la Form
- et bien entendu saisir ou sélectionner ...

J'avais espéré pouvoir utiliser le CreateWindowEx pour créer des windows qui encapsuleraient certains contrôles. J'ai réussi à créer dynamiquement ces forms mais elles conservaient leur barre de titre. Aussi ais-je abandonné et suis-je passé à la création de windows par duplication d'une window modèle pré établie (Form2 dans le programme)

remarque : Les contrôles Listbox et Text1 sont regroupés dans un frame. Pour les faire bouger cliquez entre ces deux contrôles

Source / Exemple :


CONTENU de MODULE 1 :

Option Explicit

'POSTULAT : Tout contrôle possédant la propriété "Hwnd" doit être considéré comme une sorte de WINDOW

'Permet à une Window de devenir plus ou moins transparente
Public Declare Function SetLayeredWindowAttributes Lib "user32.dll" (ByVal hwnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long

'Rentre une valeur particulière dans une Window
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
'Récupère une valeur particulière d'une Window
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
'Calcule les coordonnées d'un contrôle ou form dans le Screen (établi en pixels - ne prend pas en compte la barre de titre d'une form si celle-ci existe)
Public Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As Rect) As Long
'Offre un nouveau parent à une window (contrôle y compris - ne pas oublier la notion de Hwnd)
Public Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
'Indique quelle est la window qui  a adoptée une window (fille - hors MDI)
Public Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
'Repositionne une window en indiquant entre autre comment elle doit se comporter via hwndInsertAfter entre autre )
Public Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

'constante dwStyle
Public Const WS_CHILD = &H40000000 'La fenêtre est une fille

'transparence d'une form, contrôle ...
Public Const GWL_STYLE = (-16)
Public Const GWL_EXSTYLE = (-20)
Public Const LWA_COLORKEY = &H1
Public Const LWA_ALPHA = &H2
Public Const WS_EX_LAYERED = &H80000

Public Const SWP_FRAMECHANGED = &H20

Public Type Rect    'Réceptacle de coordonnées Screen
    Left As Long
    Top As Long
    right As Long
    Bottom As Long
End Type

Public Type Encapsuleur 'Réceptacle d'une Window "encapsulant" un contrôle
    NumForm As Long         'La position de celle-ci dans la hiérarchie FORMS (par défaut, dans le programme Form1 est en position 0)
    LeftOrigine As Long     'Coordonnée "Origine" pour permettre de repositionner dans Form1 un contrôle suite à un désencapsulage
    TopOrigine  As Long     ' "  "
    widthOrigine As Long    ' "  "
    heightOrigine As Long   ' "  "
    hwnd As Long            ' Numéro de la Window "Encapsuleur"
    NameControlAsservi As String    'NAME du contrôle encapsulé
    HwndControlAsservi As Long      'Numéro de window du contrôle encapsulé
End Type

Public NomWndRect As Rect
Public FrmRect As Rect
Public PictRect As Rect

Public TheEncapsule(2) As Encapsuleur   'Tableau de windows "Encapsuleur" (Dans le prog on en utilise 3)

Public CompteurForm As Long             'sert à attribuer un N° ordre initial aux windows "Encapsuleur" dans la hiérarchie "FORMS"

Public Compteur As Integer

Sub Main()

     CompteurForm = 0   'Initialisation du compteur de création de forms "Encapsuleur"
     
     'Présentation de l'écran principal"
     Form1.Show
     Form1.Caption = "Fenêtre primaire"
    
    
    'Afin de pouvoir rendre plus ou moins transparent certains contrôles de FORM1, et éventuellement de pouvoir les sortir de celle-ci,
    'on va créer une window "encapsuleut" qui servira de réceptacle à chacun d'eux
    'Remarque : si on veut créer un groupe de contrôles (dans leur fonctionnement) il est souhaitable de les mettre dans un "FRAME" et d'encapsuler
    '           ce dernier - Dans le programme, List1 (ListBox) et Text1 (TextBox) sont regroupés dans Frame1 (Il existe, mais son Appearance = 0
    '           et son Borderstyle = 0)
    TheEncapsule(0) = EncapsulerControl(Form1.Text2, Form1)     'Text2 est encapsulé - TheEncapsule(0) récupère les valeurs concernant cette opération
    TheEncapsule(1) = EncapsulerControl(Form1.Combo1, Form1)    'Combo1 est encapsulé ...
    TheEncapsule(2) = EncapsulerControl(Form1.Frame1, Form1)    'Frame1 est encapsulé ...
    
    'Détails des opérations dans la fonction "EncapsulerControl ..."
    
    'Ici on voit une autre manière de rendre un contrôle plus ou moins transparent. Il semble ne convenir qu'à tout contrôle ne nécessitant pas un "KEY INPUT/OUTPUT"
    'En effet utilisé avec un ListBox, par exemple, il sera possible de mouvoir le scrollbar mais pas de sélectionner un item ...
    'Dans le prog il est utilisé avec la picture LDX. Imaginez qu'à la place vous ayez un bouton ...
    
    Call SetParent(Form1.Picture1.hwnd, GetParent(Form1.hwnd))
    Call GetWindowRect(Form1.hwnd, FrmRect)   'Position de Form1 dans l'écran (coordonnées en Pixels)
    Call GetWindowRect(Form1.Picture1.hwnd, PictRect) 'Position de Picture1 dans ...
    Call SetWindowPos(Form1.Picture1.hwnd, -1, FrmRect.Left + (Form1.Picture1.Left / 15), FrmRect.Top + PictRect.Top + 30, (Form1.Picture1.Width / 15), (Form1.Picture1.Height / 15), SWP_FRAMECHANGED)
    'REMARQUE : 30 pixels sont rajoutés au "Top" prendre en compte la barre de titre qui ne l'est pas dans les coordonnées RECT de Form1
    '           Vous pouvez remarquer également que dans la barre de tâche du bureau une nouvelle fenêtre sans titre a été créée.

End Sub

Public Function WndSetOpacity(ByVal hwnd As Long, Optional ByVal crKey As Long = vbBlack, Optional ByVal Alpha As Byte = 255, Optional ByVal ByAlpha As Boolean = True) As Boolean
'(de Greengold)
'Return : True si il n'y a pas eu d'erreur.
'hWnd    : hWnd de la fenêtre à rendre transparente
'crKey : Couleur à rendre transparente si ByAlpha=False (utiliser soit les constantes vb:vbWhite ou en hexa:&HFFFFFF)
'Alpha : 0-255 0=transparent 255=Opaque si ByAlpha=true (défaut)
    Dim ExStyle As Long
    ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE)
    If ExStyle <> (ExStyle Or WS_EX_LAYERED) Then
        ExStyle = (ExStyle Or WS_EX_LAYERED)
        Call SetWindowLong(hwnd, GWL_EXSTYLE, ExStyle)
    End If
    WndSetOpacity = (SetLayeredWindowAttributes(hwnd, crKey, Alpha, IIf(ByAlpha, LWA_ALPHA, LWA_COLORKEY)) <> 0)
End Function

Public Function EncapsulerControl(ControlAEncapsuler As Object, FormPrincipal As Form) As Encapsuleur

    Dim NomWnd As ModeleForm    'ModeleForm est une form qui va servir de modèle aux forms "Encapsuleur"
                                'S'il est possible de créer une form via "CreateWindowEx" il ne semble toutefois pa possible
                                'd'en supprimer la barre de titre. Pour cette raison, j'ai préféré revenir à une forme plus traditionnelle.
    
    With EncapsulerControl
        .NumForm = CompteurForm + 1                     'Incrémentons le compteur de Form
        .LeftOrigine = ControlAEncapsuler.Left          'Sauvons les coordonnées "origine" du contrôle à encapsuler
        .TopOrigine = ControlAEncapsuler.Top
        .widthOrigine = ControlAEncapsuler.Width
        .heightOrigine = ControlAEncapsuler.Height
        .HwndControlAsservi = ControlAEncapsuler.hwnd   'Reprenons le NAME du contrôle, cela peut servir en cas de vérif (non utilisé dans ce prog)
        .NameControlAsservi = ControlAEncapsuler.Name   'reprenons le Hwnd ...
    End With
    
    Set NomWnd = New ModeleForm                         'Créons une forme "Encapsuleur" par duplication du modèle
    
    With NomWnd
        .Visible = True
        .Width = ControlAEncapsuler.Width               'Adaptons les coordonnées de ce nouveau Form à la taille du contrôle
        .Height = ControlAEncapsuler.Height
    End With
    
    Call GetWindowRect(ControlAEncapsuler.hwnd, NomWndRect) 'Reprenons les coordonnées SCREEN du contrôle à encapsuler
    'Positionnons le form "Encapsuleur" en lieu et place du contrôle ...
    Call SetWindowPos(NomWnd.hwnd, -1, (NomWndRect.Left), (NomWndRect.Top), (NomWnd.Width / 15), (NomWnd.Height / 15), SWP_FRAMECHANGED)
    
    'Le Form "Encapsuleur" ADOPTE le contrôle à ...
    Call SetParent(ControlAEncapsuler.hwnd, NomWnd.hwnd)
    'Indiquons que celui-ci (qui est une sorte de window) devient sa fille. Elle devient donc dépendante du nouveau Form
    Call SetWindowLong(ControlAEncapsuler.hwnd, GWL_STYLE, WS_CHILD)
    'Positionnons le contrôle dans sa nouvelle window
    ControlAEncapsuler.Move 0, 0
    ControlAEncapsuler.Visible = True
    
    'Incrémentons d'une unité le compteur de forme. Cela nous servira lors du désencapsulage
    CompteurForm = CompteurForm + 1
    
    'Retournons les éléments (de type Encapsuleur) à "TheEncapsule" qui l'a demandé
    EncapsulerControl.hwnd = NomWnd.hwnd
    
End Function

Public Sub LibereMemoire(NumID As Long, DonneesControl() As Encapsuleur, ControlADesencapsuler As Object, ARemettreDansForm As Form, LiaisonFormASupprimer As Form)
    
    'Il peut être intéressant parfois d'encapsuler temporairement un contrôle (exemple : lors de la saisie d'une fiche)
    'Il faut donc lors de la validation de celle-ci, désencapsuler, puis refaire une opération d'encapsulage si nécessaire ...
    Dim Compteur As Integer
    
    'Commençons à rendre à Form1 le contrôle à désencapsuler
    Call SetParent(ControlADesencapsuler.hwnd, ARemettreDansForm.hwnd)
    
    'Supprimons le window "encapsuleur" qui n'est plus nécessaire
    Unload Forms(DonneesControl(NumID).NumForm)
    
    'En remettons son NumForm à 0 indiquons que TheEncapsule(NumId) correspondant n'est plus "actif"
    DonneesControl(NumID).NumForm = 0
    
    'Rétablissons dans Form1, le contrôle désencapsulé à sa position d'origine
    ControlADesencapsuler.Move DonneesControl(NumID).LeftOrigine, DonneesControl(NumID).TopOrigine

    'Puisque l'on a supprimé une window "Encapsuleur", les restantes descendent d'une unité dans la hiérarchie FORMS(x)
    'Remarque : Dans le prog on part de TheEncapsule(0) vers ...(2). On aurait pu commencer par l'inverse, d'où autre instruction ... (non mise)
    For Compteur = 0 To 2
        If DonneesControl(Compteur).NumForm > 0 Then DonneesControl(Compteur).NumForm = DonneesControl(Compteur).NumForm - 1
    Next
    
End Sub

CONTENU de FORM1 :

Option Explicit

Public XDepart, YDepart As Single   '=> Positions Départ de la souris (ATTENTION : SCREEN pour les FORMS, WINDOW pour les contrôles)

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    WndSetOpacity Picture1.hwnd, 0, Val(Text4.Text)     'Là on rend transparent Picture1
End Sub

Private Sub Form_Unload(Cancel As Integer)
    
    Call SetParent(Picture1.hwnd, Form1.hwnd)   'N'oublions pas la picture. Elle n'est pas dans le proc "LibereMemoire"
    
    End 'Fin de programme, on décharge la mémoire de tous les composants résiduels
    
End Sub

Private Sub Frame1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

     If Button = vbLeftButton Then   'Bouton souris enfoncé
        If XDepart = 0 Then XDepart = X     'la première fois que l'on passe dans cette proc il faut aue XDepart prenne une "position départ" exacte
                                            'Les passages suivants modifieront uniquement la valeur de X
                                            'Quand on relachera le bouton souris, il sera possible de calculer la valeur de déplacement dans l'axe X
                                            'par soustraction entre X et XDépart
        If YDepart = 0 Then YDepart = Y     ' idem
        
        Forms(TheEncapsule(2).NumForm).Left = Forms(TheEncapsule(2).NumForm).Left + (X - XDepart)
        Forms(TheEncapsule(2).NumForm).Top = Forms(TheEncapsule(2).NumForm).Top + (Y - YDepart)
        
        'Repositionnons la Window "Encapsuleur" à sa nouvelle position
        Call SetWindowPos(Forms(TheEncapsule(2).NumForm).hwnd, -1, (Forms(TheEncapsule(2).NumForm).Left / 15), (Forms(TheEncapsule(2).NumForm).Top / 15), (Frame1.Width / 15), (Frame1.Height / 15), SWP_FRAMECHANGED)
        
        'Frame1.Move 0, 0       Comme la Window "Encapsuleur" a pour coordonnées celles du Frame, il n'est pas nécessaire ici de repositionner ce dernier
        
    Else
        XDepart = 0     'On n'appuie pas sur le bouton de la souris, remettons la valeur départ à 0
        YDepart = 0
        
    End If
    
End Sub

Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
    Dim PictRect As Rect
    
    If Button = vbLeftButton Then   'Bouton souris enfoncé, on veut seulement déplacer le TextBox
        'Coordonnées souris dans le SCREEN pas dans Form1
        If XDepart = 0 Then XDepart = X
        If YDepart = 0 Then YDepart = Y
        
        'A l'inverse de Frame1 qui est encapsulé, picture1 ne l'est pas. Il est donc nécessaire de le bouger
        Picture1.Move (Picture1.Left + X) - XDepart, (Picture1.Top + Y) - YDepart
        
        Call SetWindowPos(Picture1.hwnd, -1, (Picture1.Left / 15), (Picture1.Top / 15), (Picture1.Width / 15), (Picture1.Height / 15), SWP_FRAMECHANGED)
        
    Else
        XDepart = 0
        YDepart = 0
    End If
    
    Call Module1.WndSetOpacity(Picture1.hwnd, 0, 255)   'Ca, c'est uniquement pour montrer que l'on peut changer la transparence ...
    
End Sub

Private Sub List1_Click()
    Text1.Text = List1.List(List1.ListIndex)    'reprend la valeur sélectionnée dans List1
    Text3.Text = List1.List(List1.ListIndex)    'Idem ici sauf que Text3 n'est pas encapsulé. Tout cela pour montré qu'une valeur saisie, sélectionnée
                                                ' dans un environnement "Encapsulé" peut toujours communiquer avec ceux qui ne le sont pas
End Sub

Public Sub BtTranspOpaque_Click()

    'Change l'opacité des éléments encapsulés en fonction de la valeur saisie dans Text4
    
    Call Module1.WndSetOpacity(TheEncapsule(0).hwnd, 0, Val(Text4.Text))
    Call Module1.WndSetOpacity(TheEncapsule(1).hwnd, 0, Val(Text4.Text))
    Call Module1.WndSetOpacity(TheEncapsule(2).hwnd, 0, Val(Text4.Text))
End Sub

Private Sub Command1_Click()

    'Désencapsuler ...
    
    'Ici les 3 cas ont été pris. Il est évident que l'on peut n'en choisir qu'un seul ou deux ...
    
    Module1.LibereMemoire 0, TheEncapsule, Form1.Text2, Form1, ModeleForm
    Module1.LibereMemoire 1, TheEncapsule, Form1.Combo1, Form1, ModeleForm
    Module1.LibereMemoire 2, TheEncapsule, Form1.Frame1, Form1, ModeleForm
    
    'Remarque : un élément désencapsulé ne répond plus à l'opacification. Il faut l'encapsuler de nouveau pour ...
    
End Sub

Conclusion :


voilà ! j'espère avoir apporté ma pierre. Vu le nombre de personnes qui sont venus, en quelques jours, voir la première version, j'imagine que c'est un sujet qui en intéresse pas mal

Portez vous bien

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.