先来看看效果图
图中展现的是一个日历自定义控件,其显示格里高利历、农历及节气。
public partial class PanelDay: UserControl
{
public PanelDay()
{
InitializeComponent();
this.labelSolar.Enabled = false;
this.labelLunar.Enabled = false;
this.labelTerms.Enabled = false;
}
private string strSolar = "";
private string strLunar = "";
private string strTerms = "";
private MyDrawingMode myDrawingMode;
public string Solar
{
get { return strSolar; }
set
{
strSolar = value;
labelSolar.Text = strSolar;
}
}
public string Lunar
{
get { return strLunar; }
set
{
strLunar = value;
labelLunar.Text = strLunar;
}
}
public string Terms
{
get { return strTerms; }
set
{
strTerms = value;
labelTerms.Text = strTerms;
workTerms();
}
}
public enum MyDrawingMode
{
Default = 0,
Terms = 1
}
public MyDrawingMode DrawingMode
{
get { return myDrawingMode; }
set
{
myDrawingMode = value;
workDM();
}
}
private void workDM()
{
switch (myDrawingMode)
{
case MyDrawingMode.Default:
labelSolar.Location = new System.Drawing.Point(0, 0);
labelSolar.Size = new System.Drawing.Size(56, 40);
labelLunar.Location = new System.Drawing.Point(0, 40);
labelLunar.Size = new System.Drawing.Size(56, 20);
labelTerms.Location = new System.Drawing.Point(0, 45);
labelTerms.Size = new System.Drawing.Size(56, 15);
labelTerms.Visible = false;
break;
case MyDrawingMode.Terms:
labelSolar.Location = new System.Drawing.Point(0, 0);
labelSolar.Size = new System.Drawing.Size(56, 30);
labelLunar.Location = new System.Drawing.Point(0, 30);
labelLunar.Size = new System.Drawing.Size(56, 15);
labelTerms.Location = new System.Drawing.Point(0, 45);
labelTerms.Size = new System.Drawing.Size(56, 15);
labelTerms.Visible = true;
break;
default:
labelSolar.Location = new System.Drawing.Point(0, 0);
labelSolar.Size = new System.Drawing.Size(56, 40);
labelLunar.Location = new System.Drawing.Point(0, 40);
labelLunar.Size = new System.Drawing.Size(56, 20);
labelTerms.Location = new System.Drawing.Point(0, 45);
labelTerms.Size = new System.Drawing.Size(56, 15);
labelTerms.Visible = false;
break;
}
}
private void workTerms()
{
if (strTerms == string.Empty) myDrawingMode = MyDrawingMode.Default;
else myDrawingMode = MyDrawingMode.Terms;
workDM();
}
}
PanelDay为自定义控件,三个属性为格里历、农历和节气,都为字符串。两个绘图模板用枚举来表示。
PanelMonth由三个主要面板组成,即Search、Week、Wall;
代码如下:
public partial class PanelMonth : UserControl
{
public PanelMonth()
{
InitializeComponent();
DisplayPD(datetime);
}
private DateTime datetime=System.DateTime.Now;
public DateTime Datetime
{
get { return datetime; }
set
{
datetime = value;
dateTimePicker.Value = datetime;
}
}
public void Add(PanelDay pd,int x,int y)
{
pd.Location = new System.Drawing.Point(x * 56, y * 62);
panelWall.Controls.Add(pd);
}
public void Add(PanelDay pd)
{
panelWall.Controls.Add(pd);
}
public void DisplayPD(DateTime datetime)
{
panelWall.Controls.Clear();
DateTimeDS dt = new DateTimeDS();
int dim = dt.daysInMonth(dateTimePicker.Value.Year, dateTimePicker.Value.Month);
PanelDay[] panelday = new PanelDay[dim];
for (int d = 0; d < dim; d++)
{
panelday[d] = new PanelDay();
panelday[d].Name = "pd" + (d + 1).ToString();
panelday[d].Solar = (d + 1).ToString();
panelday[d].Lunar = dt.getLunarDay(dateTimePicker.Value.Year, dateTimePicker.Value.Month, d + 1);
panelday[d].MouseEnter += new EventHandler(PanelMonth_MouseEnter);
panelday[d].MouseLeave += new EventHandler(PanelMonth_MouseLeave);
panelday[d].MouseClick += PanelMonth_MouseClick;
panelday[d].Terms = dt.terms(new DateTime(dateTimePicker.Value.Year, dateTimePicker.Value.Month, d + 1));
if (datetime.Day == d + 1)
{
panelday[d].BackColor = Color.Green;
}
}
int index = 0;
DateTime newtime = dateTimePicker.Value;
DateTime firstdaytime = new DateTime(newtime.Year, newtime.Month, 1);
int firstday = (int)firstdaytime.DayOfWeek;
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 7; j++)
{
if (i == 0 && j < firstday) { }
else if (index < dim)
{
Add(panelday[index], j, i);
index++;
}
}
}
}
private void PanelMonth_MouseClick(object sender, EventArgs e)
{
PanelDay pd = (PanelDay)sender;
pd.BackColor = System.Drawing.Color.Green;
datetime = new DateTime(dateTimePicker.Value.Year, dateTimePicker.Value.Month, Convert.ToInt32(pd.Solar));
dateTimePicker.Value = datetime;
DisplayPD(datetime);
}
private void PanelMonth_MouseEnter(object sender, EventArgs e)
{
PanelDay pd = (PanelDay)sender;
if (dateTimePicker.Value.Year != datetime.Year || dateTimePicker.Value.Month != datetime.Month || dateTimePicker.Value.Day != Convert.ToInt32(pd.Solar))
{
pd.BackColor = System.Drawing.Color.DarkSeaGreen;
}
}
private void PanelMonth_MouseLeave(object sender, EventArgs e)
{
PanelDay pd = (PanelDay)sender;
if (dateTimePicker.Value.Year != datetime.Year || dateTimePicker.Value.Month != datetime.Month || dateTimePicker.Value.Day != Convert.ToInt32(pd.Solar))
{
pd.BackColor = System.Drawing.Color.Azure;
}
}
private void dateTimePicker_ValueChanged(object sender, EventArgs e)
{
datetime = dateTimePicker.Value;
DisplayPD(datetime);
}
}
PanelMonth主要区域有三个:
一为时间定义,输入日期,显示日历;
二为星期,只有显示的作用;
三为填充面板,显示动态创建的PanelDay。
PanelMonth自定义了一个属性就是Datetime,默认为当前时间,起选择的作用。
#region 根据年月获得当月天数
public int daysInMonth(int year, int month)
{
int days = 0;
switch (month)
{
case 1: case 3: case 5: case 7: case 8: case 10: case 12:days = 31;break;
case 4: case 6: case 9: case 11:days = 30;break;
case 2:
if ((year % 100 != 0 && year % 4 == 0) || (year % 400 == 0)) days = 29;
else days = 28;
break;
default:days = 0;break;
}
return days;
}
#endregion
这只是人为地创造出更符合纪日的方法,跟地理人文没啥关系。1582年还人为修改过,呵呵。
2. 农历添加
#region 根据日期获得节气
#region
public string getLunarDay(int year, int month, int day)
{
string[] lunarstr1 = { "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
string[] lunarstr2 = { "初", "十", "廿", "卅" };
string[] lunarmonthstr = { "正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月" };
ChineseLunisolarCalendar calendar = new ChineseLunisolarCalendar();
DateTime datetime = new DateTime(year, month, day);
int lunaryear = calendar.GetYear(datetime);
int lunarmonth = calendar.GetMonth(datetime);
int lunarday = calendar.GetDayOfMonth(datetime);
int leapmonth = calendar.GetLeapMonth(lunaryear);
if (leapmonth > 0)
{
if (leapmonth <= lunarmonth)
{
lunarmonth--;
}
}
if (lunarday == 1)
{
return lunarmonthstr[lunarmonth - 1].ToString();
}
else
{
if (lunarday > 1 && lunarday <= 10)
{
return lunarstr2[0].ToString() + lunarstr1[lunarday - 1].ToString();
}
else if (lunarday > 10 && lunarday < 20)
{
return lunarstr2[1].ToString() + lunarstr1[lunarday % 10 - 1].ToString();
}
else if (lunarday == 20)
{
return "二十";
}
else if (lunarday > 20 && lunarday < 30)
{
return lunarstr2[2].ToString() + lunarstr1[lunarday % 10 - 1].ToString();
}
else if (lunarday == 30)
{
return "三十";
}
else
{
return "卅一";
}
}
}
#endregion
.NET中有个ChineseLunisolarCalendar类,我们一定要尊重前人的劳动成果,我们现在所做的一切都是以踩在巨人的肩膀上为代价的。从中我们很轻易获取农历日期,我们只需改为汉字形式就行了。
3. 节气
#region 根据日期获得节气
public string terms(DateTime date)
{
string[] SolarTerm = new string[] { "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" };
int[] sTermInfo = new int[] { 0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758 };
DateTime baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM#
DateTime newDate;
double num;
int y;
string tempStr = "";
y = date.Year;
for (int i = 1; i <= 24; i++)
{
num = 525948.76 * (y - 1900) + sTermInfo[i - 1];
newDate = baseDateAndTime.AddMinutes(num);
if (newDate.DayOfYear == date.DayOfYear)
{
tempStr = SolarTerm[i - 1];
break;
}
}
return tempStr;
}
#endregion
节气本来可以分为二十四等份,如果地球对着太阳作匀速圆周运动的话。但是太阳只是在一个地球椭圆轨道的焦点上,这就造成了节气时长不等。如果只是这样的话还可以时长固定,但是地球的自转轴和地球椭圆轨道所在面并不垂直,地球每年西退,这种现象有专业名词表示,岁差。此外由于地球并不是非常接近于球形的,在自转的过程中会收到太阳和月亮的拉拉扯扯,这种现象也有专业名词表示,章动。所幸人们只可以活百岁,生活中不需要了解大周期中的小周期。某种情形下,暂时的可以为永久的,可以直接当做一个表来读取数据。