IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

 Création de Fenêtres SDI

Tout ce que l'on devrait savoir

Préambule:

L'objectif içi n'est pas de créer un cas d'école,mais du code réutilisable et modifiable.Les sources d'informations sont le SDK et les articles en C++ de MSDN.Une fois lu,vous pourrez

Créer une feuille mère et ses filles en une minute

Les différentes techniques de création de fenêtres

 Définitions:

On utilise deux points:

Windows utilise la structure suivante pour définir la position des fenêtres

    RECT STRUCT

         left    dd      ?

          top     dd      ?

         right   dd      ?

            bottom  dd      ?

RECT ENDS

Pour définir la position d'une fenêtre windows , la structure RECT (rectangle) définit les points comme ci dessus

L'origine étant en haut à gauche , pour obtenir la hauteur et la largeur en nombre positif , il faut:

 

largeur = RECT.right - RECT.left                   hauteur=RECT.bottom - RECT.top

Deux fonctions renseignent les valeurs de RECT:

GetWindowRect    fournit les coordonnées externes de la fenêtre.Toutes les coordonnées externes démarrent du coin gauche haut de lécran.

GetClientRect     fournit les dimensions internes des fenêtres,donnant l'espace disponible pour les contrôles.Seul right et top (coin droit bas) sont différent de zéro (pour un décalage Nul) et donnent donc directement largeur et hauteur disponible.

Pour obtenir ces informations , il faut
Déclarer une variable à la dimension de RECT:          LOCAL rect:RECT ou rect RECT <>
 appeler la fonction                                             invoke  GetWindowRect,handle,addr rect

Lorsqu'on veut créer ou déplacer des contrôles à l'intérieur d'une fenêtre , les coordonnées deviennent relatives au bord haut gauche de la feuille parente (on n'utilise que le point origine) et deviennent:

CreateWindowEx  pour créer les fenêtres ou les controles
MoveWindow pour déplacer la fenêtre ou les contrôles
                invoke MoveWindow,Handle,X,Y,largeur,hauteur,FALSE

 Les valeurs maximums des axes vont être définis par la résolution de l'écran,exemple 800,600
GetSystemMetrics,SystemParametersInfo permettent de retrouver ces dimensions

 Utilisation:

Pour positionner les contrôles et les fenêtres , il va falloir jongler avec des nombres a 3 chiffres sans aucune signification et surtout ne pas changer de résolution d'écran.L'utilisation de nombres comme 700,200,10,55 devient vite illisible si le nombre de contrôles dépasse le nombre des doights d'une main.Au premier essai , on obtient un tas plus ou moins éparpillé de fenêtres et de contrôles.L'utilisation de quelques variables littérales finit d'éclaircir le problème.

La solution consiste à exprimer les coordonnées en pourcentage de résolution de l'écran

Longueur et hauteur de l'écran deviennent égale à 100 , les dimensions à la visualisation restent inchangés quelque soit la résolution de l'écran , les calculs deviennent à 2 chiffres.Il est facile de mémoriser quelques valeurs de dimensions.Un gros bouton fait 9 de haut , une marge 2 ...

 Pratique:

Télécharger l'exemple

La transformation en pourcentage doit s'accomplir avec une certaine précision et l'utilisation du coprocesseur mathématique (FPU Floating Point Unit) s'impose.La fonction GetPercent fournie avec la librairie de masm32 semble résoudre le problème.Hélas , son auteur à oublier que le FPU est sujet au débordement de pile (elle ne peut contenir que 8 nombres flottants st(0) à st(7)) et au bout de 2 ou 3 calculs , ne répond plus que 0.Voici sa version corrigée.

Démarrer l'éxécution du programme fenêtre sous plusieurs résolutions , vous pourrez constater qu'il conserve les mêmes dimensions.Télécharger l'exemple

PROBLEMES d'apparition et de disparition de fenêtres:

Disparition complète:

On peut facilement voire disparaitre complétement une fenêtre de l'écran , en utilisant incorrectement les styles,en voici un exemple:

        ;==================================================

        ; rempli la structure WNDCLASSEX

        ;==================================================

 

 

CS_BYTEALIGNWINDOW est le style en trop , et la fenêtre disparait .Rien n'indique dans winhelp la possibilité d'obtenir un tel résultat:

CS_BYTEALIGNWINDOW      Aligns a window on a byte boundary (in the x direction) to enhance performance during operations that involve moving or sizing the window. This style affects the width of the window and its horizontal position on the display.

Fenêtre aux contours Flous ,palissant ,s'évanouissant.

Un controle d'édition peut avoir pour parente des tab,ayant pour parente la feuille mére

