4

I am using a TDateTimePicker to allow the user to select a day (either manually or by clicking buttons to go forward or back one day). The user will then see log entries for that particular day.

The control works great, but I want to restrict the user to between [OLDEST-DAY-IN-LOG] and [TODAY], because those are the date limits where there is data.

I've set the MinDate and MaxDate on the TDateTimePicker and that works fine. Selecting a date outside the allowed range does nothing.

My question is whether it is possible to draw the invalid dates greyed out. In the attached image, the date selected is 01/04 and today is 02/04. I'd like to see 03/04 (and later) disabled. Ideally, like the "30" and "31" you see in the image which are the last days of March.

Is this possible? Using Delphi 7 if that is relevant.

TDateTimePicker control in Delphi

Ken White
  • 123,280
  • 14
  • 225
  • 444
AlainD
  • 5,413
  • 6
  • 45
  • 99
  • 1
    MinDate and MaxDate are set (stated in question) and that works. When the user selects 03/04, the control does not generate an "OnChange" or "CloseUp" event. By greying out the invalid dates, the user would at least see why nothing happened. – AlainD Apr 02 '15 at 14:23
  • Use a TMonthCalendar, and if you use an xpmanifest, the range between min-max are the only visible days. – Sertac Akyuz Apr 02 '15 at 17:54
  • @SertacAkyuz: Interesting idea to use the TMonthCalendar, never used one of those before. My date picker is small and at the top of the form like a dropdownlist. The TMonthCalendar appears to have a (fairly large) minimum size, making it unsuitable for my user interface. Couldn't see any properties to change its' style, can this be done? – AlainD Apr 02 '15 at 18:20
  • 1
    Actually I just tested with a TDateTime picker, the calendars are the same for both controls. All you need is to use 'xpman', and you can continue to use the picker. – Sertac Akyuz Apr 02 '15 at 18:30

2 Answers2

2

TDateTimePicker is a wrapper for a Win32 DTP control, which does not provide the functionality you are looking for. There is no option for custom drawing the drop-down calendar, and the only per-day styles available are normal and bold.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks Remy. Nothing immediately jumps out at me in the listed properties and methods to set the *bold* format and see what it looks like. That might work if I can set the min/max dates bold... – AlainD Apr 02 '15 at 18:22
  • @AlainD: See the `BoldDays` property. – Ken White Apr 02 '15 at 18:27
  • 1
    @KenWhite: [`BoldDays`](http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.ComCtrls.TCommonCalendar.BoldDays) is a method, not a property. It prepares an array of day states, but does not actually assign that array to the DTP. You have to use the [`OnGetMonthInfo`](http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.ComCtrls.TCommonCalendar.OnGetMonthInfo) or [`OnGetMonthBoldInfo`](http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.ComCtrls.TCommonCalendar.OnGetMonthBoldInfo) event for that, or intercept the `MCN_GETDAYSTATE` notification. See http://stackoverflow.com/questions/7210565/ – Remy Lebeau Apr 02 '15 at 18:30
  • @Remy: Yes, I misspoke. Memory glitch; I was looking it up when your comment hit my inbox. – Ken White Apr 02 '15 at 18:37
  • How do I make use of the OnGetMonthInfo event? When I select the list of events in the Designer, this event does not appear. If I cast my TDateTimePicker to its parent TCommonCalendar, there's also no event with that name. Is this a Delphi 7 limitation? I'm also developing on XP Embedded (basically Windows XP) which does not appear to support the DTM_GETMCSTYLE messages I appear to need to get this to work. Looks like this might be a "No" until I upgrade... – AlainD Apr 02 '15 at 22:18
  • @AlainD: see the answers in the SO link I provided in my earlier comment. One of them shows how `DTM_GETMCSTYLE` and `DTM_SETMCSTYLE` are defined. As for `MCN_GETDAYSTATE`, it is defined as `const MCN_GETDAYSTATE = MCN_FIRST - 1;`, where `MCN_FIRST` is defined as `const MCN_FIRST = 0-746;`. – Remy Lebeau Apr 02 '15 at 22:30
  • @RemyLebeau: Thanks Remy. Actually, I followed the links and ran the code...but although the code runs fine the style does not change. After following the links to the DTM_GETMCSTYLE message on MSDN, it looks like this is implemented in Vista onwards only... – AlainD Apr 02 '15 at 22:36
  • @moskito-x: Just downloaded the files from GitHub and going to give them a test now...but with Easter tomorrow will be off to bed soon. Will have to give feedback over the weekend. Thanks! – AlainD Apr 02 '15 at 22:37
  • @moskito-x: Oh, that DPR looks like an XML file, what version of Delphi is that targetting? Using Delphi 7 here... – AlainD Apr 02 '15 at 22:41
  • 1
    @AlainD: You cannot always trust what MSDN says about minimum OS versions for most APIs. When Microsoft drops support for an OS (like they recently did for XP), it usually drops mention of that OS throughout MSDN. There are tons of API docs that no longer mention 2000 and XP as minimum OS versions, even though those APIs really did exist in 2000 and XP. In particular, the `MCN_GETDAYSTATE` notification has been supported since ComCtl32.dll v4.70, which was included with Internet Explorer 3 in Windows 95 OSR2. – Remy Lebeau Apr 02 '15 at 22:53
  • @AlainD : It never looks like a xml file ! Look at my answer at the bottom. I paste `MonthCalendarDemo.dpr` there . Looks normal to me. – moskito-x Apr 02 '15 at 23:03
  • @moskito-x: No, I meant the DPR file appears in an XML-style format with tag at the top...Delphi 7 DPR files have a different style, more like a standard PAS unit. What version of Delphi does that DPR target? – AlainD Apr 04 '15 at 11:48
  • @AlainD : take my posted `.dpr` version. Or go to my `4 files link` , click on `MonthCalendarDemo.dpr` and look at the content. The files are from delphi 5 up – moskito-x Apr 04 '15 at 16:32
