Première précision, c'est une adaptation. Le code original n'est pas de moi. Je me suis contenté de le commenter et de l'expliquer, donc le mérite revient à Damon Chandler, auteur du code original. Je me suis quand même permis de réorienter le code dans une optique un peu plus objet. Voilà donc l'essentiel de ma contribution. Merci à Reynald PIEPZSYK pour l'erreur sur le message WM_PAINT.
La stratégie est la suivante : nous allons détourner la méthode originale qui gère la zone client de la fenêtre MDI pour la remplacer par une méthode de substitution qui mettra en place une mosaïque d'images.
Dans le code qui va suivre, j'ai appelé ma Form principale « Main ». Bien sûr sa propriété FormStyle est à fsMDIForm.
On en profite pour placer l'image de fond dans un TImage que l'on appelle Background (attention, il est impératif que l'image placée dans le fond soit un Bitmap que l'on place dans la propriété Picture->Bitmap du TImage).
Premièrement on va définir deux pointeurs de fonctions que l'on va utiliser pour le traitement des procédures de fenêtres.
FARPROC NewClientWP;
FARPROC OldClientWP;
On va aussi définir la méthode de dessin qui permet d'afficher une mosaïque à partir de l'image :
void
__fastcall TileBlt(HDC HDestDC,int
DestWidth,int
DestHeight,HDC HSourceDC,int
SourceWidth,int
SourceHeight);
Et enfin la nouvelle procédure de fenêtre :
void
__fastcall MDIClientWndProc(TMessage &
Msg);
Le fichier Main.h doit contenir ceci :
#ifndef UnitMainH
#define UnitMainH
#include
<Classes.hpp>
#include
<Controls.hpp>
#include
<StdCtrls.hpp>
#include
<Forms.hpp>
#include
<ExtCtrls.hpp>
#include
<Graphics.hpp>
class
TMain : public
TForm
{
__published
:
// Composants gérés par l'EDI
TImage *
Background;
void
__fastcall FormCreate(TObject *
Sender);
private
:
// Déclarations utilisateur
FARPROC NewClientWP ;
FARPROC OldClientWP ;
void
__fastcall TileBlt(HDC HDestDC,
int
DestWidth,
int
DestHeight,
HDC HSourceDC,
int
SourceWidth,
int
SourceHeight);
void
__fastcall MDIClientWndProc(TMessage &
Msg);
public
:
// Déclarations utilisateur
__ fastcall TMain(TComponent*
Owner);
}
;
//---------------------------------------------------------------------------
extern
PACKAGE TMain *
Main;
//---------------------------------------------------------------------------
#endif
Bien, maintenant nous allons nous intéresser au contenu du fichier source (*.cpp).
Ensuite, dans le constructeur de la Form on va initialiser les pointeurs de fonctions :
__fastcall TMain::
TMain(TComponent*
Owner): TForm(Owner)
{
// D'abord pour la nouvelle procédure de fenêtre on crée une instance :
NewClientWP =
(FARPROC)MakeObjectInstance(MDIClientWndProc);
// la méthode MDIClientWndProc sera implémentée par la suite..
/* On fait pointer le deuxième pointeur sur l'ancienne WindowProc et
on établit la permutation des WindowProc avec la fonction API
SetWindowLong
On remarque que l'on passe en argument le Handle de la zone Client et pas celui de la fenêtre au complet*/
OldClientWP =
(FARPROC)SetWindowLong(ClientHandle, GWL_WNDPROC, (LONG)NewClientWP);
}
Ensuite on va implémenter la nouvelle méthode qui gère la procédure de fenêtre de la zone client.
Procédure pour texture de fond :
void
__fastcall TMain::
MDIClientWndProc(TMessage Msg)
{
/* Rappel pour ceux qui ne sont pas familiers des API Win32 et du
fonctionnement de base d'une appli Windows
La WindowProc (ainsi nommée sous CBuilder, dans un prog "classique" elle
s'appelle plutôt WndProc) sert à interpréter les messages que Windows
envoie à l'application. Ces messages sont ici contenus dans une structure
TMessage.
Le but du jeu est d'intercepter les messages qui nous intéressent et de
rediriger les autres vers l'ancienne WindowProc (par l'intermédiaire du
pointeur OldWP) */
.
switch
(Msg.Msg) //traitement classique du message
{
case
WM_ERASEBKGND: // message "efface le fond"
{
HDC Hdc =
(HDC)Msg.WParam; // récupère le HDC du message
SelectPalette(Hdc, Background->
Picture->
Bitmap->
Palette, true
);
/*récupère la palette de l'image que l'on a choisie pour fond*/
RealizePalette(Hdc); // applique la palette
TileBlt(Hdc, Width, Height,
Background->
Canvas->
Handle,
Background->
Picture->
Bitmap->
Width,
Background->
Picture->
Bitmap->
Height); // appelle la fonction "Mosaique de l'image"
Msg.Result =
0
;// renvoie un message nul pour pas que l'ancienne WindowProc "intervienne"
return
;
}
// fin case WM_ERASEBKGND
case
WM_QUERYNEWPALETTE: // message nouvelle pale
{
HDC Hdc =
GetDC(ClientHandle); // récupère le Handle de la zone client
SelectPalette(Hdc, Background->
Picture->
Bitmap->
Palette,true
);/*récupère
la palette de l'image que l'on a choisie pour fond*/
RealizePalette(Hdc);// applique la palette
InvalidateRect(ClientHandle, NULL
, true
); // provoque le rafraichissement de la zone client
ReleaseDC(ClientHandle, Hdc); // relâche le Handle
Msg.Result =
0
;// renvoie un message nul pour pas que l'ancienne WindowProc "intervienne"
return
;
}
//fin du case WM_QUERYNEWPALETTE
case
WM_PALETTECHANGED: // message palette changée
{
if
((HWND)Msg.WParam !=
ClientHandle) /* si le Handle transmis par le message
est différent du Handle de la zone client*/
{
HDC Hdc =
GetDC(ClientHandle); // récupère le Handle de la zone client
SelectPalette(Hdc, Background->
Picture->
Bitmap->
Palette, true
);
/*récupère la palette de l'image choisie pour fond*/
RealizePalette(Hdc); // applique la palette
UpdateColors(Hdc); // rafraichit les couleurs
ReleaseDC(ClientHandle, Hdc); // relâche le Handle de la zone client
}
Msg.Result =
0
; // renvoie un message nul pour pas que l'ancienne WindowProc "intervienne"
return
;
}
//fin du case WM_PALETTECHANGED
case
WM_HSCROLL: // message srcoll horizontal
case
WM_VSCROLL: // message scroll vertical
case
WM_SIZE : //message de changement de taille
{
InvalidateRect(ClientHandle, NULL
, true
); // provoque le rafraichissement de la zone client
break
;
}
// fin du case WM_VSCROLL et WM_VHCROLL
default
:
{
/* Si le message ne rentre dans aucun des cas suivants, on appelle l'ancienne
procédure pour le traitement "classique" des messages*/
Msg.Result =
CallWindowProc(OldClientWP, ClientHandle, Msg.Msg,
Msg.WParam, Msg.LParam);
}
// fin du cas default
}
//fin du switch
}
// fin de la procédure
Enfin nous allons implémenter la fonction qui affiche la mosaïque :
void
__fastcall TMain::
TileBlt(HDC HDestDC,
int
DestWidth,
int
DestHeight,
HDC HSourceDC,
int
SourceWidth,
int
SourceHeight)
{
for
(int
y =
0
; y <
DestHeight; y =
y +
SourceHeight)
for
(int
x =
0
; x <
DestWidth; x =
x +
SourceWidth)
BitBlt(HDestDC, x, y, SourceWidth, SourceHeight,HSourceDC, 0
, 0
,SRCCOPY);
// Copie l'image sur le "fond" de la zone client
}
Voilà tout est là.
Pour ceux qui ne veulent pas d'une mosaïque, mais une image étirée (voire n'importe quoi d'autre…), il faut bien sûr modifier MDIClientWndProc en conséquence.
Par exemple pour l'image étirée voici la partie de code à modifier :
case
WM_ERASEBKGND : // message "efface le fond"
{
HDC Hdc =
(HDC)Msg.WParam;
// Récupère le HDC du message
SelectPalette(Hdc, Background->
Picture->
Bitmap->
Palette, true
);
// Récupère la palette de l'image que l'on a choisie pour fond
RealizePalette(Hdc); // applique la palette
StretchBlt(Hdc, 0
, 0
, Width, Height, Background->
Canvas->
Handle, 0
, 0
,
Background->
Picture->
Bitmap->
Width,Background->
Picture->
Bitmap->
Height, SRCCOPY);
// appelle la fonction "copie en étirant une image"
Msg.Result =
0
;// renvoie un message nul pour pas que l'ancienne WindowProc "intervienne"
return
;
}
Bon courage !
Laurent BERNE