Le mystère des PictureBox

Un PictureBox contient 2 images accessibles par ses 2 propriétés : .Picture et .Image. Mais ces 2 propriétés sont en fait très différentes. Voici l'explication de ces 2 propriétés :

Propriété ".Picture" (image d'origine)

MyPictureBox.Picture est un objet StdPicture (IPictureDisp) défini par la bibliothèque StdOle2.tlb ("OLE Automation" doit apparaître dans la liste des références : menu Projet --> Références). Voir les propriétés de StdPicture dans l'explorateur d'objet de VB. Cet objet possède les propriétés suivantes :
Handle (par défaut), height, Width, hPal, et Type
et une méthode :
Render (hdc As Long, x As Long, y As Long, cx As Long, cy As Long, xSrc As OLE_XPOS_HIMETRIC, ySrc As OLE_YPOS_HIMETRIC, cxSrc As OLE_XSIZE_HIMETRIC, cySrc As OLE_YSIZE_HIMETRIC, prcWBounds As Any)

Cette image est l'image originale qui a été chargée dans la propriété .Picture du PictureBox. Ce n'est pas l'image affichée dans le Device Context du PictureBox. Son type est obligatoirement un des 3 suivants :
        BITMAP
        WINDOWS METAFILE (ext.: wmf)
        ENHANCED METAFILE (ext.: emf)

Cette image d'origine n'est pas altérée par le dimensionnement du PictureBox, ni par les procédures GDI (Graphic Device Interface) qu'on lui applique. Cette image est stockée en mémoire, et son Handle permet de la manipuler. Son Handle est : MyPictureBox.Picture.Handle.
On peut connaître le type de cette image avec l'API :
GetObjectType qui s'utilise ainsi :

Private Declare Function GetObjectType Lib "gdi32" (ByVal hgdiobj As Long) As Long
Private Const OBJ_BITMAP = 7
Private Const OBJ_METAFILE = 9
Private Const OBJ_ENHMETAFILE = 13
Select Case GetObjectType(MyPictureBox.Picture.Handle)
        Case OBJ_BITMAP
' (C'est un BITMAP)
        Case OBJ_METAFILE
' (Métafile)
        Case OBJ_ENHMETAFILE
' (Enhanced Metafile)
End Select

Comme .Handle est la propriété par défaut de l'objet MyPictureBox.Picture, on peut récupérer le Handle ainsi :
Dim MyPictureHandle As Long
MyPictureHandle = myPictureBox.Picture (ou : MyPictureHandle = myPictureBox.Picture.Handle)

Par exemple :
    Dim Hwnd As Long
    Hwnd = MyPictureBox.Picture '(ne renvoie pas d'erreur)
    MyPictureBox.Picture = Hwnd '(renvoie une erreur)

Propriété ".Image" (image apparente ou dessinée)

MyPictureBox.Image est aussi un objet StdPicture (IPictureDisp) défini par la bibliothèque StdOle2.tlb. Son Handle est celui de l'image affichée dans le Device Context du PictureBox. Cette image est toujours une image BITMAP, même si l'image d'origine définie par la propriété .Picture n'est pas une image BITMAP. Les dimensions en pixels de cette image dépendent des dimensions et du clipping du PictureBox, et sont donc modifiées en cas de redimensionnement du contrôle

De plus, les procédures GDI qu'on applique (quand on dessine des formes ou qu'on écrit du texte dans le DC du PictureBox) modifient l'affichage et donc l'image BITMAP définie par ce Handle (la propriété .AutoRedraw du PictureBox doit être mise à VRAI).

Le fait de récupérer cette image peut être intéressant si on a dessiné ou écrit dedans et qu'on désire imprimer ou enregistrer le résultat, mais il faut prendre garde de bien dimensionner le PictureBox en fonction de la résolution qu'on désire obtenir. Un PictureBox trop petit donnera une image de médiocre qualité.

Rappelons que, si la propriété .ScaleMode du conteneur est "Twip", les dimensions intérieures (données par les propriétés .ScaleHeight et .ScaleWidth) du PictureBox sont égales à 15 fois le nombre de pixels en standard (Screen.TwipsPerPixelY et Screen.TwipsPerPixelX)
Exemple : un PictureBox tel que .ScaleWidth = 3000 et .ScaleHeight = 6000 donnera une image de 400 x 200 pixels.
Les fonction LoadPicture et SavePicture

Function LoadPicture([filename]) As IPictureDisp
Le résultat de cette fonction est un objet StdPicture (IPictureDisp), membre de VB.Global
(Remarque : Il existe un autre LoadPicture, membre de stdole.StdFunctions et qui autorise 4 arguments)
Sub SavePicture(Picture As IPictureDisp, filename As String)

On peut par exemple les utiliser ainsi :
  
  Chargement d'une image dans le PictureBox :
        Dim IPic As StdPicture, PicHandle As Long
        Set IPic = LoadPicture(App.Path & "\Imagetest.bmp")
        PicHandle = IPic.Handle
        Set MyPictureBox.Picture = IPic

    Récupération de l'image chargée dans le PictureBox (ne dépend pas de la taille du PictureBox) :
        Dim IPic As StdPicture
        Set IPic = MyPictureBox.Picture
        SavePicture P, "C:\plop.bmp"

    Récupération de l'image dessinée dans le PictureBox (dépend de la taille du PictureBox) :
        Dim IPic As StdPicture
        Set IPic = MyPictureBox.Image
        SavePicture P, "C:\plop.bmp"

Retour à l'accueil