CnMonthCalendar.pas 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnMonthCalendar;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:CnPack 组件包界面组件库
  24. * 单元名称:中国月历组件,能显示农历与干支
  25. * 单元作者:匿名、不夜人
  26. * 备 注:字体尺寸随组件尺寸变化而变化
  27. * 开发平台:PWinXP SP2 + Delphi 2006
  28. * 兼容测试:PWin9X/2000/XP + Delphi 5/6
  29. * 本 地 化:该单元中的字符串均符合本地化处理方式
  30. * 单元标识:$Id$
  31. * 修改记录:2010.11.08 V1.2
  32. * 修正1582年10月显示不正确的问题
  33. * 2009.04.26 V1.1
  34. * 不夜人加入几种颜色以及前进后退年月的按钮,刘啸修改
  35. * 2008.06.05 V1.0
  36. * 移植单元
  37. ================================================================================
  38. |</PRE>}
  39. interface
  40. {$I CnPack.inc}
  41. uses
  42. SysUtils, Classes, Controls, Graphics, Windows, Messages,
  43. StdCtrls, CnCalendar;
  44. type
  45. TCnLunarDate = record //农历日期
  46. Year: Integer;
  47. Month: Integer;
  48. Day: Integer;
  49. IsLeap: Boolean; //闰月
  50. end;
  51. TCnGanZhiDate = record //干支日期
  52. Year: Integer;
  53. Month: Integer;
  54. Day: Integer;
  55. end;
  56. TCnMonthCalendar = class;
  57. TCnCalStyle = (csBottom, csRight, csNone);
  58. TCnCalColors = class(TPersistent)
  59. private
  60. Owner: TCnMonthCalendar;
  61. FBackColor: TColor;
  62. FTextColor: TColor;
  63. FTitleBackColor: TColor;
  64. FTitleTextColor: TColor;
  65. FTrailingTextColor: TColor;
  66. FSundayColor: TColor;
  67. FSaturdayColor: TColor;
  68. FWeekTextColor: TColor; //增加星期的字体颜色属性
  69. FDaySelectColor: TColor; //增加选择日期颜色属性
  70. FDaySelectTextColor: TColor; //增加选择日期字体颜色属性
  71. procedure SetColor(Index: Integer; Value: TColor);
  72. public
  73. constructor Create(AOwner: TCnMonthCalendar);
  74. procedure Assign(Source: TPersistent); override;
  75. published
  76. property BackColor: TColor index 0 read FBackColor write SetColor default clWindow;
  77. property TextColor: TColor index 1 read FTextColor write SetColor default clWindowText;
  78. property TitleBackColor: TColor index 2 read FTitleBackColor write SetColor default clActiveCaption;
  79. property TitleTextColor: TColor index 3 read FTitleTextColor write SetColor default clWhite;
  80. property TrailingTextColor: TColor index 4 read FTrailingTextColor write SetColor default clInactiveCaptionText;
  81. property SundayColor: TColor index 5 read FSundayColor write SetColor default clRed;
  82. property SaturdayColor: TColor index 6 read FSaturdayColor write SetColor default clMaroon;
  83. property WeekTextColor: TColor index 7 read FWeekTextColor write SetColor default clActiveCaption;
  84. //自己增加星期的字体颜色属性
  85. property DaySelectColor: TColor index 8 read FDaySelectColor write SetColor default clActiveCaption;
  86. //自己增加日期选择颜色属性
  87. property DaySelectTextColor: TColor index 9 read FDaySelectTextColor write SetColor default clWindowText;
  88. //自己增加日期选择字体颜色属性
  89. end;
  90. TCnMonthCalendar = class(TCustomControl)
  91. private
  92. FDate: TDate; //月历当前指向的日期
  93. FViewDate: TDate;
  94. FCalColors: TCnCalColors;
  95. FYear: word;
  96. FMonth: word;
  97. FDay: word;
  98. FFirstDate: TDate; //月历第一格日期
  99. FTitleRect: TRect; //标题区
  100. FWeekRect: TRect; //星期区
  101. FDaysRect: TRect; //日期区
  102. FOldRect: TRect;
  103. FNeedUpdate: Boolean;
  104. FCellWidth: Integer;
  105. FCellHeight: Integer;
  106. FShowGanZhi: Boolean; //是否使用干支记日
  107. FCalStyle: TCnCalStyle;
  108. FTitleTextSize: Integer;
  109. FWeekTextSize: Integer;
  110. FDaySize: Integer;
  111. FLunarDaySize: Integer;
  112. FOnChange: TNotifyEvent;
  113. lblPrevMonth: TLabel; //增加的动态建立一个Label
  114. lblNextMonth: TLabel; //增加的动态建立一个Label
  115. lblPrevYear: TLabel; //增加的动态建立一个Label
  116. lblNextYear: TLabel; //增加的动态建立一个Label
  117. LBTextSize: Integer;
  118. FShowMonthButton: Boolean;
  119. FShowYearButton: Boolean; //增加的动态建立一个label字体尺寸
  120. procedure CalcRect; //计算各区以及字体大小
  121. function CalcDayRect(ADate: TDate): TRect;
  122. procedure GetFirstDay;
  123. function GetMaxTextSize(S: string; W, H: Integer): Integer;
  124. procedure UpdateHighlight(X, Y: Integer);
  125. procedure SetDate(Value: TDate);
  126. procedure SetCalColors(Value: TCnCalColors);
  127. procedure SetCalStyle(Value: TCnCalStyle);
  128. procedure SetShowGanZhi(Value: Boolean);
  129. procedure PrevMonthClick(Sender: TObject); //增加的label单击事件
  130. procedure NextMonthClick(Sender: TObject);
  131. procedure PrevYearClick(Sender: TObject); //增加的label单击事件
  132. procedure NextYearClick(Sender: TObject);
  133. procedure SetShowMonthButton(const Value: Boolean);
  134. procedure SetShowYearButton(const Value: Boolean); //增加的label单击事件
  135. protected
  136. { Protected declarations }
  137. procedure CreateWnd; override;
  138. procedure Paint; override;
  139. procedure Resize; override;
  140. procedure KeyDown(var Key: word; Shift: TShiftState); override;
  141. procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  142. procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  143. procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  144. procedure DoEnter; override;
  145. procedure DoExit; override;
  146. procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
  147. procedure Changed; dynamic;
  148. public
  149. constructor Create(AOwner: TComponent); override;
  150. destructor Destroy; override;
  151. //算出农历, 传入公历日期, 返回农历日期
  152. function ToLunar(TheDate: TDate): TCnLunarDate;
  153. //求年柱,月柱,日柱TheDate为当天的公历日期)
  154. function GetGanZhi(TheDate: TDate): TCnGanZhiDate;
  155. //取汉字日期
  156. function FormatLunarDay(Day: Integer): string;
  157. //汉字月份
  158. function FormatLunarMonth(Month: Integer; isLeap: Boolean): string;
  159. //汉字年份
  160. function FormatLunarYear(Year: Integer): string;
  161. // 取得指定日期的节气
  162. function GetJieQi(TheDate: TDate): string;
  163. property Year: word read FYear;
  164. property Month: word read FMonth;
  165. property Day: word read FDay;
  166. procedure PriorYear;
  167. procedure NextYear;
  168. procedure PriorMonth;
  169. procedure NextMonth;
  170. procedure PriorDay;
  171. procedure NextDay;
  172. procedure PriorWeek;
  173. procedure NextWeek;
  174. procedure FirstDayOfMonth;
  175. procedure LastDayOfMonth;
  176. published
  177. { Published declarations }
  178. property Align;
  179. property Anchors;
  180. property BevelEdges;
  181. property BevelInner;
  182. property BevelOuter;
  183. property BevelKind;
  184. property BevelWidth;
  185. property BorderWidth;
  186. property CalColors: TCnCalColors read FCalColors write SetCalColors;
  187. property CalStyle: TCnCalStyle read FCalStyle write SetCalStyle default csBottom;
  188. property ShowGanZhi: Boolean read FShowGanZhi write SetShowGanZhi default False;
  189. property ShowMonthButton: Boolean read FShowMonthButton write SetShowMonthButton;
  190. property ShowYearButton: Boolean read FShowYearButton write SetShowYearButton;
  191. property Cursor;
  192. property Date: TDate read FDate write SetDate;
  193. property Enabled;
  194. property Font;
  195. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  196. property PopupMenu;
  197. property ShowHint;
  198. property TabOrder;
  199. property Visible;
  200. property OnClick;
  201. property OnContextPopup;
  202. property OnDblClick;
  203. property OnKeyDown;
  204. property OnKeyUp;
  205. property OnKeyPress;
  206. property OnMouseDown;
  207. property OnMouseMove;
  208. property OnMouseUp;
  209. end;
  210. implementation
  211. const
  212. LunarStrs: array[0..10] of string =
  213. ('日', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十');
  214. constructor TCnMonthCalendar.Create(AOwner: TComponent);
  215. procedure InitLabel(ALabel: TLabel);
  216. begin
  217. ALabel.Parent := Self;
  218. ALabel.Visible := False;
  219. ALabel.Left := 10;
  220. ALabel.Top := 20;
  221. ALabel.Transparent := True;
  222. ALabel.Font.Size := 12;
  223. ALabel.Font.Style := [fsBold];
  224. end;
  225. begin
  226. inherited;
  227. DoubleBuffered := True;
  228. ControlStyle := (ControlStyle - [csAcceptsControls, csNoStdEvents, csSetCaption]) + [csReflector];
  229. FDate := SysUtils.Date;
  230. FViewDate := FDate;
  231. FCalColors := TCnCalColors.Create(self);
  232. GetFirstDay;
  233. Width := 360;
  234. Height := 240;
  235. Font.Name := '宋体';
  236. Font.Charset := GB2312_CHARSET;
  237. Font.Size := 9;
  238. TabStop := True;
  239. Color := FCalColors.BackColor;
  240. FShowGanZhi := False;
  241. //DoubleBuffered := False;
  242. lblPrevMonth := TLabel.Create(Self);
  243. InitLabel(lblPrevMonth);
  244. lblPrevMonth.Caption := '<';
  245. lblPrevMonth.OnClick := PrevMonthClick;
  246. lblNextMonth := TLabel.Create(Self);
  247. InitLabel(lblNextMonth);
  248. lblNextMonth.Caption := '>';
  249. lblNextMonth.OnClick := NextMonthClick;
  250. lblPrevYear := TLabel.Create(Self);
  251. InitLabel(lblPrevYear);
  252. lblPrevYear.Caption := '<<';
  253. lblPrevYear.OnClick := PrevYearClick;
  254. lblNextYear := TLabel.Create(Self);
  255. InitLabel(lblNextYear);
  256. lblNextYear.Caption := '>>';
  257. lblNextYear.OnClick := NextYearClick;
  258. end;
  259. procedure TCnMonthCalendar.PrevMonthClick(Sender: TObject);
  260. begin
  261. PriorMonth;
  262. end;
  263. procedure TCnMonthCalendar.NextMonthClick(Sender: TObject);
  264. begin
  265. NextMonth;
  266. end;
  267. destructor TCnMonthCalendar.Destroy;
  268. begin
  269. FCalColors.Free;
  270. lblPrevMonth.Free; //增加的释放动态建立的label
  271. lblNextMonth.Free; //增加的释放动态建立的label
  272. lblPrevYear.Free;
  273. lblNextYear.Free;
  274. inherited Destroy;
  275. end;
  276. procedure TCnMonthCalendar.CreateWnd;
  277. begin
  278. inherited;
  279. CalcRect;
  280. Color := FCalColors.BackColor;
  281. end;
  282. procedure TCnMonthCalendar.Paint;
  283. var
  284. OutputStr: string;
  285. Col, I, Skip: Integer;
  286. TempDate: TDate;
  287. R, DR: TRect;
  288. Y, M, D: word;
  289. GzDate: TCnGanZhiDate;
  290. procedure DrawString(const S: string; Bounds: TRect; Flag: Cardinal);
  291. var
  292. TextSize: TSize;
  293. StartPos: TPoint;
  294. begin
  295. TextSize := Canvas.TextExtent(S);
  296. StartPos := Bounds.TopLeft;
  297. with StartPos, Bounds, TextSize do
  298. begin
  299. if (DT_CENTER and Flag) = DT_CENTER then X := X + (Right - Left - cx) div 2
  300. else if (DT_RIGHT and Flag) = DT_RIGHT then X := X + (Right - Left - cx);
  301. if (DT_VCENTER and Flag) = DT_VCENTER then Y := Y + (Bottom - Top - cy) div 2
  302. else if (DT_BOTTOM and Flag) = DT_BOTTOM then Y := Y + (Bottom - Top - cy);
  303. Canvas.TextOut(X, Y, S);
  304. end;
  305. end;
  306. procedure DrawLunarDay(R: TRect; TheDate: TDate);
  307. var
  308. S, S1: string;
  309. LunarDate: TCnLunarDate;
  310. H, yy: Integer;
  311. begin
  312. S := GetJieQi(TheDate);
  313. if S = '' then
  314. if FShowGanZhi then
  315. begin
  316. GzDate := GetGanZhi(TheDate);
  317. S := GetGanZhiFromNumber(GzDate.Day);
  318. end
  319. else
  320. begin
  321. LunarDate := ToLunar(TheDate);
  322. if LunarDate.Day = 0 then Exit;
  323. if LunarDate.Day = 1 then
  324. S := FormatLunarMonth(LunarDate.Month, LunarDate.isLeap)
  325. else
  326. S := FormatLunarDay(LunarDate.Day);
  327. end;
  328. Canvas.Font.Size := FLunarDaySize;
  329. if FCalStyle = csRight then
  330. begin
  331. H := Canvas.TextHeight(S);
  332. yy := R.Top + (FCellHeight div 2) - H;
  333. S1 := Copy(S, 1, 2);
  334. Canvas.TextOut(R.Left + 2, yy, S1);
  335. yy := yy + H;
  336. S1 := Copy(S, 3, 2);
  337. Canvas.TextOut(R.Left + 2, yy, S1);
  338. end
  339. else
  340. DrawString(S, R, DT_TOP or DT_CENTER);
  341. end;
  342. begin
  343. inherited;
  344. Canvas.Font.Assign(Font);
  345. with Canvas, FCalColors do
  346. begin
  347. // 画年月
  348. if ShowYearButton then
  349. begin
  350. lblPrevYear.Font.Size := LBTextSize;
  351. lblPrevYear.Font.Color := TitleTextColor;
  352. lblNextYear.Font.Size := LBTextSize;
  353. lblNextYear.Font.Color := TitleTextColor;
  354. end;
  355. if ShowMonthButton then
  356. begin
  357. lblPrevMonth.Font.Size := LBTextSize;
  358. lblPrevMonth.Font.Color := TitleTextColor;
  359. lblNextMonth.Font.Size := LBTextSize;
  360. lblNextMonth.Font.Color := TitleTextColor;
  361. end;
  362. if RectVisible(Canvas.Handle, FTitleRect) then
  363. begin
  364. Brush.Color := TitleBackColor;
  365. Brush.Style := bsSolid;
  366. FillRect(FTitleRect);
  367. Brush.Style := bsClear;
  368. Font.Color := TitleTextColor;
  369. Font.Size := FTitleTextSize;
  370. Font.Style := [fsBold];
  371. if FShowGanZhi then
  372. begin
  373. GzDate := GetGanZhi(FDate);
  374. OutputStr := GetGanZhiFromNumber(GzDate.Year) + '(' + GetShengXiaoFromNumber(GzDate.Year mod 12) +
  375. ')年' + GetGanZhiFromNumber(GzDate.Month) + '月';
  376. end
  377. else
  378. OutputStr := FormatDateTime('yyyy', FDate) + '年' + FormatDateTime('m', FDate) + '月';
  379. DrawString(OutputStr, FTitleRect, DT_CENTER or DT_VCENTER);
  380. Font.Style := [];
  381. end;
  382. //画星期
  383. R := Bounds(FWeekRect.Left, FWeekRect.Top, FCellWidth, FCellHeight);
  384. if RectVisible(Canvas.Handle, FWeekRect) then
  385. begin
  386. Font.Size := FWeekTextSize;
  387. Font.Color := WeekTextColor; //增加上的,改变星期头字体颜色
  388. for I := 0 to 6 do
  389. begin
  390. OutputStr := GetWeekFromNumber(GetWeek(FFirstDate + I));
  391. DrawString(OutputStr, R, DT_CENTER or DT_VCENTER);
  392. OffsetRect(R, FCellWidth, 0);
  393. end;
  394. Pen.Color := TitleBackColor;
  395. Pen.Width := 1;
  396. Pen.Mode := pmCopy;
  397. PenPos := Point(2, FWeekRect.Bottom - 2);
  398. LineTo(FWeekRect.Right - 2, FWeekRect.Bottom - 2);
  399. end;
  400. //画日期
  401. R := Bounds(FDaysRect.Left, FDaysRect.Top, FCellWidth, FCellHeight);
  402. Skip := 0;
  403. for I := 0 to 41 do
  404. begin
  405. Col := (I - Skip) mod 7;
  406. //if RectVisible(Canvas.Handle, R) then // NOTE: NEVER!
  407. TempDate := FFirstDate + I;
  408. DecodeDate(TempDate, Y, M, D);
  409. if (Y = 1582) and (M = 10) and (D in [5..14]) then
  410. begin
  411. Inc(Skip);
  412. Continue;
  413. end;
  414. if M = FMonth then
  415. if Col = 0 then
  416. Font.Color := SundayColor
  417. else if Col = 6 then
  418. Font.Color := SaturdayColor
  419. else
  420. Font.Color := TextColor
  421. else
  422. Font.Color := TrailingTextColor;
  423. if Trunc(TempDate) = Trunc(FViewDate) then //高亮显示月历日期
  424. begin
  425. Brush.Color := DaySelectColor; // 增加的颜色设置
  426. Font.Color := DaySelectTextColor; // 增加的颜色设置
  427. FillRect(R);
  428. FOldRect := R;
  429. DR := R;
  430. InflateRect(DR, -2, -2);
  431. if Focused then
  432. Windows.DrawFocusRect(Handle, DR);
  433. end
  434. else
  435. begin
  436. Brush.Color := Color;
  437. Brush.Style := bsSolid;
  438. FillRect(R);
  439. end;
  440. Brush.Style := bsClear;
  441. if TempDate = SysUtils.Date then //在当前日期画一红色框
  442. begin
  443. Pen.Color := clRed;
  444. Pen.Width := 1;
  445. Rectangle(R.Left, R.Top, R.Right, R.Bottom);
  446. end;
  447. OutputStr := IntToStr(D);
  448. Font.Size := FDaySize;
  449. if FCalStyle = csNone then
  450. DrawString(OutputStr, R, DT_VCENTER or DT_CENTER)
  451. else
  452. begin
  453. if FCalStyle = csRight then
  454. begin
  455. DR := Bounds(R.Left, R.Top, FCellWidth div 3 * 2, FCellHeight);
  456. DrawString(OutputStr, DR, DT_VCENTER or DT_RIGHT);
  457. OffsetRect(DR, FCellWidth div 3 * 2, 0);
  458. end
  459. else
  460. begin
  461. DR := Bounds(R.Left, R.Top, FCellWidth, FCellHeight div 5 * 3);
  462. DrawString(OutputStr, DR, DT_BOTTOM or DT_CENTER);
  463. DR := Bounds(DR.Left, DR.Bottom, FCellWidth, FCellHeight div 5 * 2);
  464. end;
  465. DrawLunarDay(DR, TempDate);
  466. end;
  467. if Col = 6 then
  468. OffsetRect(R, FDaysRect.Left - R.Left, FCellHeight)
  469. else
  470. OffsetRect(R, FCellWidth, 0);
  471. end; {end for}
  472. end; {end with}
  473. end;
  474. procedure TCnMonthCalendar.Resize;
  475. begin
  476. inherited;
  477. CalcRect;
  478. end;
  479. procedure TCnMonthCalendar.CalcRect;
  480. begin
  481. Canvas.Font.Assign(Font);
  482. FCellWidth := ClientRect.Right div 7;
  483. FCellHeight := ClientRect.Bottom div 8;
  484. FTitleRect := ClientRect;
  485. FTitleRect.Bottom := FCellHeight;
  486. FTitleTextSize := GetMaxTextSize(FormatDateTime('yyyy年mm月', FDate), FTitleRect.Right, Round((FTitleRect.Bottom - FTitleRect.Top) * 0.8));
  487. LBTextSize := Round(FTitleTextSize * 0.8); //增加的label字体尺寸
  488. FWeekTextSize := GetMaxTextSize(FormatDateTime('ddd', FDate), Round(FCellWidth * 1.2), FCellHeight); //这是修改后的字体(加大了!)
  489. if FCalStyle = csNone then
  490. FDaySize := GetMaxTextSize(FormatDateTime('dd', FDate), FCellWidth, FCellHeight)
  491. else begin
  492. if FCalStyle = csRight then
  493. begin
  494. FDaySize := GetMaxTextSize(FormatDateTime('dd', FDate), FCellWidth div 3 * 2, FCellHeight);
  495. FLunarDaySize := GetMaxTextSize('九', FCellWidth div 3, FCellHeight div 2);
  496. end
  497. else begin
  498. FDaySize := GetMaxTextSize(FormatDateTime('dd', FDate), FCellWidth, FCellHeight div 5 * 3);
  499. FLunarDaySize := GetMaxTextSize('九九', FCellWidth, FCellHeight div 5 * 2);
  500. end;
  501. end;
  502. FWeekRect := Bounds(0, FTitleRect.Bottom, FCellWidth * 7, FCellHeight);
  503. FDaysRect := Bounds(0, FWeekRect.Bottom, FCellWidth * 7, FCellHeight * 6);
  504. lblPrevMonth.Font.Size := LBTextSize;
  505. lblPrevMonth.Font.Color := FCalColors.TitleTextColor;
  506. lblPrevMonth.Left := 40;
  507. lblPrevMonth.Top := Round((FTitleRect.Bottom - lblPrevMonth.Height) / 2);
  508. lblNextMonth.Font.Size := LBTextSize;
  509. lblNextMonth.Font.Color := FCalColors.TitleTextColor;
  510. lblNextMonth.Left := FTitleRect.Right - 30 - Round(LBTextSize * 1.2);
  511. lblNextMonth.Top := lblPrevMonth.Top;
  512. lblPrevYear.Font.Size := LBTextSize;
  513. lblPrevYear.Font.Color := FCalColors.TitleTextColor;
  514. lblPrevYear.Left := 10;
  515. lblPrevYear.Top := Round((FTitleRect.Bottom - lblPrevYear.Height) / 2);
  516. lblNextYear.Font.Size := LBTextSize;
  517. lblNextYear.Font.Color := FCalColors.TitleTextColor;
  518. lblNextYear.Left := FTitleRect.Right - 10 - Round(LBTextSize * 1.2);
  519. lblNextYear.Top := lblPrevYear.Top;
  520. end;
  521. function TCnMonthCalendar.CalcDayRect(ADate: TDate): TRect;
  522. var
  523. DateOffset: Integer;
  524. col, Row: Integer;
  525. begin
  526. DateOffset := Trunc(Abs(ADate - FFirstDate));
  527. Row := DateOffset div 7;
  528. col := DateOffset mod 7;
  529. Result.Left := FDaysRect.Left + FCellWidth * col;
  530. Result.Top := FDaysRect.Top + FCellHeight * Row;
  531. Result.Right := Result.Left + FCellWidth;
  532. Result.Bottom := Result.Top + FCellHeight;
  533. end;
  534. function TCnMonthCalendar.GetMaxTextSize(S: string; W, H: Integer): Integer;
  535. var
  536. n: Integer;
  537. TextSize: TSize;
  538. begin
  539. for n := 5 to 72 do
  540. begin
  541. Canvas.Font.Size := n;
  542. TextSize := Canvas.TextExtent(S);
  543. if (TextSize.cx > W) or (TextSize.cy > H) then Break;
  544. end;
  545. Result := n - 1;
  546. end;
  547. procedure TCnMonthCalendar.GetFirstDay;
  548. var
  549. DayOffSet: Integer;
  550. begin
  551. DecodeDate(FDate, FYear, FMonth, FDay);
  552. FFirstDate := EncodeDate(FYear, FMonth, 1);
  553. DayOffSet := GetWeek(FFirstDate) + 1;
  554. if DayOffSet = 1 then DayOffSet := 8; //保证前面包含上月数据
  555. FFirstDate := FFirstDate + 1 - DayOffSet;
  556. end;
  557. procedure TCnMonthCalendar.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  558. begin
  559. inherited;
  560. if not (csDesigning in ComponentState) then SetFocus;
  561. if Button = mbLeft then
  562. begin
  563. UpdateHighlight(X, Y);
  564. end;
  565. end;
  566. procedure TCnMonthCalendar.MouseMove(Shift: TShiftState; X, Y: Integer);
  567. begin
  568. inherited;
  569. if ssLeft in Shift then
  570. UpdateHighlight(X, Y);
  571. end;
  572. procedure TCnMonthCalendar.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  573. begin
  574. inherited;
  575. if Button = mbLeft then
  576. begin
  577. FNeedUpdate := False;
  578. SetDate(FViewDate);
  579. end;
  580. end;
  581. procedure TCnMonthCalendar.CMWantSpecialKey(var Message: TCMWantSpecialKey);
  582. begin
  583. inherited;
  584. if Message.CharCode in [vk_Left..vk_Down] then Message.Result := 1;
  585. end;
  586. procedure TCnMonthCalendar.KeyDown(var Key: word; Shift: TShiftState);
  587. var
  588. D, m, Y: word;
  589. begin
  590. inherited;
  591. if Shift = [] then
  592. begin
  593. FNeedUpdate := True;
  594. case Key of
  595. vk_Up: SetDate(FDate - 7);
  596. vk_Down: SetDate(FDate + 7);
  597. vk_Left: SetDate(FDate - 1);
  598. vk_Right: SetDate(FDate + 1);
  599. vk_Home: begin
  600. DecodeDate(FDate, Y, m, D);
  601. SetDate(EncodeDate(Y, m, 1));
  602. end;
  603. vk_End: begin
  604. DecodeDate(IncMonth(FDate, 1), Y, m, D);
  605. SetDate(EncodeDate(Y, m, 1) - 1);
  606. end;
  607. vk_Prior: SetDate(IncMonth(FDate, -1));
  608. vk_Next: SetDate(IncMonth(FDate, 1));
  609. end;
  610. if Key = vk_Return then
  611. inherited Click;
  612. end;
  613. end; {= TCnMonthCalendar.KeyDown =}
  614. procedure TCnMonthCalendar.UpdateHighlight(X, Y: Integer);
  615. var
  616. Col, Row: Integer;
  617. TempDate: TDate;
  618. R: TRect;
  619. Ye, M, D: Word;
  620. begin
  621. if PtInRect(FDaysRect, Point(X, Y)) then
  622. begin
  623. Col := X div FCellWidth;
  624. Row := (Y - FDaysRect.Top) div FCellHeight;
  625. TempDate := FFirstDate + Col + Row * 7;
  626. DecodeDate(TempDate, Ye, M, D);
  627. if (Ye = 1582) and (M = 10) and (D in [5..31]) then
  628. begin
  629. DecodeDate(FViewDate, Ye, M, D);
  630. if M = 10 then
  631. TempDate := TempDate + 10;
  632. end;
  633. if TempDate <> FViewDate then
  634. begin
  635. R := Bounds(FDaysRect.Left + FCellWidth * Col + 1,
  636. FDaysRect.Top + FCellHeight * Row + 1, FCellWidth - 2, FCellHeight - 2);
  637. FViewDate := TempDate;
  638. InvalidateRect(Handle, @FOldRect, False);
  639. InvalidateRect(Handle, @R, False);
  640. end;
  641. end;
  642. end;
  643. procedure TCnMonthCalendar.DoEnter;
  644. begin
  645. inherited;
  646. with Canvas, FCalColors do
  647. begin
  648. Brush.Style := bsSolid;
  649. Brush.Color := TitleBackColor;
  650. Font.Color := TitleTextColor;
  651. Windows.DrawFocusRect(Handle, FOldRect);
  652. end;
  653. end;
  654. procedure TCnMonthCalendar.DoExit;
  655. begin
  656. inherited;
  657. with Canvas, FCalColors do
  658. begin
  659. Brush.Style := bsSolid;
  660. Brush.Color := TitleBackColor;
  661. Font.Color := TitleTextColor;
  662. Windows.DrawFocusRect(Handle, FOldRect);
  663. end;
  664. end;
  665. procedure TCnMonthCalendar.SetCalColors(Value: TCnCalColors);
  666. begin
  667. if FCalColors <> Value then FCalColors.Assign(Value);
  668. end;
  669. procedure TCnMonthCalendar.SetCalStyle(Value: TCnCalStyle);
  670. begin
  671. if FCalStyle <> Value then
  672. begin
  673. FCalStyle := Value;
  674. CalcRect;
  675. Invalidate;
  676. end;
  677. end;
  678. procedure TCnMonthCalendar.SetDate(Value: TDate);
  679. var
  680. oldFirstDate: TDate;
  681. R: TRect;
  682. begin
  683. if (FDate <> Trunc(Value)) then
  684. begin
  685. FDate := Value;
  686. FViewDate := FDate;
  687. oldFirstDate := FFirstDate;
  688. GetFirstDay;
  689. Changed;
  690. FNeedUpdate := True;
  691. if oldFirstDate <> FFirstDate then
  692. begin
  693. InvalidateRect(Handle, @FTitleRect, False);
  694. InvalidateRect(Handle, @FDaysRect, False);
  695. end
  696. else if FNeedUpdate then
  697. begin
  698. InvalidateRect(Handle, @FOldRect, False);
  699. R := CalcDayRect(FViewDate);
  700. InvalidateRect(Handle, @R, False);
  701. end
  702. else if FShowGanZhi then
  703. begin
  704. InvalidateRect(Handle, @FTitleRect, False);
  705. end;
  706. end;
  707. end; {= TCnMonthCalendar.SetDate =}
  708. procedure TCnMonthCalendar.Changed;
  709. begin
  710. if Assigned(FOnChange) then
  711. FOnChange(self);
  712. end;
  713. //算出农历, 传入公历日期, 返回农历日期
  714. function TCnMonthCalendar.ToLunar(TheDate: TDate): TCnLunarDate;
  715. var
  716. Y, M, D: Word;
  717. begin
  718. DecodeDate(TheDate, Y, M, D);
  719. GetLunarFromDay(Y, M, D, Result.Year, Result.Month, Result.Day, Result.IsLeap);
  720. Result.Year := Y;
  721. end;
  722. // 求年柱,月柱,日柱, TheDate 为公历日期
  723. function TCnMonthCalendar.GetGanZhi(TheDate: TDate): TCnGanZhiDate;
  724. var
  725. Y, M, D: Word;
  726. begin
  727. DecodeDate(TheDate, Y, M, D);
  728. Result.Year := GetGanZhiFromYear(Y, M, D);
  729. Result.Month := GetGanZhiFromMonth(Y, M, D);
  730. Result.Day := GetGanZhiFromDay(Y, M, D);
  731. end;
  732. function TCnMonthCalendar.FormatLunarDay(Day: Integer): string;
  733. begin
  734. case Day of
  735. 1..10: Result := SCnLunarNumber2Array[0] + LunarStrs[Day];
  736. 11..19: Result := SCnLunarNumber2Array[1] + LunarStrs[Day - 10];
  737. 20: Result := LunarStrs[2] + LunarStrs[10];
  738. 21..29: Result := SCnLunarNumber2Array[2] + LunarStrs[Day - 20];
  739. 30: Result := LunarStrs[3] + LunarStrs[10];
  740. else Result := '';
  741. end;
  742. end;
  743. function TCnMonthCalendar.FormatLunarMonth(Month: Integer; isLeap: Boolean): string;
  744. begin
  745. case Month of
  746. 1..10: Result := LunarStrs[Month];
  747. 11..12: Result := LunarStrs[10] + LunarStrs[Month - 10];
  748. else Result := '';
  749. end;
  750. if isLeap then Result := '闰' + Result;
  751. Result := Result + '月';
  752. end;
  753. function TCnMonthCalendar.FormatLunarYear(Year: Integer): string;
  754. var
  755. temp: Integer;
  756. zero: string;
  757. begin
  758. zero := '零';
  759. temp := Year div 1000;
  760. Result := LunarStrs[temp];
  761. Year := Year - temp * 1000;
  762. if Year >= 100 then
  763. begin
  764. temp := Year div 100;
  765. Result := Result + LunarStrs[temp];
  766. Year := Year - temp * 100;
  767. end
  768. else
  769. Result := Result + zero;
  770. if Year >= 10 then
  771. begin
  772. temp := Year div 10;
  773. Result := Result + LunarStrs[temp];
  774. Year := Year - temp * 10;
  775. end
  776. else
  777. Result := Result + zero;
  778. if Year = 0 then Result := Result + zero else
  779. Result := Result + LunarStrs[Year];
  780. Result := Result + '年';
  781. end;
  782. // 取得指定日期的节气
  783. function TCnMonthCalendar.GetJieQi(TheDate: TDate): string;
  784. var
  785. Y, M, D: Word;
  786. J: Integer;
  787. begin
  788. Result := '';
  789. DecodeDate(TheDate, Y, M, D);
  790. J := GetJieQiFromDay(Y, M, D);
  791. if J <> -1 then
  792. Result := SCnJieQiArray[J];
  793. end;
  794. { TCnCalColors }
  795. constructor TCnCalColors.Create(AOwner: TCnMonthCalendar);
  796. begin
  797. Owner := AOwner;
  798. FBackColor := clWindow;
  799. FTextColor := clWindowText;
  800. FTitleBackColor := clActiveCaption;
  801. FTitleTextColor := clWhite;
  802. FTrailingTextColor := clInactiveCaptionText;
  803. FSundayColor := clRed;
  804. FSaturdayColor := clMaroon;
  805. FWeekTextColor := clActiveCaption; // 增加星期字头颜色
  806. FDaySelectColor := clActiveCaption; // 增加选择日期的填充颜色颜色
  807. FDaySelectTextColor := clWhite; // 增加选择日期字体的填充颜色颜色
  808. end;
  809. procedure TCnCalColors.SetColor(Index: Integer; Value: TColor);
  810. begin
  811. case Index of
  812. 0: FBackColor := Value;
  813. 1: FTextColor := Value;
  814. 2: FTitleBackColor := Value;
  815. 3: FTitleTextColor := Value;
  816. 4: FTrailingTextColor := Value;
  817. 5: FSundayColor := Value;
  818. 6: FSaturdayColor := Value;
  819. 7: FWeekTextColor := Value; // 增加星期字头字头颜色
  820. 8: FDaySelectColor := Value; // 增加选择日期的填充颜色颜色
  821. 9: FDaySelectTextColor := Value; // 增加选择日期字体的填充颜色颜色
  822. end;
  823. if Owner.HandleAllocated then
  824. begin
  825. Owner.Color := FBackColor;
  826. Owner.Invalidate;
  827. end;
  828. end;
  829. procedure TCnCalColors.Assign(Source: TPersistent);
  830. begin
  831. if (Source = nil) or not (Source is TCnCalColors) then Exit;
  832. FBackColor := TCnCalColors(Source).BackColor;
  833. FTextColor := TCnCalColors(Source).TextColor;
  834. FTitleBackColor := TCnCalColors(Source).TitleBackColor;
  835. FTitleTextColor := TCnCalColors(Source).TitleTextColor;
  836. FTrailingTextColor := TCnCalColors(Source).TrailingTextColor;
  837. FSundayColor := TCnCalColors(Source).SundayColor;
  838. FSaturdayColor := TCnCalColors(Source).SaturdayColor;
  839. FWeekTextColor := TCnCalColors(Source).WeekTextColor; // 增加星期字头字头颜色
  840. FDaySelectColor := TCnCalColors(Source).DaySelectColor;
  841. FDaySelectTextColor := TCnCalColors(Source).DaySelectTextColor;
  842. end;
  843. procedure TCnMonthCalendar.SetShowGanZhi(Value: Boolean);
  844. begin
  845. if Value <> FShowGanZhi then
  846. begin
  847. FShowGanZhi := Value;
  848. Invalidate;
  849. end;
  850. end;
  851. procedure TCnMonthCalendar.FirstDayOfMonth;
  852. var
  853. D, m, Y: word;
  854. begin
  855. FNeedUpdate := True;
  856. DecodeDate(FDate, Y, m, D);
  857. SetDate(EncodeDate(Y, m, 1));
  858. end;
  859. procedure TCnMonthCalendar.LastDayOfMonth;
  860. var
  861. D, m, Y: word;
  862. begin
  863. FNeedUpdate := True;
  864. DecodeDate(IncMonth(FDate, 1), Y, m, D);
  865. SetDate(EncodeDate(Y, m, 1) - 1);
  866. end;
  867. procedure TCnMonthCalendar.NextDay;
  868. begin
  869. FNeedUpdate := True;
  870. SetDate(FDate + 1);
  871. end;
  872. procedure TCnMonthCalendar.NextMonth;
  873. begin
  874. FNeedUpdate := True;
  875. SetDate(IncMonth(FDate, 1));
  876. end;
  877. procedure TCnMonthCalendar.NextYear;
  878. begin
  879. FNeedUpdate := True;
  880. SetDate(IncMonth(FDate, 12));
  881. end;
  882. procedure TCnMonthCalendar.PriorDay;
  883. begin
  884. FNeedUpdate := True;
  885. SetDate(FDate - 1);
  886. end;
  887. procedure TCnMonthCalendar.PriorYear;
  888. begin
  889. FNeedUpdate := True;
  890. SetDate(IncMonth(FDate, -12));
  891. end;
  892. procedure TCnMonthCalendar.PriorMonth;
  893. begin
  894. FNeedUpdate := True;
  895. SetDate(IncMonth(FDate, -1));
  896. end;
  897. procedure TCnMonthCalendar.NextWeek;
  898. begin
  899. FNeedUpdate := True;
  900. SetDate(FDate + 7);
  901. end;
  902. procedure TCnMonthCalendar.PriorWeek;
  903. begin
  904. FNeedUpdate := True;
  905. SetDate(FDate - 7);
  906. end;
  907. procedure TCnMonthCalendar.SetShowMonthButton(const Value: Boolean);
  908. begin
  909. FShowMonthButton := Value;
  910. lblPrevMonth.Visible := Value;
  911. lblNextMonth.Visible := Value;
  912. end;
  913. procedure TCnMonthCalendar.SetShowYearButton(const Value: Boolean);
  914. begin
  915. FShowYearButton := Value;
  916. lblNextYear.Visible := Value;
  917. lblPrevYear.Visible := Value;
  918. end;
  919. procedure TCnMonthCalendar.NextYearClick(Sender: TObject);
  920. begin
  921. NextYear;
  922. end;
  923. procedure TCnMonthCalendar.PrevYearClick(Sender: TObject);
  924. begin
  925. PriorYear;
  926. end;
  927. end.