点击下载工程
其中FinalTest.aspx为测试页面

using System;
using System.Text;
using System.ComponentModel;
using System.Web.UI;
using System.Collections;

/*
 * 此控件已经完成自身所带的功能 类似时间选择应该属于别的组件所负责的工作职责,而非把所有的
 * 功能都堆砌成一个控件,应该模块化 细化。
 * 2007-6-4 21:40开始修改控件
 * 
 * 1、跟blog上的旧代码比较被错误更改的的代码2处,
 * 2、处理造成脚本错误的代码,
 * 3、把时间选择的功能交给用户控件来处理
 * 22:20分出去喝了杯凉茶
 * 添加2个月份为一行的功能
 * 在2005下完成所有的功能,并转化在2003下
 * 
 * 至23:33完成全部功能,并上传到web上
 * 
 * 
 * 
*/


namespace Controls
{
    
public class DateControl : System.Web.UI.WebControls.WebControl, System.Web.UI.INamingContainer, System.Web.UI.IPostBackEventHandler
    
{

       
//private const int PerRow=3;

        
public int PerRow
        
{
            
get
            
{
                
if(ViewState["PerRow"]==null)
                    ViewState[
"PerRow"]="3";
                
return Convert.ToInt32(ViewState["PerRow"]);
            }

            
set
            
{
                ViewState[
"PerRow"]=value.ToString();
            }

        }


        
        
private static readonly object ClickKey = new object();

        [Description(
"单击日期触发的事件!")]
        
public event EventHandler Click
        
{
            add
            
{
                Events.AddHandler(ClickKey, value);
            }

            remove
            
{
                Events.RemoveHandler(ClickKey, value);
            }

        }


        
private static readonly string[] header = new string[] "星期日""星期一""星期二""星期三""星期四""星期五""星期六" };

        
private DateTime startDate
        
{
            
get
            
{
                
return Convert.ToDateTime(this.StartDate + "-01");
            }

        }


        
/// <summary>
        
/// Gets or sets the show date.
        
/// </summary>
        
/// <value>The show date.</value>

