Search code examples
delphivcldynamic-controlsdelphi-5

Error on setting TDateTimePicker.Font.Style to italic in Delphi 5


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?


Solution

  • 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.