Capture du contenu complet d'un ongle en VB2005 [Résolu]

Signaler
Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009
-
Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009
-
Bonjour

Voici mon problème :

Je dessine sur l'onglet d'une tabpage un certain nombres de label qui peuvent dépassés la largeur de mon contrôle, donc, il y a une scrolbar horizontale. De cet onglet, je veux en faire une copie à destination de l'imprimante et dans l'idéal du presse papier. J'ai récupérer un code qui fonctionne bien pour l'impression (je n'ai pas encore traité le press paier), MAIS, il ne me capture que la partie visible du contrôle. Ma question est donc comment faire pour capturer l'intégralité du contenu du contrôle visible+invisible, je donne bien à ma bitmap de capture la taille complète du contrôle.

Si quelqu'un sait, ce serait sympa de m'aiguiller.

10 réponses

Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009

Bonsoir,

Comme promis vici le code récupéré ici et là sur le net.

Tout d'abord la procédure permettant la capture de l'objet désiré :

    Private Const WM_PRINT As Integer = &H317
    Private Const PRF_CLIENT As Integer = &H4
    Private Const PRF_CHILDREN As Integer = &H10

Public Function PrintControl(ByVal Ctrl As System.Windows.Forms.Control) As System.Drawing.Bitmap
     
        Dim bmp As System.Drawing.Bitmap = Nothing
        Dim gr As System.Drawing.Graphics = Nothing
        Dim hdc As IntPtr = IntPtr.Zero

        Dim newBounds As Rectangle
        newBounds.Location() = New Point(0, 0)
        newBounds.Height = Ctrl.PreferredSize.Height 'important, car PreferredSize contient la taille réelle du contrôle
        newBounds.Width = Ctrl.PreferredSize.Width
        Dim MaRegion As New Region(newBounds)

        Try
            bmp = New System.Drawing.Bitmap(Ctrl.PreferredSize.Width, Ctrl.PreferredSize.Height, Ctrl.CreateGraphics())
            gr = Graphics.FromImage(bmp)

            gr.FillRegion(Brushes.Silver, MaRegion) 'ici, je rempli la totaltilé de l'objet graphic avec la couleur de mon contrôle car sinon, la partie hors écran est rempli par une couleur transparente.

            hdc = gr.GetHdc()
            Dim wParam As IntPtr = hdc
            Dim lParam As IntPtr = New IntPtr(PRF_CLIENT Or PRF_CHILDREN)
            Dim msg As System.Windows.Forms.Message = System.Windows.Forms.Message.Create(Ctrl.Handle, WM_PRINT, wParam, lParam)
            MyBase.WndProc(msg)
        Catch
        Finally
            If Not gr Is Nothing Then
                If hdc <> IntPtr.Zero Then gr.ReleaseHdc(hdc)
                gr.Dispose()
            End If
        End Try
        Return bmp
    End Function

Maintenant, son appel (dans un double clique sur le contrôle, donc sender contient le contrôle au complet):

        Dim h As Integer = sender.PreferredSize.Height
        Dim w As Integer = sender.PreferredSize.Width

        Dim TargetImg As New System.Drawing.Bitmap(w, h)

        TargetImg = PrintControl(sender)

TargetImg va donc contenir l'image du contenu du contrôle. Il suffit de la sauvegarder, ou de l'envoyer, sur l'imprimant ou dans le clipboard, perso je la sauvegarde donc :

        If Dir(MyRepUsers, vbDirectory) = "" Then
            MkDir(MyRepUsers) 'si le répertoire n'existe pas je le crè (MyRepUser contient le chemin de mes documents)
        End If

        TargetImg.Save(MyRepUsers + "" + sender.name + ".bmp") 'on sauve l'image

Je passe l'image à la procédure de découpage:

    nbToPrint = SplitImage(MyRepUsers + "" + sender.name + ".bmp")

Que voici :

    Function SplitImage(ByVal path As String) As Integer
        Dim original As New Bitmap(path)
        Dim focusRectangle As New Rectangle()
        Dim destination As Drawing.Bitmap
        Dim w As Integer = 0
        Dim i As Integer = 1
        Dim OK As Boolean = False

        Do While w < original.Width
            focusRectangle.Y = 0
            focusRectangle.Height = original.Height
            focusRectangle.X = w
'pour ma part, je doit découper à des endroits précis pour ne pas couper un contrôle en 2, d'où ce test :
            If Me.chkU.Checked Then                 focusRectangle.Width 924 + IIf(i 1, 10, 0)
            Else                focusRectangle.Width 809 + IIf(i 1, 10, 0)
            End If
            If focusRectangle.Width + focusRectangle.X > original.Width Then
                focusRectangle.Width = original.Width - focusRectangle.X
            End If

            'et on découpe
            destination = original.Clone(focusRectangle, Imaging.PixelFormat.DontCare) 'on définit un second BitMap Clonant une partie du 1ere BitMap avec le rectangle
             'et on sauve avec un index
            destination.Save(path.Substring(0, Len(path) - 4) + "_" + i.ToString() + ".bmp")
            If Me.chkU.Checked Then                w +924 + IIf(i 1, 10, 0)
            Else                w +809 + IIf(i 1, 10, 0)
            End If
            i += 1
        Loop

        Return i - 1 'à la sortie, je me retrouve avec i-1 image de mon image principale

    End Function

Il ne reste plus qu'à les imprimer, retour dans le double click du contrôle :

            For i As Integer = 1 To nbToPrint
                If nbToPrint > 1 Then
                    PrepareAndPrint(MyRepUsers + "" + sender.name + "_" + i.ToString + ".bmp")
                Else
                   'il se peut que l'image n'est pas eut besoin d'être découpée, donc j'imprime l'image principale sans index
                    PrepareAndPrint(MyRepUsers + "" + sender.name + ".bmp")
                End If
            Next

 et la fonction d'impression :

    Private Sub PrepareAndPrint(ByVal Path As String)
        Dim doc As Printing.PrintDocument = New Printing.PrintDocument
        Dim printer As PrintDialog = New PrintDialog

        doc.DefaultPageSettings.Landscape = True 'rajouté pour ne pas avoir à le choisir dans le dialogue d'impression

        imgBaie = New Bitmap(Path) 'définie en public car PrintPageHandler ce sert de la variable

        AddHandler doc.PrintPage, AddressOf PrintPageHandler
        printer.Document = doc

        Dim response As Windows.Forms.DialogResult = printer.ShowDialog()
        If response = Windows.Forms.DialogResult.OK Then
            doc.Print()
        End If
    End Sub

    Private Sub PrintPageHandler(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)

        Dim canvas As Graphics = e.Graphics
        ' The printer will print the image to whatever bounds are set here (in the next two lines)
        Dim topLeft As Point = New Point(0, 0)
        Dim bottomRight As Point = New Point(e.PageBounds.Width, e.PageBounds.Height)

        ' The rest of the code should not need to be modified
        ' This algorithm makes sure the image scales properly

        Dim pageHeight As Integer = bottomRight.Y - topLeft.Y
        Dim pageWidth As Integer = bottomRight.X - topLeft.X
       
        'sur ces deux ligne, j'ai rajouté +50 car sinon, l'image était trop grande en hauteur et on n'avais pas le bas
        Dim scaleHeight As Single = pageHeight / (imgBaie.Height + 50)
        Dim scaleWidth As Single = pageWidth / (imgBaie.Width + 50)

        ' NewHeight and NewWidth determine the drawing area.
        ' Assume scaleHeight < scaleWidth

        Dim newHeight As Integer = scaleHeight * imgBaie.Height
        Dim newWidth As Integer = scaleHeight * imgBaie.Width

        ' Now check assumption, and correct if wrong

        If scaleWidth < scaleHeight Then
            newHeight = scaleWidth * imgBaie.Height
            newWidth = scaleWidth * imgBaie.Width
        End If

        canvas.DrawImage(imgBaie, 0, 0, newWidth, newHeight)

    End Sub

Et voilà, tout ce code me fait pile poil ce que je voulais.
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
11
Salut,


Tu as posté dans le bar... Pourquoi ne pas avior poster dans du VB2005 ?


Je sais pas en dotnet, mais c'est généralement diffcile de faire une
"impression écran" d'une zone qui n'est pas affiché sur l'écran.


Faut que tu demande à tes contrôles de "se rendre" dans une bitmap que tu as prévu pour.


En théorie, l'envoie d'un message WM_PRINTCLIENT est sensé le permettre, mais il faut que l'applicaiton traite ce message convenablement, ce qui est très rarement le cas.

<hr size="2" width="100%" />3ème année en ecole d'ingé d'info cherche stage de 4 mois à partir du 01/04/08
Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009

Merci de l'info, l'idée de la btimap n'est pas bête, à tester par contre peux-tu expliciter le WM_PRINTCLIENT? Quand à poster dans VB2005, je n'e l'ai pas vu dans les thèmes, j'ai du louper un truc, je vais réessayer.
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
49
Déplacé sur vbfrance











<hr />
-My Blog-
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
11
Je cite mon lien (Et je m'excuse pour les fautes de frappe...) :

"The WM_PRINTCLIENT message is sent to a window to request that it draw its client area in the specified device context, most commonly in a printer device context."

Le message WM_PRINTCLIENT est envoyé à une fenêtre pour lui demander de dessiner sa partie cliente dans le device context (Je serais pas vraiment traduire ça... Un contexte d'affichage ? Un gestionnaire de rendu ?), le plus souvent dans un device context d'imprimante.

Tu peux créer un de device context (DC), et récupérer un handle sur ce DC (Un hDC). Tu peux associer ce contexte à une bitmap. Finalement, tu envoie le message WM_PRINTCLIENT avec SendMessage, et tu prie pour que la fenêtre ait réagie correctement et se soit dessinée dans ta bitmap.

Après, je connais pas assez le dotnet pour te dire s'il existe une autre solution miraculeuse, mais c'est comme ça que je ferais en C/C++, Delphi, VB6...

<hr width="100%" size="2" />
3ème année en ecole d'ingé d'info cherche stage de 4 mois à partir du 01/04/08
Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009

OK, merci, je vais voir de ce côté là, Par contre, la partie cliente, c'est le contenu complet de la fenêtre ou bien juste la partie afficher, avec le GDI, on est jamais sur de rien, sinon, j'avais pensé à une copie par bande de pixel en faisant défiler moi même  la fenêtre...
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
11
Je ne suis parvenu à faire dessiner qu'un contrôle à la fois : cela ne dessine pas les contrôles enfants même si on le demande avec PRF_OWNED.


Par contre, contrairement à tout ce qui est BitBlt et autre impression d'écran, ça fonctionne même quand le contrôle n'est sur pas l'écran, que sa propriété visible soit à False ou qu'il se trouve hors de l'écran. Ce n'est pas une copie qui est faîte : on demande vraiment au contrôle de se dessiner.


J'ai fait une form, j'ai mis un bouton et une picturebox dessus.
Lorsque l'on clique sur le bouton, celui-ci est dessiné dans la picturebox.
Le dessin dans la picturebox n'est que temporaire. Je peux t'aider à mettre cette image en place dans le clipboard via sa méthode SetImage si tu le souhaites (Mais j'ai pas trop de temps ces derniers temps).


Imports System.Runtime.InteropServices


Public Class Form1


  Private Const WM_PRINT = 791
  Private Const WM_PRINTCLIENT = 792


  Private Const PRF_CHECKVISIBLE = 1
  Private Const PRF_NONCLIENT = 2
  Private Const PRF_CLIENT = 4
  Private Const PRF_ERASEBKGND = 8
  Private Const PRF_OWNED = 32


  <DllImport("User32.dll")> _
  Private Shared Function SendMessage( _
      ByVal Handle As Int32, _
      ByVal wMsg As Int32, _
      ByVal wParam As Int32, _
      ByVal lParam As Int32) As Int32
  End Function


  <DllImport("User32.dll")> _
  Private Shared Function GetDC( _
      ByVal hwnd As Int32) As Int32
  End Function


  <DllImport("User32.dll")> _
   Private Shared Function ReleaseDC( _
       ByVal hwnd As Int32, _
       ByVal hdc As Int32) As Int32
  End Function


  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim hDC As Int32


    hDC = GetDC(Me.PictureBox1.Handle)
    SendMessage(Me.Button1.Handle, WM_PRINTCLIENT, hDC, PRF_OWNED)
    ReleaseDC(Me.PictureBox1.Handle, hDC)
  End Sub


End Class





<hr width="100%" size="2" />
3ème année en ecole d'ingé d'info cherche stage de 4 mois à partir du 01/04/08
Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009

OK, je testerais ça, le souci j'ai que j'ai des dizaines de controles à dessiner, en fait je dessines des labels à l'intérieur d'autre labels, comme des tirroirs dans des armoires en quelques sorte et ce avec plusieurs armoires. En tous cas merci pour le source.
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
11
Ce matin je me suis dit : bon, j'en dessine un, je dois pouvoir dessiner récursivement les contrôles contenus dedans. Suffit de commencer par les dessiner dans une bitmap, puis à recopier aux coordonnées qui vont bien dans une bitmap finale.

Et là, c'est le drame : je tombe sur la méthode DrawToBitmap...

Donc il existe bien une méthode miracle en dotnet :

Public Class Form1

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    DrawControlInClipboard(GroupBox1)
  End Sub

  Private Sub DrawControlInClipboard(ByRef lpControl As Control)
    Dim lpBitmap As New Bitmap(lpControl.Width, lpControl.Height)

    lpControl.DrawToBitmap(lpBitmap, lpControl.ClientRectangle)
    Clipboard.SetImage(lpBitmap)
  End Sub

End Class

<hr size="2" width="100%" />3ème année en ecole d'ingé d'info cherche stage de 4 mois à partir du 01/04/08
Messages postés
25
Date d'inscription
lundi 5 décembre 2005
Statut
Membre
Dernière intervention
23 avril 2009

Salut

Je voulais coller mon code, mais hier, je n'ai pas réussi à m'identifier depuis le bureau, parceque en fouillant sur le net, j'ai trouvé une fonction toute écrites, ça marche nickel en utilisant les messages. j'esayerais de poster demain car aujourd'hui je n'ai pas eu le temps. En fait, ça capture l'intégralité du contenu de la page, au détail près que la partie non visible à l'écran avais un fond transparent donc, j'ai joué sur les couleurs du contrôleet de l'objet graphic pour y arriver, après, pour imprimer, j'ai récupérer une fonction de découpe de l'image (car bien évidemment, elle était trop grande por passer sur une seule page de l'imprimante) puis une pour imprimer les images obtenus et tout est nickel.Je mettrais les différents code demain.