Lorsque qu'une feuille parente rafraichi son affichage à l'aide de l'évènement PAINT elle rafraichi en même temps ses filles.C'est ce qui occasionne les problèmes d'effacement lorsque la fille utilise son propre évènement paint.La propriété WS_CLIPCHILDREN permet d'empécher la parente de rafraichir en même temps la fille.Encore faut-il appliquer la propriété à toutes les parentes de la feuille fille

Dans le cas qui nous occupe:

La fenêtre principale doit posséder la propriété WS_CLIPCHILDREN
Le contrôle tab doit posséder la propriété WS_CLIPCHILDREN
Le controle d'édition doit posséder la propriété WS_CLIPCHILDREN

 Les différentes techniques de création de fenêtres

Windows fournit:

                                les SDI (Simple Document Interface)
                                Les MDI(Mutiple Document Interface)
                                Les boites de dialogues dans lesquelles il faut distinguer
                                                            les modales (bloquant le déroulement du programme principal)
                                                            les modeless (le programme appelant continue )
                                dans lesquelles il faut distinguer celles dont la forme est contenu dans un fichier de ressource et celles dont la forme est créer en mémoire(masm32 fournit des macros pour les créer)

faire un choix ?:

A chaque type de fenêtre correspond une boucle de programme avec ces propres fonctions de terminaison et ces propres règles d'écriture et pour les boites de dialogue ces propres règles d'accès a la mémoire ,pas toujours explicite.De plus certains styles et styles étendues ne s'appliquent qu'a un certain type de fenêtres.On peut facilement mélanger les styles ensembles et obtenir des fenêtres bloquées,comme dans l'exemple ci-dessus.

La MDI est dédié aux documents possédant plusieurs fenêtres et semble être la forme la plus adaptable a l'évolution du projet,la création de la feuille de départ est différente de la création des feuilles filles

La SDI n'a au départ qu'une seule fenêtre.Elle peut créer autant de filles qu'elle veut en utilisant toujours la même technique . Les feuilles filles possèdent les mêmes propriétés que la feuille mêre.C'est à dire qu'elle peut remplacer la MDI sans problème.Une feuille fille peut servir aussi de boite de dialogue,il suffit d'utiliser CreateWindowEx

Les boites de dialogues dont la forme est défini dans un fichier de ressource présentent l'inconvénient d'avoir une taille fixe en fonction de la résolution d'écran,mais sont simples a écrire et on peut les rendre modifiables en taille.Celles dont la forme est écrite en mémoire sont assez peu utilisées et restes complexes à écrire malgré les macros fournit par masm32 qu'il faut étudier avant d'en créer une.La mémoire réservée dynamiquement par la mère, ne peut pas etre utilisée par la boite

Ma préférence va a la SDI.Je vous livre içi les deux choses à savoir pour qu'elles fonctionnent sans problèmes.
Chaque fille doit avoir son propre nom de classe (et non pas celui de la mère) déclaré avec la fonction RegisterClassEx,le nom de classe est uniquement déterminé par votre imagination.

Les feuilles parentes doivent toutes avoir la propriété WS_CLIPCHILDREN si vous ne voulez pas voir vos feuilles filles s'évanouir(problème de peinture).Pourquoi une telle préférence ?

Avec une instruction (createwindowex) et un type de boucle ,on peut créer tout ce qu'on veut

Créer une fenêtre et des fenêtres filles en une minute ?

L'exemple ci dessous est conçu pour être reproductible et modifiable.Recopier le texte tel que dans le source et changer toutes les occurences de SDI par le nom de votre choix.Placer déclarations et data aux bons endroits.Vous obtiendrez des fenêtres vides  a remplir.

Les styles choisies tiennent compte des nécessités de OLE et évitent les problèmes de peinture.Ne les changer pas sans savoir pourquoi.Les dimensions sont en pourcentage de l'écran ou de la feuille mère.Si Hmere=NULL ,on crée une mère,passer le handle de la mère pour créer une fille.Fonction particulière SystemParametersInfo au lieu de getSystemMetric,permet de tenir compte de la barre des taches plus d'autres choses.La boucle d'attente tient compte des recommandations du sdk (en cas de handle invalide) et diffère des autres asm habituelles qui sont toutes ....inprécises.On peut mettre la boucle d'attente directement après le ou les   CreerSDI ce qui suprime winmain,parfaitement inutile.Après la boucle exitprocess et c'est tout.

Si au premier essai la fille est invisible,vous avez oublié de modifier le texte de la classe fille

Remplacer SDI par un nom générique , en utilisant le menu d'édition,a partir d'ici

 

;___________________________________________________________________________________________________________

;declare

CreerSDI PROTO  :DWORD,  :DWORD,  :DWORD,  :DWORD,  :DWORD,  :DWORD

