Tutoriel pour afficher une image dans le fond d'une application MDI

Tout ceux qui à présent ont fait des applications MDI se sont rendus compte de la difficulté à mettre une image dans le fond.

Article lu   fois.

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 sur 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.

 
Sélectionnez

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 :

 
Sélectionnez

void __fastcall TileBlt(HDC HDestDC,int DestWidth,int DestHeight,HDC HSourceDC,int SourceWidth,int SourceHeight);

Et enfin la nouvelle procédure de fenêtre :

 
Sélectionnez

void __fastcall MDIClientWndProc(TMessage &Msg);

Le fichier Main.h doit contenir ceci :

 
Sélectionnez

#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 :

 
Sélectionnez

__fastcall TMain::TMain(TComponent* Owner): TForm(Owner)
{
    // D'abord pour la nouvelle procédure de fenêre 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 :

 
Sélectionnez

void __fastcall TMain::MDIClientWndProc(TMessage Msg)
{
    /* Rappel pour ceux qui ne sont pas familier 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 plutot WndProc) sert à interpreter les message que Windows
    envoie à l'application. Ces messages sont ici contenu 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 choisi 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 raffraichissement de la zone client
                    ReleaseDC(ClientHandle, Hdc); // relache 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); // raffraichit les couleurs
                            ReleaseDC(ClientHandle, Hdc); // relache 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 raffraichissement de la zone client
                    break;
                }// fin du case WM_VSCROLL et WM_VHCROLL

                default : 
                {
                /* Si le message ne rentre dans aucun des cas suivant, 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 mosaique :

 
Sélectionnez

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 mosaique mais une image étirée (voire n'importe quoi d'autre...), il faut bien sur modifier MDIClientWndProc en conséquence.

Par exemple pour l'image étirée voici la partie de code à modifier :

 
Sélectionnez

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 choisi 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

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2002 Laurent Berne. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.