【问题标题】:How do you programmatically mark a date in Delphi's TDateTimePicker's calendar?您如何以编程方式在 Delphi 的 TDateTimePicker 日历中标记日期?
【发布时间】:2011-08-26 21:05:53
【问题描述】:

当用户单击 DateTimePicker 并设置为下拉日历时,我希望各种日期(通过我创建的代码选择)以某种方式突出显示——彩色背景、粗体字体、彩色字体,没关系。我只想标记某些日期。 ...我该怎么做?

【问题讨论】:

    标签: delphi datetimepicker


    【解决方案1】:

    是的,您可以这样做。首先,你必须初始化控件:

    const
      DTM_GETMCSTYLE = DTM_FIRST + 12;
      DTM_SETMCSTYLE = DTM_FIRST + 11;
    ...
    SendMessage(DateTimePicker1.Handle,
      DTM_SETMCSTYLE,
      0,
      SendMessage(DateTimePicker1.Handle, DTM_GETMCSTYLE, 0, 0) or MCS_DAYSTATE);
    

    (uses CommCtrl).

    然后您只需回复MCN_GETDAYSTATE 通知即可。您可以创建自己的 TDateTimePicker 后代,也可以使用“拦截器类”。

    type
      TDateTimePicker = class(ComCtrls.TDateTimePicker)    
      protected
        procedure WndProc(var Message: TMessage); override;
      end;
    
      ...
    
    procedure TDateTimePicker.WndProc(var Message: TMessage);
    var
      i: integer;
    begin
      inherited;
      case Message.Msg of
        WM_NOTIFY:
          with PNMDayState(Message.LParam)^ do
            if nmhdr.code = MCN_GETDAYSTATE then
            begin
              // The first visible day is SystemTimeToDateTime(stStart);
              // cDayState is probably three, because most often three months are
              // visible at the same time. Of course, the second of these is the
              // 'currently displayed month'.
              // Each month is represented by a DWORD (32-bit unsigned integer)
              // bitfield, where 0 means not bold, and 1 means bold.
              // For instance, the following code will select all days:
              for i := 0 to cDayState - 1 do
                PMonthDayState(Cardinal(prgDayState) + i*sizeof(TMonthDayState))^ := $FFFFFFFF;
            end;
      end;
    end;
    

    另一个例子:假设当前显示由三个月组成,并且您只想选择“当前显示的月份”中的天,即中间月份。假设您希望每三天被选中一次,从选定的一天开始。

    然后你想使用位域

    Month  Bitfield
    0      00000000000000000000000000000000
    1      01001001001001001001001001001001
    2      00000000000000000000000000000000
    

    Month  Bitfield
    0      $00000000
    1      $49249249
    2      $00000000
    

    十六进制。所以你做

    for i := 0 to cDayState - 1 do
      if i = 1 then
        PMonthDayState(cardinal(prgDayState) + i*sizeof(TMonthDayState))^ := $49249249
      else
        PMonthDayState(cardinal(prgDayState) + i*sizeof(TMonthDayState))^ := $00000000;
    

    【讨论】:

    • 请注意,这仅适用于 Windows Vista 及更高版本,例如因为DTM_GETMCSTYLE 是在 Vista 中引入的。
    • 我认为操作系统分配并释放了prgDayState 缓冲区...文档在这一点上并不完全清楚。
    • VCL 的 OnGetMonthInfo / OnGetMonthBoldInfo 是否也仅适用于 Vista 及更高版本(即使用相同的 DTM_GETMCSTYLE / DTM_SETMCSTYLE 消息)?帮助中没有提到...
    • @ain,TMonthCalendar 拦截 MCN_GETDAYSTATE 并且至少适用于 XP 及更高版本,但此接口未在 windows DateTimePicker 中实现。当前的 Delphi XE 实现中未使用 DTM_GETMCSTYLE/DTM_SETMCSTYLE。
    • 现在为什么我还没走到最后就知道答案是安德烈亚斯?
    【解决方案2】:

    OnGetMonthInfo 事件查看 BoldDays 数组以标记一个月中的日子。

    摘自帮助:

    使用 BoldDays 对应以粗体显示的当前月份的天数进行编码。 BoldDays 返回的值可以分配给 OnGetMonthInfo 事件处理程序的 MonthBoldInfo 参数。 Days 是一个无符号整数数组,对应于应该加粗的日期。

    在帮助中查找。

    编辑:

    DateTimePicker.BoldDays 是可访问的。

    在 Delphi XE 中不推荐使用 OnGetMonthInfo,请改用 OnGetMonthBoldInfo。 仍然没有找到重新引入活动的最佳方式。

    编辑 2: 我确实尝试过重新介绍该事件,但似乎 Windows 消息未绑定在 TDateTimePicker 中。太糟糕了。 我猜 Andreas 直接使用 windows 消息 api 的解决方案是最好的。

    【讨论】:

    • 感谢您的回复;我真的很感激......你在想stackoverflow.com/questions/4223237/…这样的东西吗?
    • 这个答案适用于TMonthCalendar适用于TDateTimePicker
    • @Andreas,根据帮助,TCommonCalendar(OnGetMonthInfo所属的地方)是TDateTimePicker的继承成员。
    • OnGetMonthInfo 事件是在TCommonCalendar 中引入的,它是 TMonthCalendar 和 TDateTimePicker 的锚 - 也许它仍然可以与 TDateTimePicker 一起使用?虽然 TDateTimePicker 没有发布它......
    • 经过一番调查,似乎 OnGetMonthInfo 事件和 DTM_GETMCSTYLE 样式不适用于 Windows XP 中的 TDateTimePicker 控件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多