1

As already, mentioned in comments and the answer only styles normal and bold can used. With TYearBoldManager you can implement that.

only set March 30,31 and April 1,2,3,4

Tested : Delphi5 on win XP/3

minDate = 2015/03/30 and maxDate = 2015/04/04

you can set the bold dates with

procedure TForm1.FormCreate(Sender: TObject);
begin
   MonthCalendar1.CalColors.MonthBackColor :=  $6A7678;
   MonthCalendar1.CalColors.TextColor := $4D5858;

   FYearBoldManager := TYearBoldManager.Create;
   FYearBoldManager.MakeBold(3, 30);
   FYearBoldManager.MakeBold(3, 31);
   FYearBoldManager.MakeBold(4, 1);
   FYearBoldManager.MakeBold(4, 2);
   FYearBoldManager.MakeBold(4, 3);
   FYearBoldManager.MakeBold(4, 4);
   ...
end;

Then you should change the color values to get the best possible contrast. Here are just a suggestion.

enter image description here

enter image description here

to Test there are 4 files here , MonthCalendarDemo

Update :

MonthCalendarDemo.dpr

program MonthCalendarDemo;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  UMonthBoldStorage in 'UMonthBoldStorage.pas';

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Look at UMonthBoldStorage.pas how it's done.


What is the difference here ?

enter image description here

The experts have recognized it.

to the right TDateTimePicker

on the left a TMonthCalendar and a TComboBox

Advantage:

  • No min- maxDate needed
  • can have days without logfile.
  • full control over the calendar.
  • bold days are easy to use
  • can see where you clicked (Look at the nice dotted border at 25. But has no effect on the calendar).
  • no disappear if you click on the wrong date.(can click as much as you want , calendar will only disappear when you double-click on bold days. here : days 20 and 22).

enter image description here

  • can click inside the TComboBox or on the down-arrow or when focused hit ENTER opens the calendar and brings you to the last date with a logfile.

enter image description here

enter image description here

  • from there you can comfortably, with the arrow keys, the months with days that have a logfile go through.

enter image description here

all described could be accomplished with a few lines additional code

procedure TForm1.MonthCalendar1DblClick(Sender: TObject);
var
   year, month, day : Word;
begin
    DecodeDate(MonthCalendar1.Date,Year, Month, Day);
    if FYearBoldManager.GetDayStatus(month, day) then begin
       if ValidDate then MonthCalendar1.Visible:=False;
    end;
end;

procedure TForm1.MonthCalendar1Click(Sender: TObject);
var
   year, month, day : Word;
begin
   DecodeDate(MonthCalendar1.Date,Year, Month, Day);
   if FYearBoldManager.GetDayStatus(month, day) then begin
      lastValidDate := MonthCalendar1.Date;
      ValidDate:=True;
   end else begin
      MonthCalendar1.Date := lastValidDate;
      ValidDate:=False;
   end;
end;

function TForm1.getComboBoxText(var validText:AnsiString):Boolean;
var
actText :AnsiString;

begin
    if ComboBox1.Text = '' then  actText := validText else actText := ComboBox1.Text;
    Try
    MonthCalendar1.Date :=  StrToDateTime(Copy(actText,1,10));
    if actText <> validText then validText := actText;
    lastValidDate := MonthCalendar1.Date;
    ValidDate:=True;
    Result := True;
    except
      Result := False;
    end;
end;

procedure TForm1.ComboBox1Click(Sender: TObject);
begin
     if getComboBoxText(validText) then MonthCalendar1.Visible:=True;
end;

procedure TForm1.ComboBox1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
     if getComboBoxText(validText) then MonthCalendar1.Visible:=True;
end;
moskito-x
  • 11,832
  • 5
  • 47
  • 60