Technical Article

Format TDateTimePicker-a u Delphiju 5: DateTime_SetFormat

TDateTimePicker je dobio svojstvo Format negdje između verzije Delphi 5 i Delphi 6. Točna granica verzije rijetko je dokumentirana na pouzdan način, a kada na nju naiđete, prevoditelj vam jednostavno javi da identifikator ne postoji. Linija koja se prevodi i radi na svakom novijem Delphiju koji posjedujete, tiho ili potpuno zakazuje na starom, ovisno o tome kako je projekt strukturiran.

Temeljna kontrola je Windows zajednička kontrola DATETIMEPICK_CLASS iz knjižnice comctl32.dll, i ona je oduvijek prihvaćala niz za oblikovanje putem poruke DTM_SETFORMAT. Delphijev VCL u novijim verzijama omotava tu poruku u svojstvo Format. U Delphiju 5 tu poruku šaljete sami putem makro omotača DateTime_SetFormat deklariranog u jedinici CommCtrl.

Uzorak uvjetnog prevođenja (conditional compilation)

Standardni pristup je blok s uvjetnim prevođenjem na temelju simbola verzije. Delphi 5 definira VER130; možete testirati izravno taj simbol ili definirati vlastiti simbol D5 u opcijama projekta kako bi naziv grane bio čitljiviji u cijeloj bazi koda. U svakom slučaju, grana za Delphi 5 poziva DateTime_SetFormat s handleom kontrole, dok sve ostale grane dodjeljuju vrijednost svojstvu:

{$IFDEF D5}
  DateTime_SetFormat(DateTimePicker1.Handle, PChar('MM/dd/yyyy'));
{$ELSE}
  DateTimePicker1.Format := 'MM/dd/yyyy';
{$ENDIF}

Dodajte CommCtrl u klauzulu uses za jedinicu koja sadrži granu za Delphi 5. Nema potrebe dodavati je bezuvjetno; ako više volite zadržati ovisnost unutar određenog opsega, omotajte unos uses u isti uvjet:

uses
  ...,
{$IFDEF D5}
  CommCtrl,
{$ENDIF}
  ...;

Sintaksa niza za oblikovanje (format string)

Niz za oblikovanje proslijeđen funkciji DateTime_SetFormat prati format Windows birača datuma i vremena, a ne tokene Delphijeve funkcije FormatDateTime. Ova dva formata izgledaju slično, ali nisu međusobno zamjenjivi. Windows koristi d za dan u mjesecu (bez vodeće nule), dd za dan s vodećom nulom, M za broj mjeseca, MM za mjesec s vodećom nulom, yy za dvoznamenkastu godinu i yyyy za četveroznamenkastu godinu. Vremenska polja koriste h/hh za 12-satni prikaz, H/HH za 24-satni prikaz, m/mm za minute, s/ss za sekunde. Doslovni tekst stavlja se unutar jednostrukih navodnika unutar niza za oblikovanje, tako da je 'dd/MM/yyyy' već ispravno, ali bi 'dd '<literal>' MM' zahtijevalo ugniježđene parove jednostrukih navodnika.

Nasuprot tome, Delphijeva funkcija FormatDateTime koristi d/dd/ddd/dddd za oblike dana s različitom semantikom, te m za mjesec (a ne M). Ako kopirate niz za oblikovanje iz poziva FormatDateTime i predate ga izravno funkciji DateTime_SetFormat, polja mjeseca i minuta vjerojatno će biti zamijenjena na izlazu. Zapišite niz za oblikovanje kontrole ispočetka prema Windows dokumentaciji, radije nego da ga pretvarate iz Delphi formata.

Regionalne postavke i globalni ShortDateFormat

Promjena Delphijeve globalne varijable ShortDateFormat ne utječe na TDateTimePicker koji je već stvoren. Birač čita regionalne postavke iz Windowsa u trenutku stvaranja i nakon toga se iscrtava prema porukama koje prima, a ne prema globalnim varijablama Delphi RTL-a. To znači da postavljanje ShortDateFormat u proceduri FormCreate neće promijeniti ono što kontrola prikazuje. Trebate DateTime_SetFormat (ili svojstvo Format na novijem Delphiju) za nadjačavanje vlastitog niza oblikovanja kontrole nakon što handle postoji.

Za aplikacije koje se distribuiraju na međunarodnom tržištu, testirajte niz za oblikovanje na računalu s regionalnim postavkama koje nisu za SAD. Windows može nadjačati prazan ili nil niz za oblikovanje sa zadanim postavkama lokaliteta, ali izričiti niz koji nije nil u potpunosti zamjenjuje regionalnu postavku. To je obično ono što želite, ali to znači da će uzorak usmjeren na SAD, poput 'MM/dd/yyyy', izgledati upravo tako za korisnike koji po konvenciji očekuju dd/MM/yyyy. Ako je birač datuma namijenjen korisniku, a ne čisto polje za unos podataka, razmislite o tome da dopustite zadane postavke kontrole (prosljeđivanjem vrijednosti nil u DateTime_SetFormat) kako bi automatski pratio lokalitet korisnika.

Ponovno stvaranje handlea (Handle recreation)

Postavka formata pripada prozorskom handleu, a ne nekom polju VCL objekta. Ako se handle kontrole uništi i ponovno stvori, što VCL može učiniti kada promijenite roditelja kontrole (reparenting) ili promijenite određena svojstva stila u vrijeme izvođenja, format se vraća na zadane Windows postavke. Sigurno mjesto za primjenu DateTime_SetFormat je u rukovatelju koji se izvodi nakon stvaranja handlea: nadjačajte CreateWnd u klasi potomka i pozovite ga tamo, ili ga primijenite u događaju OnEnter ili OnShow tek nakon potvrde da je handle aktivan. Jednokratno postavljanje u FormCreate često je dovoljno za jednostavne forme, ali nije pouzdano ako forma vrši dinamičku promjenu stilova ili promjenu roditelja kontrola.

Isto ograničenje vrijedi i za VCL svojstvo Format u novijim verzijama Delphija; interno poziva istu Windows poruku i suočava se s istim životnim ciklusom handlea. Razlika je u tome što postavljač svojstva VCL provjerava je li handle dodijeljen prije pozivanja, te automatski ponovno primjenjuje format u CreateWnd. Sirovi poziv DateTime_SetFormat nema takvu zaštitu, pa grana za Delphi 5 mora biti pažljivija o tome kada se izvodi.

Uklanjanje zaobilaznog rješenja

Kada projekt odbaci podršku za Delphi 5, uvjetni blok postaje suvišan teret. Uklonite granu {$IFDEF D5} i uvezenu jedinicu CommCtrl, ostavljajući samo izravnu dodjelu svojstva Format. Zadržite uklanjanje u jednom commitu kako biste lakše prepoznali ako se nešto neočekivano pokvari na prevoditelju koji niste testirali. Sam niz za oblikovanje ostaje isti; mijenja se samo mehanizam isporuke.