feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009
-
26 oct. 2007 à 17:56
feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009
-
31 oct. 2007 à 18:45
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.
feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009 31 oct. 2007 à 18:45
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
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
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
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 26 oct. 2007 à 18:09
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
feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009 26 oct. 2007 à 19:32
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.
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 27 oct. 2007 à 13:16
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
feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009 27 oct. 2007 à 14:36
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...
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 28 oct. 2007 à 10:26
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).
<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
feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009 28 oct. 2007 à 11:41
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.
cs_rt15
Messages postés3874Date d'inscriptionmardi 8 mars 2005StatutModérateurDernière intervention 7 novembre 201413 30 oct. 2007 à 09:52
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
feanor91
Messages postés25Date d'inscriptionlundi 5 décembre 2005StatutMembreDernière intervention23 avril 2009 30 oct. 2007 à 17:27
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.