V. L'imprime à l'emploi▲
Remarque : n'oubliez pas de placer vos feuilles et PictureBox dans le même système d'unités que votre imprimante, afin de ne pas compliquer à loisir les conversions d'échelle.
V-A. Utilisation générale▲
V-A-1. Le centrage▲
Pour centrer le texte sur la feuille ou dans un cadre, il suffit d'une simple opération mathématique. Par exemple pour centrer sur la feuille horizontalement
Printer.CurrentX
=
Printer.Width
/
2
-
Printer.TextWidth
(
Machaine)/
2
V-A-2. Encadrer un texte▲
Comme je l'ai dit plus haut, il vaut mieux éviter d'encadrer à la volée, mais pour les passionnés, sachez que la syntaxe est la suivante (pour un cadre situé à 1.5 mm autour du texte) :
machaine =
"Essai d'encadrement"
Printer.Print
machaine;
Printer.Line
Step
(-
Picture1.TextWidth
(
machaine) -
1
.5
, -
1
.5
)-
Step
(
Picture1.TextWidth
(
machaine) +
3
,
Printer.TextHeight
(
machaine) +
3
), , B
Vous noterez juste l'emploi des deux mots clés Step afin de travailler en coordonnées relatives.
V-A-3. Dessiner une case cochée▲
Là c'est très simple si on utilise pleinement le « dynamisme » des propriétés CurrentX et CurrentY :
Printer.Line
-
Step
(
4
, 4
), , B
Printer.Line
-
Step
(-
4
, -
4
)
Printer.Line
Step
(
4
, 0
)-
Step
(-
4
, 4
)
Trace une case cochée de 4 mm de côté.
V-A-4. Insérer une image▲
Le plus simple est que cette image se trouve dans un contrôle image (pas forcément visible d'ailleurs) sur la feuille :
Printer.PaintPicture
Image1.Picture
, PositionX , PositionY
À noter que la position doit être en coordonnées absolues.
V-B. L'impression géométrique▲
Celle-ci est la plus simple. C'est une version un peu plus élaborée de la méthode PrintForm. Elle convient particulièrement pour les impressions d'un formulaire sur une page.
L'idée générale est de placer sur sa feuille, l'ensemble des contrôles qui devront être imprimés, dans une zone virtuelle (ou un picturebox). Éventuellement, pour gagner en qualité, on donnera un rapport hauteur/largeur à cette zone sensiblement égale à celui d'une page A4 dans le sens de l'orientation désiré (le rapport hauteur/largeur d'une feuille A4 en mode portrait est de ) On calcule dans le code les coefficients d'expansions de cette zone vers la zone imprimable (et non vers la feuille) en X et en Y, par exemple :
FacX=(
Printer.Width
-
2
*
MargeX)/
LargeurZone
FacY=(
Printer.Heigth
-
2
*
MargeY)/
HauteurZone
On utilisera le plus petit de ces facteurs pour l'appliquer à la police.
Après cela on va parcourir la collection Controls de la feuille, tester la position ou le container, puis le type de contrôle afin de définir les actions.
Remarque : Placer ces contrôles dans un PictureBox simplifie la mise en place des contrôles sur la feuille, n'oubliez pas de mettre sa propriété ScaleMode égale à celle de l'imprimante. N'utilisez surtout pas de Frame, l'ensemble des coordonnées et dimensions de vos contrôles repasserait en twips.
Cette technique convient mieux à une impression en mode paysage, qui est le mode écran.
Exemple
Ma zone imprimable en paysage fait 257*180. Je place mes contrôles à imprimer dans un PictureBox. J'ai besoin qu'il ait une largeur de 200, je lui donne une hauteur de 140. Bien sûr, je respecte la proportion dans ce cas ce qui me permettra de n'utiliser qu'un seul facteur.
Dans cet exemple je complique pour montrer la conversion, car vous n'êtes pas sans remarquer que mon PictureBox tiendrait sur la feuille avec un facteur 1.
Dans ma feuille je déclare une constante de conversion
Private
Const
FacC! =
1
.285
'facteur de conversion
Et la fonction Impression sera
Private
Sub
Impression
(
)
Dim
DecalY as
single
, DecalX as
Single
, ContImprim as
Control
Printer.ScaleMode
=
6
Printer.Orientation
=
2
Printer.PaperSize
=
9
Printer.PrintQuality
=
-
4
Printer.ColorMode
=
1
DecalX =
20
-
(
Printer.Width
-
Printer.ScaleWidth
) /
2
DecalY =
15
-
(
Printer.Heigth
-
Printer.ScaleHeigth
) /
2
'utilisez le décalage pour que le point(0,0) du PictureBox soit le point (0,0) de la zone imprimable
For
Each
ContImprim In
Me.Controls
If
TypeOf ContImprim Is
Label Then
With
Printer
.FontName
=
ContImprim.FontName
.FontBold
=
ContImprim.FontBold
.FontSize
=
Int
(
FacEch *
ContImprim.FontSize
)
.CurrentX
=
ContImprim.Left
*
FacEch
.CurrentY
=
ContImprim.Top
*
FacEch
End
With
Printer.Print
ContImprim.Caption
ElseIf
TypeOf ContImprim Is
TextBox Then
With
Printer
.FontName
=
ContImprim.FontName
.FontBold
=
ContImprim.FontBold
.FontSize
=
Int
(
FacEch *
ContImprim.FontSize
)
.CurrentX
=
ContImprim.Left
*
FacEch
.CurrentY
=
ContImprim.Top
*
FacEch
End
With
Printer.Print
ContImprim.Text
ElseIf
TypeOf ContImprim Is
CheckBox Then
Printer.Line
(
ContImprim.Left
*
FacEch, ContImprim.Top
*
FacEch)-
Step
(
7
, 7
), , B
If
ContImprim.Value
=
1
Then
Printer.Line
(
ContImprim.Left
*
FacEch, ContImprim.Top
*
FacEch)-
Step
(
7
, 7
)
Printer.Line
((
ContImprim.Left
*
FacEch) +
7
, ContImprim.Top
*
FacEch)-
Step
(-
7
, 7
)
End
If
With
Printer
.FontName
=
ContImprim.FontName
.FontBold
=
ContImprim.FontBold
.FontSize
=
Int
(
ContImprim.FontSize
*
FacEch)
.CurrentX
=
ContImprim.Left
*
FacEch +
10
.CurrentY
=
ContImprim.Top
*
FacEch +
2
End
With
Printer.Print
ContImprim.Caption
ElseIf
TypeOf ContImprim Is
MSFlexGrid Then
With
Printer
.CurrentX
=
ContImprim.Left
*
FacEch
.CurrentY
=
ContImprim.Top
*
FacEch
End
With
Printer.PaintPicture
ContImprim.Picture
, Printer.CurrentX
, Printer.CurrentY
End
If
Next
End
sub
Bien sûr, il s'agit d'un exemple pour quelques types de contrôles, mais le principe peut facilement s'étendre. Pour les polices, n'oubliez pas de prendre des polices TrueType et arrondissez le résultat.
V-C. Impression d'un ListBox.▲
Nous allons prendre le cas d'une liste à sélection unique, peu importe si tous ses éléments sont visibles.
Dim
RappL As
Single
, AligH As
Single
, MaChaine As
String
, LigSel As
Integer
, compteur As
Long
AligH =
Printer.CurrentX
For
compteur =
0
To
List1.ListCount
-
1
If
List1.Selected
(
compteur) Then
LigSel =
compteur
MaChaine =
List1.List
(
compteur)
RappL =
Iif (
Printer.TextWidth
(
MaChaine) >
RappL, Printer.TextWidth
(
MaChaine), RappL)
Printer.Print
MaChaine
Printer.CurrentX
=
AligH
Next
compteur
'encadrement de la liste
Printer.Line
Step
(-
1
, 0
)-
Step
(
RappL +
2
, -
1
*
Printer.TextHeight
(
String
$(
List1.ListCount
-
1
, vbCrLf
))), , B
'sur lignage de la sélection
Printer.Line
Step
(-
1
*
RappL -
2
, Printer.TextHeight
(
String
$(
LigSel, vbCrLf
)))-
Step
(
RappL +
2
, -
1
*
Printer.TextHeight
(
""
)), QBColor
(
8
), B
V-D. Impression de longues chaînes▲
En général, les chaînes sont coupées dans les contrôles par VbCrLf et l'impression se passe bien. Mais par exemples, lors de l'impression d'une chaîne qui n'apparaît pas sur le formulaire, ou dans un TextBox multiligne, ce caractère n'est pas présent dans la chaîne. L'imprimante risque alors de déborder du cadre ou de la feuille. On utilise alors la fonction suivante pour découper la chaîne.
N.B. Cette fonction gère le cas de très longues chaînes, elle peut être optimisée si l'on est sûr de ne jamais rencontrer de chaîne de plus de 3 ou 4 lignes.
Private
Function
DecoupeChaine
(
ByVal
TextLong As
String
, ByVal
TailleMax As
Single
) As
String
(
)
Dim
TabSplit
(
) As
String
, TabRetour
(
) As
String
, compteur1 As
Long
, compteur2 As
Long
Dim
TabMot
(
) As
String
, ChaineInter As
String
TabSplit =
Split
(
TextLong, vbCrLf
)
ReDim
TabRetour
(
0
To
0
)
For
compteur1 =
0
To
UBound
(
TabSplit)
If
Printer.TextWidth
(
TabSplit
(
compteur1)) >
TailleMax Then
TabMot =
Split
(
TabSplit
(
compteur1), " "
)
For
compteur2 =
0
To
UBound
(
TabMot)
If
Printer.TextWidth
(
ChaineInter &
" "
&
TabMot
(
compteur2)) >
TailleMax Then
TabRetour
(
UBound
(
TabRetour)) =
ChaineInter
ChaineInter =
TabMot
(
compteur2)
ReDim
Preserve
TabRetour
(
0
To
UBound
(
TabRetour) +
1
)
Else
ChaineInter =
IIf
(
Len
(
ChaineInter) >
0
, ChaineInter &
" "
&
TabMot
(
compteur2),
TabMot
(
compteur2))
End
If
Next
compteur2
If
Len
(
ChaineInter) >
0
Then
TabRetour
(
UBound
(
TabRetour)) =
ChaineInter
ChaineInter =
""
ReDim
Preserve
TabRetour
(
0
To
UBound
(
TabRetour) +
1
)
End
If
Else
TabRetour
(
UBound
(
TabRetour)) =
TabSplit
(
compteur1)
ReDim
Preserve
TabRetour
(
0
To
UBound
(
TabRetour) +
1
)
End
If
Next
compteur1
ReDim
Preserve
TabRetour
(
0
To
UBound
(
TabRetour) -
1
)
DecoupeChaine =
TabRetour
End
Function
De manière générale il est bon de traiter ces chaînes de façon à ne pas laisser dedans de caractères « VbCrLf » ce qui ramènerait currentX contre la marge de gauche, sauf si c'est évidemment le but rechercher.
V-E. Imprimer un recordset ou une grille▲
Je regroupe ces deux cas, car ils sont identiques. En effet un recordset peut être imaginé comme une grille de colonne champ et de ligne enregistrement. J'ai mis dans cet exemple deux modes d'encadrement différents, avant ou après l'écriture de la chaîne.
Private
Sub
cmdPrint_Click
(
)
Dim
TabLong
(
) As
Single
, compteur As
Long
, Total As
Single
Dim
NomChamp As
String
, ReqLong As
New
ADODB.Recordset
, strSQL As
String
Dim
XFixe As
Single
, YMobile As
Single
, prnstring As
String
, ConnTemp As
New
ADODB.Connection
ConnTemp.ConnectionString
=
Adodc1.ConnectionString
ConnTemp.Open
With
Printer
.TrackDefault
=
True
.ScaleMode
=
6
.Orientation
=
1
.PaperSize
=
9
.PrintQuality
=
-
4
.ColorMode
=
1
End
With
XFixe =
20
-
(
210
-
Printer.ScaleWidth
) /
2
YMobile =
15
-
(
297
-
Printer.ScaleHeight
) /
2
ReDim
TabLong
(
ReqGrille.Fields.Count
-
1
)
With
ReqGrille
For
compteur =
0
To
.Fields.Count
-
1
NomChamp =
.Fields
(
compteur).Name
strSQL =
"SELECT MAX(len(["
&
NomChamp &
"])) AS LeMax FROM LaTable"
ReqLong.Open
strSQL, ConnTemp, adOpenStatic
, adLockReadOnly
ReqLong.MoveFirst
TabLong
(
compteur) =
IIf
(
ReqLong!lemax >
Len
(
NomChamp), ReqLong!lemax, Len
(
NomChamp))
ReqLong.Close
TabLong
(
compteur) =
Printer.TextWidth
(
String
(
TabLong
(
compteur), "X"
)) +
4
Total =
Total +
TabLong
(
compteur)
Next
compteur
.MoveFirst
Total =
XFixe
For
compteur =
0
To
.Fields.Count
-
1
Printer.Line
(
Total, YMobile)-
Step
(
TabLong
(
compteur), 8
), QBColor
(
6
), BF
prnstring =
CStr
(
.Fields
(
compteur).Name
)
Printer.CurrentX
=
Total +
TabLong
(
compteur) /
2
-
Printer.TextWidth
(
prnstring) /
2
Printer.CurrentY
=
(
YMobile +
4
) -
Printer.TextHeight
(
prnstring) /
2
Printer.Print
prnstring
Total =
Total +
TabLong
(
compteur)
Next
compteur
YMobile =
YMobile +
8
While
Not
.EOF
Total =
XFixe
For
compteur =
0
To
.Fields.Count
-
1
prnstring =
CStr
(
.Fields
(
compteur).Value
)
Printer.CurrentX
=
Total +
TabLong
(
compteur) /
2
-
Printer.TextWidth
(
prnstring) /
2
Printer.CurrentY
=
(
YMobile +
4
) -
Printer.TextHeight
(
prnstring) /
2
Printer.Print
prnstring
Total =
Total +
TabLong
(
compteur)
Printer.Line
(
XFixe, YMobile)-
Step
(
Total -
XFixe, 8
), , B
Next
compteur
.MoveNext
YMobile =
YMobile +
8
Wend
End
With
Printer.EndDoc
End
Sub
V-F. L'impression sur plusieurs pages▲
Le cas se pose en général, pour l'impression de longues listes, de grands tableaux. J'envisage ici le cas d'une impression continue. En effet, l'impression de plusieurs fiches liées à un recordset par exemple n'est jamais qu'un cas d'impression de page unique répété à chaque changement de ligne du recordset.
Mais supposons pour reprendre le cas précédent, que je souhaite imprimer ma grille sans connaître au préalable son nombre de lignes. Je vais prendre un exemple que j'ai développé, il y a quelques années pour une association qui organisait un concours de pêche.
Le travail était simple, tout tenait dans une table. Il fallait pouvoir imprimer la liste des participants, puis le classement.
Sur ma page devait apparaître le logo de l'association, le titre, la date, le tableau et en bas le numéro de page. Donc sur mon formulaire il y avait un contrôle image, deux labels et un MSFlexGrid (plus un dessin de poisson, mais lui il n'y avait pas besoin de l'imprimer).
Je me suis basé sur les marges habituelles, il me fallait trois centimètres en haut pour le logo, et un centimètre en bas pour le numéro de page. Chaque ligne devait faire 8 mm de haut.
L'impression était en paysage afin de pouvoir visualiser toutes les colonnes.
Afin de connaître le nombre de pages, pour pouvoir valoriser les propriétés FromPage et ToPage de la boite de dialogue d'impression, j'ai procédé comme suit :
Ma hauteur imprimable pour la grille était de 140 (210-2*15-30-10). Il rentrait donc 17 lignes de 8 mm par page soit 16 enregistrements plus la ligne de titre.
Ce préambule n'a pas pour but de faire de vous des experts en concours de pêche, mais bien de vous montrer comment je me suis posé le problème. Allons-y !
Le code est le même que celui fait précédemment, sauf la différence d'orientation, dans la fonction ci-dessous je mettrai juste les lignes différentes.
Private
Sub
Impression
(
)
Printer.Orientation
=
2
XFixe =
20
-
(
297
-
Printer.ScaleWidth
) /
2
YMobile =
45
-
(
210
-
Printer.ScaleHeight
) /
2
NbLigne=
ReqList.Recordcount
NbPage=
Iif
(
NbLigne Mod
16
>
0
, NbLigne \
16
+
1
, NbLigne \
16
)
À la place de l'impression de la ligne de titre :
Call
EntetePied
(
TabLong)
YMobile =
YMobile +
8
Et dans la boucle du recordset un compteur qui :
If
Compt16=
16
then
Compt16=
0
Printer.NewPage
Call
EntetePied
YMobile =
53
-
(
210
-
Printer.ScaleHeight
) /
2
'53, car 45+8 de la première ligne
End
if
End
Sub
Private
Sub
EntetePied
(
TabTaille
(
) as
Single
)
Dim
Xfixe!, Yfixe!, Total&
, compteur&
XFixe =
20
-
(
297
-
Printer.ScaleWidth
) /
2
YFixe =
15
-
(
210
-
Printer.ScaleHeight
) /
2
YfirstLigne =
45
-
(
210
-
Printer.ScaleHeight
) /
2
Printer.PaintPicture
imgLogo.Picture
, XFixe, YFixe
Printer.CurrentX
=
Printer.Width
/
2
-
Printer.TextWidth
(
Titre)/
2
Printer.CurrentY
=
Yfixe+
15
-
Printer.TextHeigth
(
Titre)/
2
Printer.Print
Titre
Printer.CurrentX
=
Printer.Width
-
20
-
Printer.TextWidth
(
LaDate)
Printer.CurrentY
=
Yfixe+
15
-
Printer.TextHeigth
(
LaDate)/
2
Printer.Print
LaDate
'Insère la ligne de titre
Total =
XFixe
For
compteur =
0
To
.Fields.Count
-
1
Printer.Line
(
Total, YMobile)-
Step
(
TabTaille
(
compteur), 8
), QBColor
(
6
), BF
prnstring =
CStr
(
ReqGrille.Fields
(
compteur).Name
)
Printer.CurrentX
=
Total +
TabTaille
(
compteur) /
2
-
Printer.TextWidth
(
prnstring) /
2
Printer.CurrentY
=
(
YMobile +
4
) -
Printer.TextHeight
(
prnstring) /
2
Printer.Print
prnstring
Total =
Total +
TabTaille
(
compteur)
Next
compteur
'Met Le pied De page
strPied=
"Page : "
&
Printer.Page
&
" de "
&
NbPage
Printer.CurrentX
=
Printer.Width
/
2
-
Printer.TextWidth
(
strPied)/
2
Printer.CurrentY
=
Yfixe+
Printer.ScaleHeight
-
20
-
Printer.TextHeigth
(
strPied)/
2
Printer.Print
strPied
End
sub
Vous pouvez noter que l'on peut très bien écrire le pied de page avant le corps du texte.
V-G. Prévisualisation▲
Bien sûr, vous vous voyez déjà poussant des wagonnets de feuilles vers l'imprimante afin de réaliser vos tests d'impression. Afin de sauver les forêts, je vais vous montrer comment fabriquer une feuille de visualisation.
Dans cet exemple, je reste dans un cas d'impression paysage, mais on peut faire un outil beaucoup plus complexe en gérant dynamiquement l'orientation de la feuille, faire une réduction de taille, etc.
Tous les scalemode de la feuille et des picturebox sont en millimètres
Dans ma feuille je mets les contrôles suivants :
-
Un commandButton
caption
Fermer
Dans ma feuille je mets les contrôles suivant :
-
Un PictureBox
AutoRedraw
True
Name
PctCont
Height
109.5
Width
153
Dans ma feuille je mets les contrôles suivants :
-
Dans ce PictureBox, un HscrollBar sur toute la largeur
Name
HScroll1
LargeChange
148
SmallChange
15
Dans ma feuille je mets les contrôles suivants :
-
Un VscrollBar sur toute la hauteur
Name
VScroll1
LargeChange
105
SmallChange
10
Dans ma feuille je mets les contrôles suivants :
- Et enfin un autre pictureBox (donc contenu dans pctCont)
AutoRedraw |
True |
Appearance |
Flat |
Name |
PctPict |
Height |
210 |
Index |
1 |
Left |
0 |
Top |
0 |
Width |
153 |
Dans ma feuille je mets les contrôles suivants :
Dans le code de cette feuille
Private
Sub
HScroll1_Change
(
)
pctCible
(
1
).Left
=
HScroll1.Value
*
-
1
End
Sub
Private
Sub
VScroll1_Change
(
)
pctCible
(
1
).Top
=
VScroll1.Value
*
-
1
End
Sub
L'astuce consiste à se dire qu'à peu de choses près l'objet printer et le PictureBox fonctionnent de la même façon.
Donc on écrit une fonction ou dans le code on remplacera partout Printer par Cible. Pour les méthodes différentes pour les deux objets, on fera un test avec la fonction Is
Public
sub
Impression (
Cible as
object)
If
Cible Is
Printer Then
With
Printer
.TrackDefault
=
True
.ScaleMode
=
6
.Orientation
=
1
.PaperSize
=
9
.PrintQuality
=
-
4
.ColorMode
=
1
End
With
End
if
'
'
'
If
Cible Is
Printer then
Printer.EndDoc
Else
frmView.Show
End
If
End
Sub
Le seul cas particulier étant pour NewPage
If
Cible Is
Printer then
Printer.NewPage
Else
Cible.Visible
=
False
Load frmView.pctPict
(
Count+
1
)
Set
Cible=
frmView.pctPict
(
Count)
Cible.Visible
=
True
End
if
Dans le cas d'une impression multipage, il convient de modifier le code d'événements des scrollbars.
L'appel de la fonction se fait de la manière suivante
Call Impression(frmView.pctPict.Picture)