Forms avec transparence par pixel, problèmes avec GDI

stefsouron Messages postés 8 Date d'inscription samedi 20 octobre 2007 Statut Membre Dernière intervention 29 octobre 2007 - 24 oct. 2007 à 11:38
stefsouron Messages postés 8 Date d'inscription samedi 20 octobre 2007 Statut Membre Dernière intervention 29 octobre 2007 - 24 oct. 2007 à 19:43
Bonjour,

J'ai besoin d'aide pour résoudre un problème d'utilsation des API GDI sous VB 2005. Je sais que mon post est très long, et j'en suis désolé, mais pour obtenir une aide précise, je suis obligé de décrire tout le cheminement effectué...

J'avais besoin d'afficher des forms avec une transparence par pixel, façon widget. Après de nombreuses recherches, je suis tombé sur un code de Pym Corp qui faisait cela très bien, voir l'article ici : http://www.vbfrance.com/codes/FORM-GRAPHIQUE-BORDURES-IRREGULIERES-STYLE-WIDGET_36788.aspx

Mais j'avais aussi besoin de pouvoir modifier la taille d'affichage de la form en question (dans le code proposé, la form ne s'affiche que dans la taille native du fichier PNG choisi).
J'ai commencé par modifier la classe pngForm pour qu'elle soit un peu plus pratique à utiliser :

J'ai ajouté ces variables privées :
   ImageContainer As System.Drawing.Image pour y stocker l'image originale
   ImageWidth As Integer pour y stocker la largeur d'affichage
   ImageHeight As Integer pour y stocker la hauteur d'affichage
   ImageOpacity As Byte pour la valeur alpha générale de la form

Puis j'ai implémenté des propriétés publiques permettant de lire et modifier ces valeurs. A la fin de chaque Set de ces propriétés, je fais appel à une fonction UpdateForm() qui actualise la form à l'écran.

UpdateForm() est une modification de la fonction SetImage(Bitmap, Opacity) écrite dans son code par PymCorp. J'y ai d'abord retiré les paramètres puisque j'utilise les conteneurs créés ci-dessus. Cette fonction, à la base,  créait un Device Context GDI en mémoire, y sélectionnait le bitmap et se s'en servait pour appeller la fonction UpdateLayeredWindow (une fonction de user32.dll).

J'ai d'abord essayé de redimensionner directement le bitmap contenu dans ImageContainer, mais les propriétés Width et Height dans la classe System.Drawing.Image sont Read Only.
J'ai alors essayé de jouer sur le paramètre size de UpdateLayeredWindow, mais en fait, cela ne modifie que la taille d'affichage de la layered window : si on la réduit, l'image est rognée.

J'ai donc fini par utiliser les fonctions de l'api GDI32.DLL : Je crée un bitmap supplémentaire, de la taille voulue pour l'affichage final, avec un Device Context associé. J'y recopie la version redimensionnée de l'image avec StretchBlt, et je me sers de ce DC lors de l'appel à UpdateLayeredWindow.

Sauf que ça ne marche pas...

Voici le code de ma procédure (les fonctions API sont définies dans un module, il n'y a pas d'exception PInvoke à l'éxécution):

Public
Sub UpdateForm()

   ' Gets the screen's Device Context Handle :
   Dim hScreenDC
As IntPtr = GetDC(IntPtr.Zero)

   ' Creates a GDI Bitmap from ImageContainer, with a transparent background :
   Dim hOriginalBMP
As IntPtr =
CType(ImageContainer, Bitmap).GetHbitmap(Color.FromArgb(0, 0, 0, 0))

   ' Creates a screen compatible memory Device Context :
   Dim hOriginalDC
As IntPtr = CreateCompatibleDC(hScreenDC)

   ' Associates this Device Context with the bitmap :
   SelectObject(hOriginalDC, hOriginalBMP)





   ' Creates the bitmap that will receive the resized image, with the final dimensions :



   Dim
hResizedBMP

As


IntPtr = CreateCompatibleBitmap(hOriginalBMP, ImageWidth, ImageHeight)




   ' Creates another screen compatible Device Context :



   Dim
hResizedDC

As


IntPtr = CreateCompatibleDC(hScreenDC)




   ' Associates this with the bitmap :



   SelectObject(hResizedDC, hResizedBMP)






   ' Stretch the bitmap in hOriginalDC to hResizedDC :



   StretchBlt(hResizedDC, 0, 0, ImageWidth, ImageHeight, hOriginalDC, 0, 0, ImageContainer.Width, ImageContainer.Height, SRCCOPY)






   ' Preparing the parameters for the UpdateLayeredWindow function :



   Dim
size

As
SIZE =

New


SIZE(ImageWidth, ImageHeight)




   Dim
pointSource

As
POINT =

New


POINT(0, 0)




   Dim
topPos

As
POINT =

New


POINT(Left, Top)




   Dim
blend

As
BLENDFUNCTION =

New


BLENDFUNCTION()


   blend.BlendOp = AC_SRC_OVER

   blend.BlendFlags = 0

   blend.SourceConstantAlpha = ImageOpacity
   
blend.AlphaFormat = AC_SRC_ALPHA





   ' Updates the layered window :



   UpdateLayeredWindow(Handle, hScreenDC, topPos, size, hResizedDC, pointSource, 0, blend, ULW_ALPHA)






   ' Releasing and destroying ressources :



   ReleaseDC(IntPtr.Zero, hScreenDC)




   If



Not
hOriginalBMP.Equals(IntPtr.Zero)

Then
      


DeleteObject(hOriginalBMP)
   End If


   DeleteDC(hOriginalDC)



   If



Not
hResizedBMP.Equals(IntPtr.Zero)

Then




      DeleteObject(hResizedBMP)


   End If
   DeleteDC(hOriginalDC)

   DeleteDC(hResizedDC)





End



Sub





A l'affichage, il n'y a rien. En fait, il semblerait que le Device Context final (pointé par hResizedDC) soit vide. Si je remplace, dans l'appel à UpdateLayeredWindow, hResizedDC par hOriginalDC, alors cela fonctionne.

Je pense que le problème se situe au niveau de l'opération StretchBlt. J'ai consulté toute la documentation MSDN Library sur l'utilisation de GDI, et n'ai trouvé aucune piste. J'en appelle donc à la communauté, pour voir si quelqu'un entrevoit une solution...

Merci pour la patience de ceux qui ont lu jusque là...

@ + (j'espère, et je continue à chercher...)

1 réponse

stefsouron Messages postés 8 Date d'inscription samedi 20 octobre 2007 Statut Membre Dernière intervention 29 octobre 2007
24 oct. 2007 à 19:43
Ca y est, le problème est résolu, par un appel à un constructeur de la classe System.Drawing.Bitmap .

Le code est disponible à cette adresse :


http://www.vbfrance.com/codes/FORMS-SKINNABLES-STYLE-WIDGET-VB2005_44495.aspx




Merci...
0
Rejoignez-nous