PourCent PROTO  :DWORD,  :DWORD

;data

msg MSG <>

HwndSDI dd 0

Hinstance dd 0

;code

mov   Hinstance,FUNC(GetModuleHandle, NULL) ;FUNC retourne eax

invoke CreerSDI,Hinstance,NULL,0,0,75,75

 

;################################################################

CreerSDI PROC  ModHandle:DWORD,  Hmere:DWORD,  Xsdi:DWORD,  Ysdi:DWORD,  largeurSdi:DWORD,  HauteurSdi:DWORD

                        LOCAL wc:WNDCLASSEX

                        Local ecran:DWORD

                        Local hauteur:DWORD

                        Local largeur:DWORD

                        Local rect:RECT

                        Local Xpos:DWORD

                        Local Ypos:DWORD

                        Local Hcreateicon:DWORD

                        Local  retour:DWORD

                        ;Local   :DWORD

                        ;Local    [MAX_PATH +1]:BYTE

        mov retour,1

        ;----------------- vérifiez les paramètres ------------------------

        ;--- içi mieux vaut appeler la fonction en dehors du proc et placer le handle dans Hinst

        .if ModHandle == 0

                mov   ModHandle,FUNC(GetModuleHandle, NULL) ;FUNC retourne eax

        .endif

        

        jmp PardessusTexte

                classSDI db "TestFenetre",0                             ;doit etre unique pour chaque feuille

                WindowNameSDI db "exemple de titre",0                  ;changer le c'est mieux

                ;MAINMENU MENUEX MOVEABLE IMPURE LOADONCALL DISCARDABLE ; dans rc

                MenuSDI   db "MAINMENU",0

        PardessusTexte:

        ;-------------- remplir la definition de classe ---------------------

        mov wc.cbSize,sizeof WNDCLASSEX

        mov wc.style,CS_BYTEALIGNCLIENT or \                    ;a conserver de préférence

                                CS_BYTEALIGNWINDOW

        PuPo wc.lpfnWndProc,    WndSDI                          ;sera renommé en remplaçant SDI

        mov wc.cbClsExtra,     NULL                                     ;extra bytes , utilisation très spéciale

        mov wc.cbWndExtra,     NULL     

        PuPo wc.hInstance,  ModHandle

        ;500 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "MAINICON.ICO" ; dans le .rc     

        mov Hcreateicon,FUNC(LoadIcon,ModHandle, 500)           ;changer le 500 si autre icon dans .rc

        PuPo wc.hIcon,  Hcreateicon       

        PuPo wc.hCursor, FUNC(LoadCursor, NULL, IDC_ARROW)      ;pointeur standard

        PuPo wc.hbrBackground,  COLOR_BTNFACE+1                 ;couleur de fond standard

        .if Hmere == 0          ;fille pas de menu

                mov wc.lpszMenuName, offset MenuSDI                     ;peut etre NULL

        .else

                mov wc.lpszMenuName,NULL

        .endif

        

        mov wc.lpszClassName, offset classSDI

        PuPo wc.hIconSm,       Hcreateicon                              ;petit icon

 

        invoke RegisterClassEx, ADDR wc

        ;renvoie un atom contenant le nom de classe créée,perdu içi

        

        ;----------- dimensionner la fenêtre,les dimensions passés a l'appel sont en % -------------

        .if Hmere == NULL                                               ;on crée la feuille mère

                invoke  SystemParametersInfo,SPI_GETWORKAREA,NULL,addr ecran,NULL

                mov hauteur,HAUTEUR (ecran)

                mov largeur,LARGEUR (ecran)

                invoke   PourCent,hauteur,HauteurSdi

                mov hauteur,eax

                invoke   PourCent,largeur,largeurSdi

                mov largeur,eax

                ;------- on centre la feuille

                mov edx,HAUTEUR (ecran)

                sub edx,hauteur

                shr edx,1                                       ;divise par deux

                mov Ypos,edx

                mov edx,LARGEUR (ecran)         

                sub edx,largeur

                shr edx,1

                mov Xpos,edx

                                                

        .else                                                           ;on crée une fille

                invoke   GetClientRect,Hmere,addr rect                                          

                mov hauteur,HAUTEUR (rect)

                mov largeur,LARGEUR (rect)

                invoke   PourCent,hauteur,HauteurSdi

                mov hauteur,eax

                invoke   PourCent,largeur,largeurSdi

                mov largeur,eax

                ;calcul des bords

                .if Xsdi != 0

                        invoke   PourCent,largeur,Xsdi

                        mov Xpos,eax

                .else

                        mov Xpos,0

                .endif

                

                .if Ysdi != 0

                        invoke   PourCent,hauteur,Ysdi

                        mov Ypos,eax

                .else

                        mov Ypos,0

                .endif

        .endif

                

        ;si la feuille contient plusieurs controles ou feuilles,poursuivre le calcul

        ;------- creation de la fenetre,

        .if Hmere == NULL

                ;--- WS_CLIPCHILDREN évite que la feuille mère repeigne la fille

                ;------- voir aussi WS_CLIPSIBLINGS style,flickering

                INVOKE     CreateWindowEx,WS_EX_LEFT ,ADDR classSDI,ADDR WindowNameSDI,

                                WS_OVERLAPPEDWINDOW or WS_CLIPCHILDREN ,\

                                Xpos,Ypos,largeur,hauteur, NULL, NULL,ModHandle, NULL   

        .else

                ; WS_POPUP incompatible avec WS_CHILD

                invoke CreateWindowEx,WS_EX_LEFT ,      ADDR classSDI,ADDR WindowNameSDI,

                                        WS_CHILD or WS_OVERLAPPEDWINDOW ,

                                        Xpos,Ypos,largeur,hauteur,

                                        Hmere,NULL,ModHandle,NULL

        .endif  

                ;handle perdu,initialiser par wndSDI            

        ;---------------- visualiser la fenetre ----------------------- ou ajouter WS_VISIBLE

        INVOKE     ShowWindow,HwndSDI, SW_SHOWNORMAL            

        INVOKE     UpdateWindow,HwndSDI                 

 

