Technisch Artikel

TDateTimePicker Formaat in Delphi 5: DateTime_SetFormat

TDateTimePicker kreeg een Format eigenschap ergens tussen Delphi 5 en Delphi 6. De exacte versiegrens wordt zelden gedocumenteerd op een manier die u kunt vertrouwen, en wanneer u erop stuit, vertelt de compiler u simpelweg dat de identifier niet bestaat. De regel die op elke nieuwere Delphi compileert en werkt, faalt stilletjes of direct op de oude versie, afhankelijk van hoe het project is gestructureerd

Het onderliggende besturingselement is de Windows DATETIMEPICK_CLASS common control van comctl32.dll, en deze heeft altijd een formaatreeks geaccepteerd via het DTM_SETFORMAT bericht. Delphi's VCL verpakt dat bericht in de Format eigenschap in nieuwere versies. In Delphi 5 verstuurt u het bericht zelf via de DateTime_SetFormat macrowrapper gedeclareerd in CommCtrl

Het voorwaardelijke compilatiepatroon

De standaard benadering is een compiler-voorwaardelijk blok gekoppeld aan een versiesymbool. Delphi 5 definieert VER130; u kunt er direct tegen testen of uw eigen D5 symbool definiëren in de projectopties om het vertakkingslabel leesbaarder te maken in de codebase. Hoe dan ook, de Delphi 5 vertakking roept DateTime_SetFormat aan met de control-handle; elke andere vertakking wijst de eigenschap toe:

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

Voeg CommCtrl toe aan de uses clausule voor de unit die de Delphi 5 vertakking bevat. Het is niet nodig om het onvoorwaardelijk toe te voegen; als u de afhankelijkheid liever beperkt houdt, verpak dan de uses toevoeging in dezelfde conditional:

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

Syntaxis van de formaatreeks

De formaatreeks die wordt doorgegeven aan DateTime_SetFormat volgt het Windows date-time picker formaat, niet Delphi's FormatDateTime tokens. De twee zien er vergelijkbaar uit, maar zijn niet uitwisselbaar. Windows gebruikt d voor de dag van de maand (geen voorloopnul), dd voor een dag met nul-opvulling, M voor maandnummer, MM voor een maand met nul-opvulling, yy voor een tweecijferig jaar en yyyy voor een viercijferig jaar. Tijdvelden gebruiken h/hh voor 12-uurs uren, H/HH voor 24-uurs, m/mm voor minuten, s/ss voor seconden. Letterlijke tekst gaat in enkele aanhalingstekens binnen de formaatreeks, dus 'dd/MM/yyyy' is al correct, maar 'dd '<literal>' MM' zou ingebedde enkele aanhalingstekensparen vereisen

Delphi's FormatDateTime gebruikt daarentegen d/dd/ddd/dddd voor dagvormen met andere semantiek, en m voor maand (niet M). Als u een formaatreeks kopieert van een FormatDateTime aanroep en deze direct aan DateTime_SetFormat doorgeeft, worden de maand- en minuutvelden waarschijnlijk verwisseld in de uitvoer. Schrijf de formaatreeks van de control vanaf nul volgens de Windows-documentatie in plaats van te converteren vanuit een Delphi-formaat

Regionale instellingen en de globale ShortDateFormat

Het wijzigen van Delphi's globale ShortDateFormat variabele heeft geen invloed op een TDateTimePicker die al is gemaakt. De picker leest regionale instellingen van Windows tijdens het aanmaken en rendert daarna volgens de berichten die hij ontvangt, niet de Delphi RTL globals. Dat betekent dat het instellen van ShortDateFormat in FormCreate niet verandert wat de control weergeeft. U heeft DateTime_SetFormat (of de Format eigenschap in nieuwere Delphi) nodig om de eigen formaatreeks van de control te overschrijven nadat de handle bestaat

Voor toepassingen die internationaal worden geïmplementeerd, test de formaatreeks op een machine met een niet-Amerikaanse regionale instelling. Windows kan een lege of nil formaatreeks overschrijven met de lokale standaard, maar een expliciete niet-nil reeks vervangt de regionale instelling volledig. Dat is meestal wat u wilt, maar het betekent dat een Amerikaans gecentreerd patroon zoals 'MM/dd/yyyy' als zodanig zal verschijnen voor gebruikers die conventioneel dd/MM/yyyy verwachten. Als de datumpicker voor de gebruiker is bedoeld in plaats van een puur data-invoerveld, overweeg dan om de control standaard te laten werken (geef nil door aan DateTime_SetFormat) zodat deze automatisch de landinstelling van de gebruiker volgt

Handle recreatie

De formaatinstelling behoort toe aan de window-handle, niet aan een VCL-objectveld. Als de handle van de control wordt vernietigd en opnieuw wordt aangemaakt, wat VCL kan doen wanneer u een control re-parent of bepaalde stijleigenschappen tijdens runtime wijzigt, keert het formaat terug naar de Windows-standaard. De veilige plaats om DateTime_SetFormat toe te passen is in een handler die draait na het aanmaken van de handle: overschrijf CreateWnd in een afstammeling-klasse en roep het daar aan, of pas het alleen toe in de OnEnter of OnShow gebeurtenis na bevestiging dat de handle actief is. Het eenmalig instellen in FormCreate is vaak voldoende voor eenvoudige formulieren, maar is niet betrouwbaar als het formulier dynamische stijlwisseling of control re-parenting toepast

Dezelfde beperking is van toepassing op de VCL Format eigenschap in nieuwere Delphi versies; intern roept het hetzelfde Windows-bericht aan en wordt het geconfronteerd met dezelfde handle-levenscyclus. Het verschil is dat de VCL property-setter controleert of de handle is toegewezen voordat hij doorkoppelt, en het formaat automatisch opnieuw toepast bij CreateWnd. De ruwe DateTime_SetFormat aanroep heeft geen dergelijke bescherming, dus de Delphi 5 vertakking moet bewuster zijn over wanneer deze draait

De tijdelijke oplossing verwijderen

Wanneer een project de ondersteuning voor Delphi 5 laat vallen, wordt het voorwaardelijke blok dode ballast. Verwijder de {$IFDEF D5} vertakking en de gescopte CommCtrl import, waardoor alleen de directe Format eigenschapstoewijzing overblijft. Houd de verwijdering in een enkele commit zodat het gemakkelijk te identificeren is als er onverwacht iets breekt op een compiler die u niet heeft getest. De formaatreeks zelf blijft hetzelfde; alleen het leveringsmechanisme verandert