【问题标题】:Error on setting TDateTimePicker.Font.Style to italic in Delphi 5在 Delphi 5 中将 TDateTimePicker.Font.Style 设置为斜体时出错
【发布时间】:2009-08-24 19:51:30
【问题描述】:

TDateTimePicker 的字体可以设置成斜体吗?我在这段代码中这样做

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;

并且每次代码执行最后一行时,都会抛出一个 EInvalidOperation 异常并显示一条消息:

„Control” has no parent window.

这是 Delphi 5 的功能,还是我做错了什么?

【问题讨论】:

    标签: delphi vcl dynamic-controls delphi-5


    【解决方案1】:

    是的,你做错了什么。 :)

    基于窗口的控件的某些方面要求将控件放置在它的容器窗口上。这通常反映了底层 Window 类本身的一些要求(不是 VCL 类,而是代表控件的 MS Windows 窗口对象)。

    在这种情况下,只需移动您对 Parent 属性的分配,使其在您尝试更改 Font.Style 之前发生:

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

    我不知道字体是否可以通过这种方式成功设置为斜体,但这至少应该解决您的异常。

    更新:我已经确认可以通过这种方式将字体设置为斜体,只要你先设置了控件Parent。 (我使用的是 Delphi 2009,但如果它在 Delphi 5 中不起作用,我会感到惊讶)

    仅供参考:我会强烈为“画布”面板控件推荐一个不同的名称选择。

    附录:--------

    CMFontChanged() 消息处理程序中调用 InvalidateRect() 需要一个窗口句柄(这反过来又要求该控件是父控件)。

    如果您绝对需要能够在修改 Font 之后设置 Parent ,您可以从 派生自己的控件类>TDateTimePicker 并实现对 CMFontChanged() 消息的响应,该消息抑制继承的行为,除非控件是父控件:

    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;
    

    请注意,AdjustHeight() 过程是从 TDateTimePickerprivate 方法复制而来的。如果您只更改 Font.Style 您可能不需要重现此代码并且可以删除它和对它的调用,但是如果您更改 Font 的其他属性那么这应该确保控件的大小正确。

    当您调用 inherited 时,将调用私有 AdjustHeight() 机制,因此在这种情况下无需调用该过程的本地副本。

    【讨论】:

    • 将此组件放在 Parent 上是稍后出于目的。我经常创建控件(甚至是 TFrame 的后代)并在很久以后将它们放在 Parent 上。对我来说奇怪的是 - 在同一个应用程序中,我对 TEdit 或 TComboBox 使用几乎相同的代码......并且它可以工作。那么为什么TDateTimePicker在修改Font.Style时需要设置Parent,而TEdit不需要呢?
    • 顺便说一句:“Canvas”这个名字很可能会被替换。
    • 快速查看源代码显示(例如)TEdit 和 TDateTimePicker 之间的主要区别在于,当您修改 TDateTimePicker 的字体(ComCtrls 中的 TDateTimePicker.CMFontChanged())时,它会导致控件使自身无效,这需要一个窗口句柄。要获得该窗口句柄,必须创建底层窗口对象,这需要知道容器窗口。这可以通过测试是否存在窗口句柄并仅在窗口句柄存在时无效来避免。但这种改进不是当前实现的一部分。
    • 我已经更新了我的答案以包含一个解决方法,以允许您在设置父控件之前更改 TDateTimePicker(派生)控件的字体。显然,如果您遇到与其他控件类型(或 TDateTimePicker 的其他属性)类似的问题,则必须在每种情况下找到合适的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-02
    • 2017-04-09
    相关资源
    最近更新 更多