FindeCreerSDI:

        mov eax,retour

        ret

CreerSDI endp

 

;################################################################

 

WndSDI proc  uses ebx esi edi  hwnd:DWORD, uMsg, wParam, lParam

                        Local hauteur:DWORD,largeur:DWORD,fwSizeType:DWORD

                        Local rect:RECT

                        Local Xpos:DWORD,Ypos:DWORD

                        Local  retour:DWORD

        PuPo HwndSDI,hwnd               ;XP accepte uniquement celui la

        

        .if uMsg == WM_CREATE

                mov ebx,lParam

                ;mov     eax, (CREATESTRUCT  ptr [ebx]).hwnd

                ;mov     edx,(CREATESTRUCT  ptr [ebx]).hMenu

                ;suite  

                mov retour,0

                

        .elseif uMsg == WM_COMMAND

                HIWORD wParam           ;evenements des listbox ,combo sont ici

                mov edx,eax             ;exemple :TCN_SELCHANGE

                LOWORD wParam           ;IDentificateurs de  commandes eax

                mov ebx,lParam  ; handle du controle

                ;suite  

        

                mov retour,0

        .elseif uMsg == WM_SIZE

                mov eax, wParam         ;resizing flag

                mov fwSizeType,eax

                LOWORD lParam                   ;largeur fenetre

                mov largeur,eax

                HIWORD lParam           ;height of client area

                mov hauteur,eax

                

 

                mov retour,0

        .ELSEIF uMsg == WM_CLOSE ;return zero

                INVOKE     DestroyWindow, hwnd

                mov retour,0            

        .ELSEIF uMsg == WM_DESTROY

                INVOKE     PostQuitMessage, NULL

                mov retour,0

        .else

                INVOKE     DefWindowProc, hwnd, uMsg, wParam, lParam            

                mov retour,eax          

        .endif

        mov eax,retour

 

        ;pushad

        ;invoke EspionneMessages,hwnd,uMsg,wParam,lParam,sortie 

        ;popad

        ret

WndSDI endp

 

;--------------------- boucle d'attente --------------------------------------------

        @@:

        INVOKE     GetMessage, addr msg, NULL, 0, 0

        .if eax != 0         ;le message est WM_QUIT                    ; quitter sur commande quit

                .if eax != -1                                                   ;handle invalid,crash

                        INVOKE     TranslateMessage, addr msg           ;prend le message

                        INVOKE     DispatchMessageA , addr msg          ;envoi les messages aux fenêtres

                .endif

                jmp @B

        .endif

        mov     eax, msg.wParam

 

;################################################################

PourCent proc source:DWORD, percent:DWORD

;        invoke   PourCent,Xresolution,80         ;80 % de Xresolution

        LOCAL var1:DWORD

 

        mov var1, 100   ; to divide by 100

        FINIT             ; l'instruction qui manque dans lib masm32

        fild source     ; load source integer

        fild var1       ; load 100

        fdiv            ; divide source by 100

        fild percent    ; load required percentage

        fmul            ; multiply 1% by required percentage

        fist var1       ; store result in variable

        mov eax, var1

 

        ret

PourCent endp

; #####################

 

Copyright © 2005 Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.