views:

251

answers:

1

Is it possible to set TDateTimePicker’s font to italic? I am doing it in this code

var
  DatEdit : TDateTimePicker;
begin
  //I know Canvas is a stupid name for TPanel
  DatEdit:=TDateTimePicker.Create(Canvas);

  DatEdit.OnEnter := CtrlInputProc;
  DatEdit.OnExit := CtrlExitProc;
  DatEdit.Enabled := false;
  DatEdit.Font.Style := DatEdit.Font.Style + [fsItalic]; //this line creates an exception
  DatEdit.Parent := Canvas;

end;

And every time code executes last line, an EInvalidOperation exception is thrown with a message:

„Control” has no parent window.

Is this Delphi 5 feature, or am I doing something wrong?

+7  A: 

Yes, you are doing something wrong. :)

Some aspects of a window based control require that the control be placed on it's container window. This usually reflects some requirement in the underlying Window Class itself (not the VCL class, but the MS Windows window object representing the control).

In this case, simply move your assignment of the Parent property so that it occurs BEFORE you attempt to change the Font.Style :

  DatEdit.OnEnter := CtrlInputProc;
  DatEdit.OnExit := CtrlExitProc;
  DatEdit.Enabled := false;
  DatEdit.Parent := Canvas;
  DatEdit.Font.Style := DatEdit.Font.Style + [fsItalic];

I don't know if the font can be successfully set to italic in this way or not, but this should at least address your exception.

UPDATE: I have confirmed that the font can be made italic in this way, as long as you have first set the control Parent. (I used Delphi 2009 ,but I'd be surprised if it didn't work in Delphi 5)

FYI: I would strongly recommend a different choice of name for that "Canvas" panel control.

Addendum: -------

It is the call to InvalidateRect() in the CMFontChanged() message handler that requires a Window Handle (which in turn requires that the control be parented).

If you absolutely need to be able to set the Parent after modifying the Font, you could derive your own control class from TDateTimePicker and implement a response to the CMFontChanged() message that suppresses the inherited behaviour unless the control is parented:

type
  TMyDateTimePicker = class(TDateTimePicker)
  protected
    procedure CMFontchanged(var Message: TMessage); message CM_FONTCHANGED;
  end;


  procedure TMyDateTimePicker.CMFontchanged(var Message: TMessage);

    procedure AdjustHeight;
    var
      DC: HDC;
      SaveFont: HFont;
      SysMetrics, Metrics: TTextMetric;
    begin
      DC := GetDC(0);
      try
        GetTextMetrics(DC, SysMetrics);
        SaveFont := SelectObject(DC, Font.Handle);
        GetTextMetrics(DC, Metrics);
        SelectObject(DC, SaveFont);
      finally
        ReleaseDC(0, DC);
      end;
      Height := Metrics.tmHeight + (GetSystemMetrics(SM_CYBORDER) * 8);
    end;

  begin
    if HasParent then
      inherited
    else
      AdjustHeight;
  end;

Note that the AdjustHeight() procedure is reproduced from a private method of TDateTimePicker. If you are only changing the Font.Style you may not need to reproduce this code and can remove it and the call to it, but if you alter other properties of the Font then this should ensure that the control is correctly sized.

When you call inherited the private AdjustHeight() mechanism is invoked so there is no need to call the local copy of that procedure in that case.

Deltics
Putting this component on Parent is done later by purpouse. I often create controls (even descendants of TFrame) and put them on Parent much later. What is strange for me - in the same application I use almost the same code to TEdit or TComboBox... and it works. So why TDateTimePicker requires to have set Parent when modyfying Font.Style, while TEdit does not?
smok1
BTW: the name "Canvas" is most probably to be replaced.
smok1
A quick look at the source shows the key difference between (e.g) a TEdit and a TDateTimePicker is that when you modify the font of a TDateTimePicker (TDateTimePicker.CMFontChanged() in ComCtrls) it results in the control invalidating itself, which requires a window handle.To obtain that window handle, the underlying window object must be created, and that requires that the container window be known.This could be avoided by testing for the existence of a window handle and only invalidating if the window handle exists. But this refinement isn't part of the current implementation.
Deltics
I've updated my answer to include a work-around to allow you to change the font of a TDateTimePicker (derived) control before you have set the parent. Obviously if you run into similar problems with other control types (or other properties of a TDateTimePicker) then a suitable solution will have to be found in each case.
Deltics