Technical Article

TDateTimePicker formatas Delphi 5 aplinkoje: DateTime_SetFormat

TDateTimePicker gavo savybę Format kažkur tarp „Delphi 5“ ir „Delphi 6“ versijų. Tiksli versijų riba retai kada dokumentuojama taip, kad būtų galima pasitikėti, o kai su tuo susiduriate, kompiliatorius tiesiog praneša, kad toks identifikatorius neegzistuoja. Eilutė, kuri kompiliuojasi ir veikia visose naujesnėse „Delphi“ versijose, senojoje versijoje tyliai arba visiškai sugenda, priklausomai nuo projekto struktūros.

Valdiklis, kuriuo remiasi šis komponentas, yra standartinis „Windows“ DATETIMEPICK_CLASS valdiklis iš comctl32.dll bibliotekos, ir jis visada priėmė formato eilutę per DTM_SETFORMAT pranešimą. Naujesnėse versijose „Delphi“ VCL apgaubia šį pranešimą savybe Format. Naudojant „Delphi 5“, pranešimą turite nusiųsti patys naudodami DateTime_SetFormat makrokomandos apvalkalą, deklaruotą CommCtrl modulyje.

Sąlyginio kompiliavimo šablonas

Standartinis būdas yra sąlyginio kompiliavimo blokas pagal versijos simbolį. „Delphi 5“ apibrėžia VER130; galite jį tikrinti tiesiogiai arba projekto nustatymuose apibrėžti savo D5 simbolį, kad šakos žymė būtų lengviau skaitoma visoje kodo bazėje. Bet kuriuo atveju „Delphi 5“ šaka iškviečia DateTime_SetFormat su valdiklio rankenėle (handle), o visos kitos šakos tiesiog priskiria savybę:

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

Įtraukite CommCtrl į to modulio uses skyrių, kuriame yra „Delphi 5“ šaka. Nereikia jo įtraukti besąlygiškai; jei norite apriboti priklausomybę, apgaubkite uses įrašą ta pačia sąlyga:

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

Formato eilutės sintaksė

Formato eilutė, perduodama į DateTime_SetFormat, atitinka „Windows“ datos ir laiko parinkiklio formatą, o ne „Delphi“ FormatDateTime elementus. Jie atrodo panašiai, bet nėra suderinami. „Windows“ naudoja d mėnesio dienai (be priekinio nulio), dd dienai su nuliniu užpildu, M mėnesio numeriui, MM mėnesiui su nuliniu užpildu, yy dviženkliams metams ir yyyy keturženkliams metams. Laiko laukai naudoja h/hh 12 valandų formatui, H/HH 24 valandų formatui, m/mm minutėms, s/ss sekundėms. Tekstinė reikšmė (literal text) formato eilutėje rašoma viengubose kabutėse, todėl 'dd/MM/yyyy' jau yra teisinga, tačiau, pavyzdžiui, 'dd '<literal>' MM' pareikalautų įdėtinių viengubų kabučių porų.

Priešingai, „Delphi“ FormatDateTime naudoja d/dd/ddd/dddd dienų formoms su kitokia semantika, o mėnesiui naudoja m (ne M). Jei nukopijuosite formato eilutę iš FormatDateTime iškvietimo ir perduosite ją tiesiai į DateTime_SetFormat, išvestyje mėnesio ir minučių laukai tikriausiai susikeis vietomis. Rašykite valdiklio formato eilutę iš naujo pagal „Windows“ dokumentaciją, užuot konvertavę ją iš „Delphi“ formato.

Regioniniai nustatymai ir visuotinė ShortDateFormat reikšmė

Pakeitus „Delphi“ visuotinį ShortDateFormat kintamąjį, tai nepaveikia jau sukurto TDateTimePicker. Parinkiklis nuskaito regioninius nustatymus iš „Windows“ sistemos kūrimo metu ir toliau atvaizduoja duomenis pagal gaunamus pranešimus, o ne pagal „Delphi“ RTL visuotinius kintamuosius. Tai reiškia, kad nustačius ShortDateFormat procedūroje FormCreate, valdiklio rodomas vaizdas nepasikeis. Jums reikia DateTime_SetFormat (arba savybės Format naujesnėse „Delphi“ versijose), kad pakeistumėte paties valdiklio formato eilutę po to, kai atsiranda rankenėlė (handle).

Jei programa platinama tarptautiniu mastu, išbandykite formato eilutę kompiuteryje su ne JAV regioniniais nustatymais. „Windows“ gali pakeisti tuščią arba nil formato eilutę numatytuoju lokalės formatu, tačiau aiškiai nurodyta ne nil eilutė visiškai pakeičia regioninį nustatymą. Paprastai to ir norima, tačiau tai reiškia, kad į JAV orientuotas šablonas, toks kaip 'MM/dd/yyyy', bus rodomas toks, koks yra, net ir vartotojams, kurie pagal konvenciją tikisi formato dd/MM/yyyy. Jei datos parinkiklis yra skirtas tiesiogiai vartotojui, o ne grynai duomenų įvedimo laukas, apsvarstykite galimybę palikti numatytąjį valdiklio formatą (perduokite nil į DateTime_SetFormat), kad jis automatiškai sektų vartotojo lokalę.

Rankenėlės perrašymas (recreation)

Formato nustatymas priklauso lango rankenėlei, o ne kokiam nors VCL objekto laukui. Jei valdiklio rankenėlė sunaikinama ir sukuriama iš naujo (ką VCL gali padaryti, kai pakeičiate valdiklio tėvinį elementą / reparent arba pakeičiate tam tikras stiliaus savybes vykdymo metu), formatas grįžta į „Windows“ numatytąjį nustatymą. Saugi vieta pritaikyti DateTime_SetFormat yra apdorojimo programa, kuri paleidžiama sukūrus rankenėlę: perrašykite CreateWnd antrinėje klasėje ir iškvieskite jį ten, arba pritaikykite jį įvykiuose OnEnter ar OnShow tik įsitikinę, kad rankenėlė yra aktyvi. Paprastose formose dažnai pakanka jį nustatyti vieną kartą FormCreate, tačiau tai nėra patikima, jei formoje atliekamas dinaminis stilių perjungimas ar valdiklių tėvinių elementų keitimas.

Tas pats apribojimas galioja ir VCL savybei Format naujesnėse „Delphi“ versijose; viduje ji siunčia tą patį „Windows“ pranešimą ir susiduria su tuo pačiu rankenėlės gyvavimo ciklu. Skirtumas yra tas, kad VCL savybės priskyrimas patikrina, ar rankenėlė yra išskirta prieš siunčiant pranešimą, ir automatiškai pritaiko formatą iš naujo per CreateWnd. Grynas DateTime_SetFormat iškvietimas netcyrė tokios apsaugos, todėl „Delphi 5“ šaka turi tiksliau valdyti savo paleidimo momentą.

Laikino sprendimo pašalinimas

Kai projekte atsisakoma „Delphi 5“ palaikymo, sąlyginis blokas tampa nereikalingu svoriu. Pašalinkite {$IFDEF D5} šaką ir apribotą CommCtrl importą, palikdami tik tiesioginį savybės Format priskyrimą. Atlikite šį pašalinimą vienoje transakcijoje (commit), kad būtų lengva nustatyti, jei kas nors netikėtai sugenda kompiliatoriuje, kurio netestavote. Pati formato eilutė išlieka tokia pati; keičiasi tik jos perdavimo mechanizmas.