Artigo Técnico

Formato de TDateTimePicker no Delphi 5: DateTime_SetFormat

O TDateTimePicker ganhou uma propriedade Format em algum lugar entre o Delphi 5 e o Delphi 6. O limite exato da versão raramente é documentado de uma forma em que você possa confiar e, quando você se depara com ele, o compilador simplesmente diz que o identificador não existe. A linha que compila e funciona em todo Delphi mais recente falha silenciosamente ou completamente no antigo, dependendo de como o projeto está estruturado

O controle subjacente é o controle comum do Windows DATETIMEPICK_CLASS da comctl32.dll e ele sempre aceitou uma string de formato por meio da mensagem DTM_SETFORMAT. A VCL do Delphi encapsula essa mensagem na propriedade Format nas versões mais recentes. No Delphi 5, você mesmo envia a mensagem por meio do wrapper de macro DateTime_SetFormat declarado na CommCtrl

O padrão de compilação condicional

A abordagem padrão é um bloco condicional de compilador chaveado em um símbolo de versão. O Delphi 5 define VER130; você pode testar em relação a ele diretamente ou definir o seu próprio símbolo D5 nas opções do projeto para tornar o rótulo do ramo mais legível em toda a base de código. De qualquer maneira, a ramificação do Delphi 5 chama DateTime_SetFormat com o handle do controle; todas as outras ramificações atribuem a propriedade:

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

Adicione CommCtrl à cláusula uses para a unidade que contém o ramo do Delphi 5. Não há necessidade de adicioná-la incondicionalmente; se você preferir manter a dependência no escopo, encapsule a entrada uses na mesma condicional:

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

Sintaxe da string de formato

A string de formato passada para DateTime_SetFormat segue o formato de seletor de data e hora do Windows, não os tokens FormatDateTime do Delphi. Os dois parecem semelhantes, mas não são intercambiáveis. O Windows usa d para o dia do mês (sem zero à esquerda), dd para o dia com preenchimento de zero, M para o número do mês, MM para o mês com preenchimento de zero, yy para o ano com dois dígitos e yyyy para o ano com quatro dígitos. Os campos de hora usam h/hh para horas no formato de 12 horas, H/HH para formato de 24 horas, m/mm para minutos, s/ss para segundos. O texto literal vai entre aspas simples dentro da string de formato, então 'dd/MM/yyyy' já está correto, mas 'dd '<literal>' MM' exigiria pares de aspas simples embutidos

O FormatDateTime do Delphi, em contrapartida, usa d/dd/ddd/dddd para formas de dia com diferentes semânticas e m para o mês (não M). Se você copiar uma string de formato de uma chamada FormatDateTime e passá-la diretamente para DateTime_SetFormat, os campos de mês e minuto provavelmente serão trocados na saída. Escreva a string de formato do controle do zero com base na documentação do Windows, em vez de converter de um formato Delphi

Configurações regionais e a global ShortDateFormat

Aterar a variável global ShortDateFormat do Delphi não afeta um TDateTimePicker que já foi criado. O seletor lê as configurações regionais do Windows no momento da criação e, a partir de então, renderiza de acordo com as mensagens que recebe, não as variáveis globais da RTL do Delphi. Isso significa que definir ShortDateFormat no FormCreate não mudará o que o controle exibe. Você precisa de DateTime_SetFormat (ou a propriedade Format no Delphi mais recente) para substituir a string de formato do próprio controle depois que o handle existir

Para aplicativos implantados internacionalmente, teste a string de formato em uma máquina com uma configuração regional não americana. O Windows pode substituir uma string de formato em branco ou nil pelo padrão de localidade, mas uma string não nil explícita substitui a configuração regional inteiramente. Isso geralmente é o que você deseja, mas significa que um padrão centrado nos EUA como 'MM/dd/yyyy' aparecerá como está para usuários que esperam dd/MM/yyyy por convenção. Se o seletor de data for voltado para o usuário, em vez de um puro campo de entrada de dados, considere deixar o padrão do controle (passe nil para DateTime_SetFormat) para que ele siga o local do usuário automaticamente

Recriação do handle

A configuração de formato pertence ao handle da janela, não a nenhum campo de objeto da VCL. Se o handle do controle for destruído e recriado, o que a VCL pode fazer quando você repassa um controle ou altera certas propriedades de estilo em tempo de execução, o formato reverte para o padrão do Windows. O lugar seguro para aplicar DateTime_SetFormat é em um manipulador que é executado após a criação do handle: substitua CreateWnd em uma classe descendente e chame-o lá, ou aplique-o no evento OnEnter ou OnShow somente após confirmar que o handle está ativo. Defini-lo uma vez em FormCreate é frequentemente suficiente para formulários simples, mas não é confiável se o formulário fizer comutação dinâmica de estilo ou reparentalidade de controle

A mesma restrição se aplica à propriedade Format da VCL nas versões mais recentes do Delphi; internamente ela chama a mesma mensagem do Windows e enfrenta o mesmo ciclo de vida do handle. A diferença é que o setter da propriedade da VCL verifica se o handle está alocado antes de chamar por meio dele e reaplica o formato automaticamente no CreateWnd. A chamada DateTime_SetFormat simples não tem tal proteção, então a ramificação do Delphi 5 precisa ser mais deliberada sobre quando é executada

Removendo a solução alternativa

Quando um projeto deixa de suportar o Delphi 5, o bloco condicional se torna peso morto. Remova a ramificação {$IFDEF D5} e a importação com escopo do CommCtrl, deixando apenas a atribuição direta da propriedade Format. Mantenha a remoção em um único commit, de modo que seja fácil identificar se algo quebrar inesperadamente em um compilador que você não testou. A própria string de formato permanece a mesma; apenas o mecanismo de entrega muda