TDateTimePicker, Delphi 5 ile Delphi 6 arasındaki bir noktada bir Format özelliği kazandı. Kesin sürüm sınırını güvenebileceğiniz biçimde belgelemek nadirdir; o sınıra çarptığınızda derleyici size yalnızca tanımlayıcının var olmadığını söyler. Peşinden koşulacak bir çalışma zamanı sürprizi yoktur: elinizdeki her yeni Delphi'de derlenip çalışan satır Delphi 5'te sert bir derleme hatası verir; asıl soru, tek bir kaynak ağacını her ikisinde de derlenebilir tutmaktır
Alttaki kontrol, comctl32.dll içindeki Windows DATETIMEPICK_CLASS ortak kontrolüdür ve her zaman bir biçim dizesini DTM_SETFORMAT mesajıyla kabul etmiştir. Delphi'nin VCL'i, daha yeni sürümlerde bu mesajı Format özelliğine sarar. Delphi 5'te mesajı, CommCtrl içinde tanımlanan DateTime_SetFormat makro sarmalayıcısı üzerinden kendiniz gönderirsiniz
Koşullu derleme kalıbı
Standart yaklaşım, bir sürüm sembolüne anahtarlanmış derleyici koşullu (compiler-conditional) bir bloktur. Delphi 5, VER130 tanımlar; doğrudan buna karşı test edebilir veya dallanma etiketini kod tabanında daha okunaklı hale getirmek için proje seçeneklerinde kendi D5 sembolünüzü tanımlayabilirsiniz. Her iki durumda da, Delphi 5 dalı kontrol tanıtıcısı (handle) ile DateTime_SetFormat işlevini çağırır; diğer tüm dallar ise özelliği atar:
{$IFDEF D5}
DateTime_SetFormat(DateTimePicker1.Handle, PChar('MM/dd/yyyy'));
{$ELSE}
DateTimePicker1.Format := 'MM/dd/yyyy';
{$ENDIF}
Delphi 5 dalını içeren unitin uses satırına CommCtrl'i ekleyin. Bunu koşulsuz eklemeniz gerekmez; bağımlılığı kapsamlı tutmayı tercih ederseniz uses girdisini aynı koşulun içine sarın:
uses
...,
{$IFDEF D5}
CommCtrl,
{$ENDIF}
...;
Format dizisi sözdizimi (syntax)
DateTime_SetFormat işlevine iletilen format dizisi, Delphi'nin FormatDateTime belirteçlerini değil, Windows tarih-saat seçici formatını izler. İkisi benzer görünür ancak birbirinin yerine kullanılamaz. Windows, ayın günü için d (başında sıfır olmadan), sıfır dolgulu gün için dd, ay numarası için M, sıfır dolgulu ay için MM, iki haneli yıl için yy ve dört haneli yıl için yyyy kullanır. Saat alanlarında 12 saatlik format için h/hh, 24 saatlik format için H/HH, dakikalar için m/mm, saniyeler için s/ss kullanılır. Format dizisinin içinde metin değişmezleri (literal text) tek tırnak içine alınır, yani 'dd/MM/yyyy' zaten doğrudur, ancak 'dd '<literal>' MM' gömülü tek tırnak çiftleri gerektirir
Buna karşılık Delphi'nin FormatDateTime işlevi, farklı semantiklere sahip gün biçimleri için d/dd/ddd/dddd kullanır ve ay için m (M değil) kullanır. Bir format dizisini bir FormatDateTime çağrısından kopyalayıp doğrudan DateTime_SetFormat işlevine verirseniz, çıktıda büyük ihtimalle ay ve dakika alanları yer değiştirmiş olacaktır. Kontrolün format dizisini bir Delphi formatından dönüştürmek yerine Windows belgelerine bakarak sıfırdan yazın
Bölgesel ayarlar ve küresel ShortDateFormat
Delphi'nin global ShortDateFormat değişkenini değiştirmek, zaten oluşturulmuş bir TDateTimePicker'ı etkilemez. Kontrol, oluşturulma anında bölgesel ayarları Windows'tan okur ve bundan sonra aldığı mesajlara göre çizer; Delphi RTL global'larına göre değil. Yani FormCreate içinde ShortDateFormat ayarlamak, kontrolün gösterdiklerini değiştirmez. Tanıtıcı var olduktan sonra kontrolün kendi biçim dizesini geçersiz kılmak için DateTime_SetFormat (ya da daha yeni Delphi'lerde Format özelliği) gerekir
Uluslararası dağıtılan uygulamalarda biçim dizesini, ABD dışında bir bölgesel ayarla çalışan bir makinede test edin. Windows, boş ya da nil bir biçim dizesini yerel ayarın varsayılanıyla değiştirebilir; ama açık bir non-nil dize bölgesel ayarı tamamen geride bırakır. Bu genellikle istediğiniz şeydir; yine de bu, 'MM/dd/yyyy' gibi ABD merkezli bir kalıbın, gelenek olarak dd/MM/yyyy bekleyen kullanıcılarda olduğu gibi görüneceği anlamına gelir. Tarih seçici salt veri girişi alanı değil de kullanıcıya dönükse, kontrolün varsayılana uymasına izin vermeyi (yani DateTime_SetFormat'a nil geçmeyi) düşünün; böylece kullanıcının yerel ayarını kendiliğinden izler
Handle (Tanıtıcı) yeniden oluşturulması
Format ayarı herhangi bir VCL nesne alanına değil, pencere tanıtıcısına (window handle) aittir. Çalışma zamanında bir kontrolün ebeveynini değiştirdiğinizde (reparent) veya belirli stil özelliklerini değiştirdiğinizde VCL'nin yapabildiği gibi, kontrolün tanıtıcısı yok edilip yeniden oluşturulursa format Windows varsayılanına döner. DateTime_SetFormat uygulamak için güvenli yer, tanıtıcı oluşturulduktan sonra çalışan bir işleyicidir (handler): alt bir sınıfta CreateWnd'yi geçersiz kılın (override) ve orada çağırın veya yalnızca tanıtıcının canlı olduğunu onayladıktan sonra OnEnter veya OnShow olayına uygulayın. Basit formlar için bunu FormCreate içinde bir kez ayarlamak genellikle yeterlidir, ancak form dinamik stil geçişleri veya kontrolün ebeveynini değiştirme (reparenting) işlemleri yapıyorsa güvenilir değildir
uses
ComCtrls, CommCtrl;
type
TFormattedDatePicker = class(TDateTimePicker)
private
FDisplayFormat: string;
procedure ApplyDisplayFormat;
procedure SetDisplayFormat(const Value: string);
protected
procedure CreateWnd; override;
public
property DisplayFormat: string read FDisplayFormat write SetDisplayFormat;
end;
procedure TFormattedDatePicker.ApplyDisplayFormat;
begin
if FDisplayFormat = '' then
DateTime_SetFormat(Handle, nil) // yerel ayarın varsayılanına geri döner
else
DateTime_SetFormat(Handle, PChar(FDisplayFormat));
end;
procedure TFormattedDatePicker.CreateWnd;
begin
inherited CreateWnd;
ApplyDisplayFormat; // her tanıtıcı yeniden oluşturulduğunda yeniden çalışır
end;
procedure TFormattedDatePicker.SetDisplayFormat(const Value: string);
begin
FDisplayFormat := Value;
if HandleAllocated then
ApplyDisplayFormat;
end;
Biçim dizesi Delphi nesnesinin bir alanında yaşar, bu yüzden pencereden uzun ömürlüdür; CreateWnd her yeni tanıtıcıya bu değeri aktarır. Özellik, Format yerine DisplayFormat adını taşır; böylece aynı unit daha yeni Delphi sürümleriyle derlendiğinde kalıtsal Format ile çakışmaz
Aynı kısıtlama daha yeni Delphi sürümlerindeki VCL Format özelliği için de geçerlidir; dahili olarak aynı Windows mesajını çağırır ve aynı handle yaşam döngüsüyle karşı karşıyadır. Aradaki fark, VCL özellik ayarlayıcısının (setter) çağrı yapmadan önce tanıtıcının ayrılıp (allocated) ayrılmadığını kontrol etmesi ve CreateWnd anında formatı otomatik olarak yeniden uygulamasıdır. Ham (raw) DateTime_SetFormat çağrısı böyle bir korumaya sahip değildir, bu nedenle Delphi 5 dalının ne zaman çalışacağı konusunda daha dikkatli olması gerekir
Geçici çözümü kaldırma
Bir proje Delphi 5 desteğini bıraktığında koşullu blok ölü ağırlık haline gelir. {$IFDEF D5} dalını ve kapsamlı CommCtrl içe aktarımını (import) kaldırarak geriye yalnızca doğrudan Format özellik atamasını bırakın. Test etmediğiniz bir derleyicide beklenmedik bir şekilde bir şey bozulursa tanımlamayı kolaylaştırmak için kaldırma işlemini tek bir işlem (commit) içinde tutun. Format dizisinin kendisi aynı kalır; yalnızca teslim mekanizması değişir