        [Description("格式必须为yyyy-MM一样")]
        
public string StartDate
        
{
            
get
            
{
                
if (ViewState["startdate"== null)
                
{
                    
// if (startDate.ToShortDateString() == null)
                    
// startDate = Convert.ToDateTime(System.DateTime.Now.Year + "-" + System.DateTime.Now.Month + "-01");
                    ViewState["startdate"= Convert.ToDateTime(System.DateTime.Now.Year + "-" + System.DateTime.Now.Month + "-01").ToString("yyyy-MM");
                }

                
return ViewState["startdate"].ToString();
            }

            
set
            
{
                
//if (value == null)
                
//{
                
//    startDate = Convert.ToDateTime(System.DateTime.Now.Year + "-" + System.DateTime.Now.Month + "-01");
                
//}
                
//else
                
//{
                
//    startDate = Convert.ToDateTime(value + "-01");
                
//}
                ViewState["startdate"= value;
            }

        }


        
private DateTime endDate
        
{
            
get
            
{
                
return Convert.ToDateTime(this.EndDate + "-01");
            }

        }

        [Description(
"格式必须为yyyy-MM一样")]
        
public string EndDate
        
{
            
get
            
{
                
if (ViewState["enddate"== null)
                
{
                    
//if (endDate.ToShortDateString() == null)
                    
//    endDate = Convert.ToDateTime(System.DateTime.Now.Year + "-" + System.DateTime.Now.Month + "-01");
                    ViewState["enddate"= Convert.ToDateTime(System.DateTime.Now.Year + "-" + System.DateTime.Now.Month + "-01").ToString("yyyy-MM");
                }

                
return ViewState["enddate"].ToString();

            }

            
set
            
{
                
//if (value == null)
                
//{
                
//    endDate = Convert.ToDateTime(System.DateTime.Now.Year + "-" + System.DateTime.Now.Month + "-01");
                
//}
                
//else
                
//{
                
//    endDate = Convert.ToDateTime(value + "-01");

                
//}
                ViewState["enddate"= value;
            }

        }


        [Description(
"预定的日期")]
        [BrowsableAttribute(
false)]
        
public string SelectedDate
        
{
            
get
            
{
                
if (Page.Request.Form["datacontrolhid"!= null)
                
{
                    
return Page.Request.Form["datacontrolhid"].ToString();
                }

                
else
                
{
                    
return string.Empty;
                }

            }

        }


        
private string initDate;
        [Description(
"字符串形式的数据源")]
        
public string InitDate
        
{
            
set
            
{
                initDate 
= value;
            }

            
get
            
{
                
return initDate;
            }

        }


        
protected override void OnPreRender(EventArgs e)
        
{
            
base.OnPreRender(e);

            
嵌入css和javascript

            
//Page.Header.Controls.Add(new LiteralControl(str));
            if (!Page.IsClientScriptBlockRegistered("tdate"))
            
{
                Page.RegisterClientScriptBlock(
"tdate", str);
            }

        }


        
protected override void Render(HtmlTextWriter writer)
        
{
            
int j = 0//用于月份的累加,即从startDate一直加到endDate
            DateTime tempDate = new DateTime(); //表示从startDate到endDate之间月份的临时变量
            
// string sTime = string.Empty;
            
// string eTime = string.Empty;
            writer.Write("<input type='hidden' name='datacontrolhid' id='" + this.UniqueID + "hid'/>");//隐藏域,用于存放客户选中的日期
            
//writer.Write("<input type='hidden' name='SToSDate' id='SToSDate'>");
            
//writer.Write("<input type='hidden' name='SToEDate' id='SToEDate'>");
            
            
//writer.Write("<a>开始时间:</a><select id=\"sYear1\" onchange=document.getElementById('SToEDate').value=document.getElementById(\"sYear1\").value+\"-\"+>");
            
//for (int i = 2000; i < 2050; i++)
            
//{
            
//    writer.Write("<option id=op" + i.ToString() + " value='" + i.ToString() + "'>" + i.ToString() + "</option>");
            
//}
            
//writer.Write("</select>年<select id=\"sMonth1\" onchange=document.getElementById('SToEDate').value=\"0\"+document.getElementById(\"sMonth1\").value;>");

            
//for (int i = 1; i < 13; i++)
            
//{
            
//    writer.Write("<option id=op2" + i.ToString() + " value='" + i.ToString() + "'>" + i.ToString() + "</option>");
            
//}
            
//writer.Write("</select>月");
            
//writer.Write("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
            
//writer.Write("<a>结束时间:</a><select id=\"sYear2\" onchange=alert(document.getElementById(\"sYear2\").value);>");
            
//for (int i = 2000; i < 2050; i++)
            
//{
            
//    writer.Write("<option id=op" + i.ToString() + " value='" + i.ToString() + "'>" + i.ToString() + "</option>");
            
//}
            
//writer.Write("</select>年<select id=\"sMonth2\" onchange=alert(document.getElementById(\"sMonth2\").value)>");

            
//for (int i = 1; i < 13; i++)
            
//{
            
//    writer.Write("<option id=op2" + i.ToString() + " value='" + i.ToString() + "'>" + i.ToString() + "</option>");
            
//}
            
//writer.Write("</select>月");
            
//writer.Write("<input type='button' id='btnView' onclick=\"javascript:document.getElementById('SToSDate').value=document.getElementById('sYear1').value+'-'+document.getElementById('sMonth1').value;document.getElementById('SToEDate').value=document.getElementById('sYear2').value+'-'+document.getElementById('sMonth2').value;__doPostBack('"+this.UniqueID+"','');\" value='查看'></input>");

            writer.RenderBeginTag(HtmlTextWriterTag.Table);
            
int k=0;
            
do //为每个月画出一张表格
            {
                
if(k%PerRow==0)
                    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);

                
//为table标签定义属性和样式,注意:是先写出属性,再给出标签
                writer.AddAttribute(HtmlTextWriterAttribute.Width, "380px");
                writer.AddAttribute(HtmlTextWriterAttribute.Border, 
"1px");
                writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, 
"0");
                writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, 
"0");
                writer.AddAttribute(HtmlTextWriterAttribute.Bordercolor, 
"#cccc99");

                tempDate 
= startDate.AddMonths(j);

                
//给出标签 
                writer.Write("<table id=\"TableAdSeatState_" + j.ToString() + "\" month=\"" + tempDate.ToString("yyyy-MM") + "\" selected=\"false\" cellspacing=\"0\" cellpadding=\"2\" bordercolor=\"silver\" border=\"1\" class=\"borderAll\" width=\"100%\">");

                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.Write(
"<td colspan=\"7\" align=\"middle\" class=\"borderBottom\"  onclick=\"header_onclick()\">");    //结束标记
                writer.Write(tempDate.Year + "" + tempDate.Month + "");  //计算当月的年月

                writer.Write(
"</td>");

                writer.RenderEndTag();
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);

                
//画出星期几
                for (int i = 0; i < DateControl.header.Length; i++)
                
{
                    
//     writer.RenderBeginTag(HtmlTextWriterTag.Td);
                    
//     writer.Write(DateControl.header[i]);
                    
//     writer.RenderEndTag();
                    writer.Write("<td selected=\"false\" align=\"middle\" bgcolor=\"#99cc33\" class=\"borderRightBottom\" onclick=\"week_onclick()\">");
                    writer.Write(DateControl.header[i]);
                    writer.Write(
"</td>");
                }

                writer.RenderEndTag();

                DateTime end 
= tempDate.AddMonths(1);
                
int days = ((TimeSpan)end.Subtract(tempDate)).Days;    //计算当月的天数
                int spaceday = Convert.ToInt32(tempDate.DayOfWeek);    //计算当天是周几并转换成数字
                writer.AddAttribute(HtmlTextWriterAttribute.Align, "center");
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);

                
//以下循环是用于把1号之前的日期用“&nbsp;”即空格填充
                for (int i = 0; i < spaceday; i++)
                
{
                    writer.RenderBeginTag(HtmlTextWriterTag.Td);
                    writer.WriteLine(
"&nbsp;");
                    writer.RenderEndTag();
                }


                
int t = 0;
                
//以下循环用于填充日期,并能把周末用红色标记出来,且伴随着事件的响应即颜色的变化。
                for (int i = 1; i <= days; i++)
                
{
                    
//定义一个变量存放当月的第一天
                    string tddate = tempDate.ToString("yyyy-MM"+ "-" + i.ToString().PadLeft(2'0');

                    
//     writer.Write(Page.GetPostBackEventReference(this,this.StartDate+"-"+i.ToString().PadLeft(2,'0')));
                    writer.Write("<td align=\"middle\" Occupancy=\"0\" Prearrange=\"0\" Largess=\"0\" title=\"\" onclick=\"day_onclick()\" oncontextmenu=\"day_oncontextmenu()\" day=\"" + tddate + "\" ");

                    
//存放颜色参数
                    string colour = string.Empty;
                    
//存放类名
                    string classname = string.Empty;

                    
//如果系统时间之后背景颜色默认为可预定,即绿色,否则为白色,不可预定
                    if (Convert.ToDateTime(tddate) > System.DateTime.Now)
                    
{
                        colour 
= "#99ffff";
                        classname 
= "styleCan";
                    }

                    
else
                    
{
                        colour 
= "white";
                        classname 
= "styleCannot";
                    }


                    
//if语句是根据用户提供的数据源来对日历进行颜色的初始化
                    
//过期无背景颜色(White)(sign:W),已经被预定显示红色背景(sign:R),可以预定显示绿色(sign:G),被选中的显示蓝色(#6495ed)(sign:B)
                    if (this.InitDate == "" || this.InitDate == null)
                    
{
                        writer.Write(
"");
                    }

                    
else
                    
{
                        
this.InitDate = this.InitDate.TrimEnd(';');
                        
string[] dateStrs = null;
                        dateStrs 
= this.SplitPage(this.InitDate, ";");
                        
for (int l = 0; l < dateStrs.Length; l++)
                        
{
                            
//如果当天已经被预定,则背景呈现红色
                            
//switch (dateStrs[l].Substring(dateStrs[l].Length - 1))
                            
//{
                            
//    case "R":
                            
//if (dateStrs[l].TrimEnd('R') == tddate)
                            if (dateStrs[l] == tddate)
                            
{
                                colour 
= "#ffc0cb";
                                classname 
= "styleCannot";
                            }

                            
//        break;
                            
//    case "G":
                            
//        if (dateStrs[l].TrimEnd('G') == tddate)
                            
//        {
                            
//            colour = "#99ffff";
                            
//            classname = "styleCan";
                            
//        }
                            
//        break;
                            
//    default:
                            
//        break;
                            
//}
                        }

                    }


                    writer.Write(
" style=\"background-color:" + colour + "\" class=\"" + classname + "\" ");

                    
//周末的字体显示红色
                    if (tempDate.AddDays(i - 1).DayOfWeek == System.DayOfWeek.Saturday || tempDate.AddDays(i - 1).DayOfWeek == System.DayOfWeek.Sunday)
                    
{
                        writer.Write(
"><font color='red'>" + i.ToString() + "</font></td>");
                    }

                    
else
                    
{
                        writer.Write(
">" + i.ToString() + "</td>");
                    }


                    
//每到7天换行
                    if ((i + spaceday) % 7 == 0)
                    
{
                        writer.RenderEndTag();

//                        //处理月末是周六的情况
//                        if ((i + spaceday) / 7 != 5)
//                        {
//                            writer.AddAttribute(HtmlTextWriterAttribute.Align, "center");
//                            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
//                        }
//                        else
//                        {
//                            //writer.RenderBeginTag(HtmlTextWriterTag.Tr);
//                        }
                        if(i!=days)
                        
{
                            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                        }

                    }


                    
//下文备用
                    t = i;
                }


                
//每个月的表格有5行和6行之分
                if ((spaceday + days) % 7 != 0)
                
{
                    
int allcount = spaceday + days > 35 ? 42 : 35;
                    
for (int i = 0; i < allcount - spaceday - days; i++)
                    
{
                        writer.RenderBeginTag(HtmlTextWriterTag.Td);
                        writer.Write(
"&nbsp;");
                        writer.RenderEndTag();
                    }

                    writer.RenderEndTag();
                    
                }

                writer.Write(
"</table>");
//                else
//                {
////                    if ((t + spaceday) / 7 != 5)
////                    {
////                        writer.RenderEndTag();
////                    }

//                    writer.RenderEndTag();
//                    writer.Write("</table>");
//                }
                j++;
                writer.RenderEndTag();
                k
++;
                
if(k%PerRow==0)
                    writer.RenderEndTag();
                
                
            }

            
while (tempDate < endDate);
            
if(k%PerRow!=0)
            
{
                
for (int i = 0; i < PerRow-k%PerRow; i++)
                
{
                    writer.RenderBeginTag(HtmlTextWriterTag.Td);
                    writer.Write(
"&nbsp;");
                    writer.RenderEndTag();
                }

                writer.RenderEndTag();
            }

            writer.RenderEndTag();
            
        }


        
/// <summary>
        
/// 把一个字符串以一个分隔符分开
        
/// </summary>
        
/// <param name="source">需分割的字符串</param>
        
/// <param name="split">分割符</param>
        
/// <returns>字符数组</returns>

        private string[] SplitPage(string source, string split)
        
{
            
int len = split.Length;
            ArrayList al 
= new ArrayList();
            
int start = 0//开始位置
            int j = -1//匹配索引位置
            while (true)
            
{
                j 
= source.IndexOf(split, start);
                
if (j > -1)
                
{
                    al.Add(source.Substring(start, j 
- start));
                    
int s = j - start;
                    start 
= j + len;
                }

                
else
                
{
                    al.Add(source.Substring(start));
                    
break;
                }

            }

            
string[] result;
            
if (al.Count == 0)
            
{
                
string[] r = new string[1];
                r[
0= source;
                result 
= r;
            }

            
else
            
{
                
string[] r = new string[al.Count];
                
for (int i = 0; i < al.Count; i++)
                
{
                    r[i] 
= al[i].ToString();
                }

                result 
= r;
            }

            
return result;
        }



        
//定义onclick事件处理程序
        protected virtual void OnClick(StringEventArgs e)
        
{
            EventHandler clickEventDelegate 
= (EventHandler)Events[ClickKey];
            
if (clickEventDelegate != null)
            
{
                clickEventDelegate(
this, e);
            }

        }


        
IPostBackEventHandler 成员
    }


    
public class StringEventArgs : EventArgs
    
{
        
private string argString;

        
public string ArgString
        
{
            
get return argString; }
        }


        
public StringEventArgs(string arg)
        
{
            
this.argString = arg;
        }


        
public override string ToString()
        
{
            
return this.argString;
        }

    }



}


运行效果如下图: