c ++ - Oprettelse af en professionel udseende (og opfører!) form designer

Indlæg af Hanne Mølgaard Plasc

Problem



Da jeg begyndte at programmere (for nogle 10 + år siden), overraskede tre ting mig:



  • Kompilatorer/tolke (da kendte jeg dem som 'programmer, der gør mine programmer til at arbejde', ofte efterfulgt af kvalifikationen 'uanset hvad de er')

  • Kodeditorer

  • Form designere



Dengang accepterede jeg dem alle som fakta om livet. Jeg var i stand til at lave mine egne specialprogrammer, men 'programmer, der gjorde mine programmer', kodeditorer og formredaktører blev lavet af guderne, og der var ingen måde, jeg kunne rodde med dem.


Så gik jeg på universitetet og tog et kursus på formel sprogbehandling. Efter at have lært formelle grammatikker, parsere, abstrakte syntaks træer mv .; al magien om kompilatorer, tolke og kode redaktører var snart væk. Kompilatorer og tolke kunne skrives på kloge og enkle måder, og den eneste ikke-sane ting, som en syntax fremhævende kode editor kunne kræve, var Windows API hacks.


Men til denne dag forbliver form editorer et mysterium for mig. Enten mangler jeg den tekniske viden, der kræves for at lave en form designer, eller jeg har sådan viden, men kan ikke finde en måde at bruge den til at implementere en form designer.


Ved hjælp af Visual C ++ og MFC'en vil jeg gerne implementere en formular designer inspireret af den bedste form designer nogensinde:


Visual Basic 6s form designer


Navnlig vil jeg gerne efterligne de to funktioner, som jeg kan lide mest:



  • Den form, der er udformet, er inde i en beholder. Således kan en vilkårligt stor form udformes uden at spilde for meget skærmfast ejendom ved blot at ændre beholderen til en passende størrelse.

  • Alternativet 'Juster til gitter' gør det meget mindre at designe professionelt udseende brugergrænseflader
    frustrerende. Faktisk ville jeg gå så langt som at sige at skabe professionelle brugergrænseflader ved hjælp af Visual Basic 's form designer er faktisk nemt, sjovt og sjovt. Selv for venstrehårede programmerere som mig.



Så jeg har følgende spørgsmål:



  1. Hvordan laver jeg en form designer, hvor formularen er designet er inde i en container? Er formularen designet til et faktisk vindue indeholdt i et andet vindue? Eller er det bare en mockup 'manuelt' malet af form designer?

  2. Kan Windows API og/eller MFC'en indeholde funktioner, klasser, uanset hvad der gør det nemt at oprette 'valgbare' elementer (omgivet af små hvide eller blå bokse, når de vælges, kan ændres, når de 'greb' af en af disse 'kanter')?

  3. Hvordan implementerer jeg funktionen 'Juster til gitter'?


Bedste reference


Både svarene her er gode, men udeladt, hvad jeg anser for at være de virkelig interessante bits (herunder et par, som du ikke spørger direkte, men det kan du nok finde af interesse), så her er min 2c:


Tegning af betjeningselementerne



Ideelt set går du bare videre og skaber en regelmæssig forekomst af kontrollen. Vil du have noget, der ligner en knap? Opret en rigtig knap. Den vanskelige ting er at stoppe den fra at opføre sig som en knap: du vil have klik for at aktivere den til at flytte, ikke faktisk 'klikke' den.


En måde at håndtere dette på - forudsat at de pågældende betjeninger er 'HWND-baserede' (f.eks. Standardvinduesæt med knap, rediger, statisk, listeboks, treeview osv.) - er at skabe kontrol og derefter underklasse det - dvs. overstyr wndproc'en med SetWindowLongPtr (GWLP\_WNDPROC, ...), således at designerkoden kan aflytte mus og tastaturindgang og bruge den til at starte et træk, for eksempel i stedet for at få indtastningen af ​​musen igennem til den aktuelle knapkode, hvilket vil i stedet fortolke det som en 'klik' begivenhed.


En alternativ tilgang til subclassing er at placere et usynligt vindue over knappen for at fange input. Samme idé om at opfange input, bare en anden implementering.


Ovenstående gælder for både managed (VB.Net, C #) og unmanaged (C/C ++) kontroller; de 'er begge hovedsagelig lagervinduer HWNDs; de administrerede versioner har kun en administreret wrapper kode, der afgiver den underliggende ukontrollerede kontrol.


De gamle (pre-managed code) ActiveX-kontroller, som bruges i pre- .Net VB, var et helt andet boldspil. Der er et ret komplekst forhold mellem en ActiveX-beholder og ActiveX-kontrollerne i den, med mange COM-grænseflader, der håndterer ting som forhandling af egenskaber, arrangementer, maleri osv. (Der er et sæt grænseflader, der muliggør en ActiveX-kontrol at modtage input og tegne sig uden at have sin egen HWND.) En fordel, du får fra denne kompleksitet, er imidlertid, at ActiveX-kontroller har en eksplicit 'design mode'; så en kontrol ved at reagere hensigtsmæssigt i den sag og kan samarbejde med hele proceduren.


Formularen selv ...



Så grundlæggende er kontrollerne bare regulære kontroller. Så du forventer, at selve formularen er en regelmæssig form? - Næsten. Så vidt jeg ved, er det bare et andet HWND-baseret vindue, det er et designerens barn (så det bliver klippet og kan rulles ind i det ); men jeg tror, ​​at designeren gør en smule 'snyd' her, for det er normalt, at Windows kun rammer rammer som - med titellinje og min/max knapper som til faktiske topniveau vinduer. Jeg kender ikke den præcise teknik, som de bruger her, men nogle muligheder kan omfatte: male det manuelt for at efterligne Windows-udseendet; ved hjælp af Windows 'tema' API'erne, som giver dig adgang til de grafiske elementer, der bruges til bits og stykker af titelbjælker og male dem, hvor du vil; eller måske mindre sandsynligt at sætte vinduet op som et 'MDI Child Window' - dette er et undtagelsestilfælde, hvor vinduer vil tegne en ramme omkring et indlejret vindue.


Trækbare håndtag



Den enkleste tilgang her er for designeren at oprette otte små firkantede titellinjer-popup-vinduer, der sidder over alle de andre elementer - som starter den passende resize-kode, når de klikkes. Når brugeren klikker fra kontrol for at styre, skal du blot flytte trækhåndtagets vinduer til den aktuelt aktive kontrol. (Bemærk at i alt det ovenstående Windows selv er at finde ud af, hvem der er blevet klikket, behøver du aldrig at sammenligne musekoordinater mod elementrektangelkoordinater og udarbejde det selv.)


Saving &Recreating



For almindelig windows systemkontrol, der bruges af ustyret C/C ++, er det relativt nemt: der er et velkendt tekstbaseret filformat - .rc - der beskriver kontrollerne og placeringerne. Har designeren spyttet det (og sandsynligvis en resource.h-fil også), og du er færdig: Et hvilket som helst C/C ++-projekt kan hente disse filer og kompilere dem. Administreret kode (C #, VB.Net) har noget mere komplekse ordning, men det er stadig den samme grundlæggende idé: skriv en beskrivelse i den stil, som de administrerede værktøjer forventer, og de 'vil sammen kompilere det og bruge det.


(ActiveX-kontroller er - du har gættet det - en hel 'noterhistorie. Der er ikke et standardformat, som jeg er opmærksom på, så form editoren og runtime, der forbruger dataene, ville være tæt knyttet sammen - fx formularen redaktør fra pre-. Net VB6 producerer former, som kun VB kan bruge. - Jeg tror. Det har været for nogen tid siden ...)


Hvad angår genskabelse af formularen: Hvis du har en .rc-fil, bliver den samlet i en dialogressource, har Windows indbygget support til at genskabe dem. På samme måde kan de understøttede biblioteker i administreret kode vide, hvordan man genskaber en formular fra sit bestemte format. Begge grundlæggende analysere beskrivelsen, og for hvert element opretter du elementer i de relevante klasser og indstiller passende stil, tekst og andre egenskaber som angivet. Det gør ikke noget, du kan ikke gøre selv, det er bare hjælperværktøjskoden.


Håndtering Fokus



For en samling af HWND'er i enhver container, uanset om du lader Windows eller Winforms håndtere formoprettelsen, eller om du selv har oprettet hver HWNDs, kan du tilføje tabbing support ved at bruge 'test' mode eller rent faktisk i den rigtige app. kalder IsDialogMessage i din meddelelsessløjfe: Se afsnittet om MSDN-sideanmærkninger for detaljer. (Mens WinForms kunne gøre dette, tror jeg , det gør faktisk sin egen fokushåndtering, så den kan have tabordrække uafhængig af visuel stabling af Z-Order.)


Andre ting at udforske ...



Få venner med Spy ++ appen (del af SDK, installerer med Visual Studio). Hvis du vil gøre noget med HWND'er, administreret eller uhåndteret, er det en rigtig god ide at vide, hvordan du bruger dette værktøj: Du kan pege på det på et hvilket som helst brugergrænseflade på Windows og se, hvordan det er bygget ud af et træ af forskellige typer HWNDs. Peg det på VB-designer og se, hvad der virkelig sker for dig selv. (Klik på ikonet 'kikkert' på værktøjslinjen, og træk så krydset i vinduet du er interesseret i.)


Se også på de ressourcefiler, som designeren spytter ud. Alt, hvad du kan tilpasse eller flytte eller redigere i formulardesigneren, svarer til noget element et eller andet sted i en af ​​disse ressourcefiler. Lav en kopi af dem, tweak nogle indstillinger, og sammenlign derefter de to sæt, og se hvad der er ændret. Prøv at ændre nogle ting i filerne manuelt (jeg tror, ​​at de er næsten alle tekst), genindlæs og se hvis designeren hentede dine ændringer.


Andre ting at notere ...


Meget af det ovenstående er specifikt for Windows - især fordi vi bruger Window's egne byggesten - HWNDs - vi kan få Windows til at gøre noget af det hårde arbejde for os: det giver os mulighed for at genbruge styrer sig selv på design tid, så vi behøver ikke at trække mock-ups, at aflytte input på andre kontroller, så vi kan få et klik i et træk eller hvad som helst anden handling, vi ønsker, eller finde ud af hvilken kontrol der klikkes uden at skulle gøre Placeringen matte os selv. Hvis dette var en designer til nogle andre UI-rammer - sig Flash - som ikke bruger HWND'er internt, ville det sandsynligvis i stedet bruge at rammernes egne interne faciliteter til at gøre lignende arbejde .


Det er også meget lettere, hvis du begrænser antallet af kontroller i paletten til et lille finitivt sæt, i hvert fald i første omgang. Hvis du vil tillade, at nogen kontrol overhovedet trækkes ind - f.eks. En tredjepart, eller en du har brugt i et andet projekt du har typisk først brug for en eller anden måde for at kontrollen er 'registreret', så designeren ved, at den er tilgængelig i første omgang. Og det kan også være nødvendigt at finde ud af, hvilket ikon det bruger i værktøjslinjen, hvad det hedder , hvilke egenskaber det understøtter - og så videre.


Hav det sjovt at udforske!

Andre referencer 1


Du implementerer en formedesigner næsten som en normal GUI. Du har ting du kan trække (dine widgets), du har ting du kan klikke på (dine knapper), og du har ting du kan vælge (dine placerede widgets), og det handler virkelig om det.





Q: Nu viser du et vindue i et GUI?

A: Du maler det, simpelt som det.


Q: Og hvordan holder du ting inde i vinduet?

A: Du kontrollerer grænserne for 'overordnet' objektet. Du kan næsten sige, at en form designer er som et lille spil, og du har en scene graf, der indeholder alle dine widgets, der er forbundet med forældre-barn-relationer.


Q: Så, hvordan vælger du ting i et GUI?

A: Kontroller den aktuelle musposition på-klik mod grænserne for alle (nær) widgets (en scenediagram hjælper kun her som en quadtree).


Q: Hvordan justerer du widgets på et gitter?

A: Til netværksjustering, lad os have et simpelt eksempel: Sig din reelle opløsning er 100px på x-aksen, men du vil have dit net til kun at have en opløsning på 10px på x. Nu siger du, at du flytter din widget med 28px i reel opløsning. For at få gridopløsningen deler du blot ved 10, får 2.8, rundt det og endelig flytter widgeten 3 fliser på x. Afrundingen er nøglen her. Kun hvis netbevægelsen er >= ?.5, klikker du til næste flise. Ellers forbliver du simpelthen på den gamle.





Håber dette kan give dig et generelt tip om, hvordan du starter en form designer. Hav det sjovt. :)

(PS: Ved ikke om specifikke WinAPI/MFC funktioner/klassen til at hjælpe dig med, undskyld.)

Andre referencer 2


Bare for at tilføje et punkt eller to til hvad @Xeo allerede har sagt:


Først og fremmest, nej, du don't altid tegner alt indhold selv. Under normal designfase er du stort set bare ved at tegne noget, der ligner kontrollen, men (i det mindste IIRC) lader det også du 'kører' en form i testtilstand (bestemt gør VC ++ dialogdesigneren det, og selv om VB'er var mere primitive tror jeg det også havde den særlige evne). Testtilstand var, når du kan 'køre' en formular før du (nødvendigvis) vedhæfter nogen kode til den - selvom klik på en knap (for eksempel) ikke gør noget i det omgivende program, fungerer kontrollen selv som normalt - en knap klik normalt, en redigeringskontrol vil lad dig redigere osv.


Det gøres ved at instansere en kontrol og fortælle den korrekte position, størrelse og egenskaber. ActiveX-kontroller gør en hel del for at understøtte dette, og de tidligere 'Windows-brugerdefinerede kontroller' gjorde det også, men med betydeligt mindre sofistikering. Fra kontrolsynspunktet virker det næsten lige præcis som det normalt ville have, modtaget input, sende besked til sin forælder osv. Det eneste, der er ændret, er, at forældren mest ignorerer de fleste af de meddelelser, den modtager, men kontrollen ved det ikke rigtigt.


Der er to grundlæggende måder at gøre dette på. Den ene er at oprette en samling af kontroller selv sammen med deres positioner, størrelser mv og bruge CreateWindow (eller CreateWindowEx osv.) For at oprette vinduet i den rigtige klasse. Selvom det er relativt nemt at håndtere, har dette den ulempe, at det efterlader hele tabbehandlingen til dig.


Den anden mulighed er at oprette en DLGTEMPLATE struktur for at holde data om dialogboksen og nogle DLGITEMTEMPLATES for de enkelte kontroller og endelig bruge CreateDialogIndirect til at oprette en dialogboks med disse specs , holde disse kontroller. Det er kedeligt, men det virker, og når du er færdig håndterer det tabbing mellem kontroller automatisk (og fungerer det samme som enhver anden dialog, da det er den samme Windows-kode, der opretter det på en måde).


For det andet, da du har tagget denne C ++, vil du måske kigge på nogle kode på CodeProject, der rent faktisk implementerer en dialogredaktor. Selv om det ikke er så avanceret som nogle af de kommercielle dem, er dette en rimeligt komplet form/dialog editor, komplet med det meste af hvad du har spurgt om. [13]