65.9K
CodeProject 正在变化。 阅读更多。
Home

另一个月历

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (214投票s)

2005年6月29日

CPOL

19分钟阅读

viewsIcon

3033093

downloadIcon

46434

一个支持日期格式化、可自定义的月历。

引言

我决定编写这个控件,因为我在一个项目中需要一个比标准 Visual Studio 日历更具可定制性和灵活性的月历。我的主要关注点是能够为每天添加信息(颜色、文本、图像等),为了使其良好运行,它还必须是可调整大小的。

这是我为 CodeProject 撰写的第二篇文章,它与我上一篇文章共享了一些实现技术,因此如果您想了解主题、嵌套属性和 TypeEditors 的实现细节,请查看我的 MozBar 文章。有关如何编辑和持久化集合的信息,请查看 Daniel Zaharia 优秀的文章

使用控件

像任何其他 .NET 控件一样,要在 IDE 中使用,您应该将 MonthCalendar 控件添加到工具箱面板。这可以通过右键单击工具箱选项卡并选择“添加/删除项...”,浏览到 MonthCalendar 程序集并选择它来完成。这将把 MonthCalendar 控件添加到工具箱中,以便可以将其拖放到 Windows 窗体上。

MonthCalendar 实例添加到窗体后,选择它并查看其属性。

布局和设计

该控件的基本设计与标准 Visual Studio Calendar 非常相似,但它更加灵活。为了使其更具可移植性并减少开销,所有内容都直接使用 GDI 绘制。MonthCalendar 基本上由五个不同的区域组成,每个区域都有自己的一组属性。

唯一必须可见的区域是月份区域,所有其他区域都可以打开或关闭。

属性

以下属性控制 MonthCalendar 控件最显著的行为和外观

  • BorderColor:用于边框的颜色。
  • BorderStyle:用于边框的样式。
  • ShowFooter:指示页脚是否应可见(默认值=true)。
  • ShowHeader:指示页眉是否应可见(默认值=true)。
  • ShowWeekdays:指示工作日是否应可见(默认值=true)。
  • ShowWeeknumbers:指示周数是否应可见(默认值=false)。
  • TodayColor:用于标记今天日期的圆圈的颜色。
  • ActiveMonth:日历中当前显示的月份。
  • Culture:日历使用的区域性。
  • Dates:格式化日期集合。
  • ImageList:用于保存日历图像的 ImageList,如果需要为日期分配图像,则必须设置此属性。
  • MaxDate:可选择的最大日期。
  • MinDate:可选择的最小日期。
  • SelectedDates:选定日期集合。
  • SelectionMode:指示日历中使用的选择模式(NoneOneMultiSimpleMultiExtended)。
  • ExtendedSelectionKey:扩展选择模式使用的键。
  • FirstDayOfWeek:设置当前区域性中一周的第一天。
  • SelectButton:用于选择的鼠标按钮。
  • SelectTrailingDates:指示是否可以选择拖尾日期(默认值=true)。
  • ShowFocus:鼠标移到日期上时切换热跟踪(默认值=true)。
  • ShowToday:指示是否应标记今天的日期,标记使用的颜色是分配给 TodayColor 的颜色(默认值=true)。
  • ShowTrailingDates:指示是否应显示拖尾日期(默认值=true)。
  • Theme:指示日历是否应使用主题颜色(默认值=false)。
  • KeyboardEnabled:指示是否启用键盘支持(默认值=true)。
  • Keyboard:用于键盘控制的键集合。
  • Version:返回版本号。

  • BackgroundImage:用作背景的图像。
  • Transparency:文本和背景透明度(绘制颜色时使用)。
  • FormatTrailing:指示是否应显示拖尾日期的格式(默认值=true)。
  • Padding:垂直和水平间距。
  • DateAlign:日期在一天中的对齐方式。
  • ShowMonthInDay:指示月份的第一天和最后一天是否应在日期中显示月份(默认值=false)。
  • TextAlign:文本在一天中的对齐方式。
  • EnableImageClick:启用 ImageClick 事件(默认值=false)。
  • ImageAlign:图像在一天中的对齐方式。
  • DateFont:用于日期的字体。
  • TextFont:用于文本的字体。
  • Colors:日历中使用的颜色集合。
  • BorderStyles:日历中使用的边框样式集合。

标题

  • Align:标题中月份/文本的对齐方式。
  • MonthSelectors:指示是否应显示下一个/上一个月份按钮(默认值=true)。
  • YearSelectors:指示是否应显示下一个/上一个年份按钮(默认值=false)。
  • ShowMonth:指示月份名称是否应在标题中可见(默认值 = true)。
  • Text:要在标题中显示的文本(如果 ShowMonthfalse)。
  • BackColor1:用于标题背景的颜色。
  • BackColor2:使用渐变时用于背景的第二种颜色。
  • GradientMode:使用的渐变类型。
  • Font:用于月份/文本的字体。
  • TextColor:用于月份/文本的颜色。
  • MonthContextMenu:指示右键单击标题时是否应显示月份选择菜单(默认值 = true)。

页脚

  • Align:页脚的文本对齐方式。
  • Text:如果 ShowTodayfalse,则在页脚中显示的文本。
  • ShowToday:指示是否应在页脚中显示今天的日期(默认值=true)。
  • Format:今天日期使用的格式。
  • BackColor1:用于标题背景的颜色。
  • BackColor2:使用渐变时用于背景的第二种颜色。
  • GradientMode:使用的渐变类型。
  • Font:用于日期/文本的字体。
  • TextColor:用于日期/文本的颜色。

工作日

  • BorderColor:用于边框的颜色。
  • BackColor1:用于标题背景的颜色。
  • BackColor2:使用渐变时用于背景的第二种颜色。
  • GradientMode:使用的渐变类型。
  • Font:用于工作日的字体。
  • TextColor:用于工作日的颜色。
  • Format:用于显示工作日的格式。
  • Align:工作日的文本对齐方式。

周数

  • BorderColor:用于边框的颜色。
  • BackColor1:用于标题背景的颜色。
  • BackColor2:使用渐变时用于背景的第二种颜色。
  • GradientMode:使用的渐变类型。
  • Font:用于周数的字体。
  • TextColor:用于周数的文本颜色。
  • Align:周数的文本对齐方式。

事件

最有用的事件是

  • MonthChanged:指示活动月份已更改。
  • BeforeMonthChanged:在显示月份之前引发。使用此事件可防止选择某些月份。
  • DayDragDrop:指示数据已拖放到某天,必须将 AllowDrop 设置为 true 才能引发此事件。
  • ImageClick:指示图像被点击。
  • DayClick:指示某天被点击。
  • DayRender:在日期更新之前发生。
  • DayQueryInfo:在日期更新之前发生。
  • DayDoubleClick:指示某天被双击。
  • DaySelected:指示一个或多个日期被选中。
  • BeforeDaySelected:在选择日期之前引发。使用此事件可防止选择日期。
  • DayDeselected:指示一个或多个日期被取消选择。
  • BeforeDayDeselected:在取消选择日期之前引发。使用此事件可防止取消选择日期。
  • DayGotFocus:指示某天获得焦点。
  • DayLostFocus:指示某天失去焦点。
  • DayMouseMove:指示鼠标在某天内移动。

标题

  • HeaderClick:指示页眉被点击。
  • HeaderDoubleClick:指示页眉被双击。
  • HeaderMouseEnter:指示鼠标进入页眉区域。
  • HeaderMouseLeave:指示鼠标离开页眉区域。

页脚

  • FooterClick:指示页脚被点击。
  • FooterDoubleClick:指示页脚被双击。
  • FooterMouseEnter:指示鼠标进入页脚区域。
  • FooterMouseLeave:指示鼠标离开页脚区域。

工作日

  • WeekdayClick:指示工作日被点击。
  • WeekdayDoubleClick:指示工作日被双击。
  • WeekdayMouseEnter:指示鼠标进入工作日区域。
  • WeekdayMouseLeave:指示鼠标离开工作日区域。

周数

  • WeeknumberClick:指示某周被点击。
  • WeeknumberDoubleClick:指示某周被双击。
  • WeeknumberMouseEnter:指示鼠标进入周数区域。
  • WeeknumberMouseLeave:指示鼠标离开周数区域。

像往常一样,在使用事件时,提供的 eventArgs 参数包含对该特定事件有用的属性。

方法

支持以下方法

日期格式化

  • void AddDateInfo(DateItem[] info):添加格式化日期数组。
  • void RemoveDateInfo(DateTime dt):删除与提供的日期关联的 DateItem
  • void RemoveDateInfo(DateItem item):删除提供的 DateItem
  • void AddDateInfo(DateItem info):添加格式化日期。
  • void ResetDateInfo():删除所有日期格式。
  • DateItem[] GetDateInfo():返回所有格式化日期。
  • DateItem[] GetDateInfo(DateTime dt):返回与提供的日期匹配的 DateItem 数组。

选择

  • void SelectDate(DateTime d):选择提供的日期。日期必须介于 MinDateMaxDate 之间。
  • void ClearSelection():清除现有选择。
  • bool IsSelected(DateTime dt):如果选择的日期是提供的日期,则返回 true,否则返回 false
  • void SelectArea(DateItem topleft, DateItem bottomright):选择由两个日期描述的区域。
  • void DeselectArea(DateItem topleft, DateItem bottomright):取消选择由两个日期描述的区域。
  • void SelectRange(DateItem from, DateItem to):选择由两个日期描述的范围。
  • void DeselectRange(DateItem from, DateItem to):取消选择由两个日期描述的范围。
  • void SelectWeek(int week):选择指定周中的每一天。
  • void DeselectWeek(int week):取消选择指定周中所有已选择的日期。
  • void SelectWeekday(DayOfWeek day):选择当前月份中的每个 DayOfWeek
  • void DeselectWeekday(DayOfWeek day):取消选择当前月份中所有已选择的 DayOfWeek

杂项。

  • void Print():在默认打印机上打印当前日历。
  • Bitmap Snapshot():返回当前日历的位图。
  • void SaveAsImage(string filename, ImageFormat format):将日历保存到文件。
  • void Copy():将日历复制到剪贴板。

添加日期格式

您可以在运行时使用 DateInfo 方法之一,或者在设计模式下使用内置的 DateItemCollection 添加格式化日期。

在设计模式下添加日期格式对于实际应用程序来说当然不太实用,您的信息很可能存储在某种数据库中,因此添加日期格式的最佳方式是响应 MonthChanged 事件并在运行时添加日期

private void monthCalendar1_MonthChanged(object sender, 
             Pabo.Calendar.MonthChangedEventArgs e)
{
    AddInfo(e.Year,e.Month);
}

要在运行时添加格式化日期,您需要创建 DateItem 对象。DateItem 有几个属性定义日期的外观

  • Date:信息适用的日期。
  • BackgroundImage:用作背景的图像。
  • BackColor1:用于背景的颜色。
  • BackColor2:使用渐变时用于背景的第二种颜色。
  • GradientMode:使用的渐变类型。
  • Weekend:指示日期是否应作为周末处理(默认值 = false)。
  • DateColor:用于日期的颜色。
  • TextColor:用于文本的颜色。
  • ImageListIndex:要显示在日期中的图像的索引。要使其工作,必须将 ImageList 分配给 MonthCalendar
  • Image:要显示在日期中的图像。如果设置此属性,它将覆盖 ImageList
  • BoldedDate:指示日期是否应使用粗体字体(默认值 = false)。
  • Enabled:指示日期是否启用。禁用的日期不可选择,将使用禁用颜色绘制。任何图像都将以禁用状态绘制(默认值 = true)。
  • Pattern:描述此 DateItem 使用的重复模式(默认值 = None)。
  • Range:描述重复模式的最大范围(结束日期)。

创建和添加 DateItems 可能看起来像这样

private void FormatDates()
{
    DateItem[] d = new DateItem[5]; 
    d.Initialize(); 
    for (int i = 0;i<5;i++)
        d[i] = new DateItem();

    d[0].Date = new DateTime(2005,6,3);
    d[0].BackColor1 = Color.Red;
    d[0].ImageListIndex = 3;
    d[0].Text = "Help";
    d[1].Date = new DateTime(2005,6,12);
    d[1].ImageListIndex = 2;
    d[2].Date = new DateTime(2005,6,16);
    d[2].BackColor1 = Color.LightBlue;
    d[2].ImageListIndex = 8;
    d[3].Date = new DateTime(2005,6,18);
    d[3].BackColor1 = Color.GreenYellow;
    d[3].ImageListIndex = 1;
    d[3].Text = "NorDev";
    d[4].Date = new DateTime(2005,6,22);
    d[4].ImageListIndex = 1;
    d[4].Text = "Cebit";

    monthCalendar1.AddDateInfo(d);
}

如果您不想一次添加大量 DateItem,可以使用 DayQueryInfo 事件并根据日期添加格式。DayQueryInfo 事件在每个日期渲染之前引发,并允许使用 DateItem 属性提供日期格式。

private void monthCalendar1_DayQueryInfo(object sender, 
                            DayQueryInfoEventArgs e)
{
    // Check date
    if (e.Date.DayOfWeek == DayOfWeek.Thursday)  
    {
       // Add custom formatting
       e.Info.BackColor1 = Color.Yellow;
       e.Info.BackColor2 = Color.GhostWhite;
       e.Info.ImageListIndex = 3; 
       e.Info.GradientMode = mcGradientMode.Horizontal;
       // Set ownerdraw = true to add custom formatting
       e.OwnerDraw = true; 
    }
}

请注意,您必须设置 OwnerDraw=true 才能添加格式,如果使用了 DayRender 事件,则此事件添加的格式将被覆盖。

重复日期

MonthCalendar 支持基本的重复日期格式化。此功能由 PatternRange 属性控制。

以下模式可以应用于 DateItem

  • None:不应用重复模式(默认),格式仅对 Date 属性指定的日期有效。
  • Daily:格式将应用于 Range 内的每一天。
  • Weekly:格式将应用于 Range 内的每个相同的星期几。
  • Monthly:格式将应用于 Range 内每个月的同一天。
  • Yearly:格式将应用于 Range 内每年的同一天。

请注意,没有“冲突检测”,因此重叠的模式将相互覆盖。您可以使用 GetDateInfo 等函数来检查哪些 DateItem 适用于特定日期。当通过 DayQueryInfo 事件设置时,RangePattern 属性没有影响。

自绘日期

如果内置格式不能满足您想要的外观,那么您可以使用 DayRender 事件提供自己的格式。DayRender 事件在日历中的每个日期绘制之前引发。您可以通过在 DayRender 事件的处理程序中提供代码来控制日期的内容和格式。要使日期自绘,请设置 DayRenderEventArs.OwnerDraw=true

private void monthCalendar1_DayRender(object sender, 
             Pabo.Calendar.DayRenderEventArgs e)
{
    Brush bgBrush =  new SolidBrush(Color.White);
    Brush dateBrush = new SolidBrush(Color.Black); 
    Font dateFont = new Font("Microsoft Sans Serif",(float)8.25);
    StringFormat dateAlign = new StringFormat();

    dateAlign.Alignment = StringAlignment.Far;   
    dateAlign.LineAlignment = StringAlignment.Near;

    Rectangle rect = new Rectangle(0,0,e.Width,e.Height); 

    // Set OwnerDraw = true to override built in formatting...
    e.OwnerDraw = true; 
    // ...then Draw the appearance of the date
    e.Graphics.FillRectangle(bgBrush,rect);
    e.Graphics.DrawString(e.Date.Day.ToString(), 
               dateFont,dateBrush,rect,dateAlign);

    // Clean up
    bgBrush.Dispose();
    dateBrush.Dispose();
    dateAlign.Dispose();
    dateFont.Dispose();
}

请注意,使用 DayRender 事件绘制日期的外观会完全覆盖控件为特定日期完成的所有其他格式。

类似 Excel 的多选

我想添加到控件的一个功能是多选。问题是,当选定日期具有不同的边框和背景颜色时,单独绘制所有选定日期看起来不太好,而且将日期绘制为已选定也会覆盖之前的背景色;所以您必须做的是用一个透明背景和一个边框(类似 Excel)绘制整个选定区域。那么,我们该如何做到这一点呢?

这很简单。月份区域基本上由一个日期数组组成,每个日期都有自己的矩形,所以我们需要的是选择的开始和结束日期,以便我们可以构建我们的“选择”矩形。当用户点击一个日期时,我们设置开始日期(m_selStart)。当鼠标按下并移动到新日期(可以选中)时,我们设置结束日期(m_selEnd)。绘制选择时,我们只需要获取选择区域的坐标并用所需的透明颜色绘制它。

// Check if an selection exist
if (m_selectedDays.Length>0)
{
    Brush selBrush = new SolidBrush(Color.FromArgb(125,
                             Colors.SelectedBackground));

    // Get Coordinates for selection rectangle
    m_selRight = System.Math.Max(m_days[m_selStop].Rectangle.Right,
                                 m_days[m_selStart].Rectangle.Right); 
    m_selLeft = System.Math.Min(m_days[m_selStop].Rectangle.Left,
                                  m_days[m_selStart].Rectangle.Left);
    m_selTop = System.Math.Min(m_days[m_selStop].Rectangle.Top,
                                   m_days[m_selStart].Rectangle.Top); 
    m_selBottom = System.Math.Max(m_days[m_selStop].Rectangle.Bottom,
                                  m_days[m_selStart].Rectangle.Bottom);     

    // Draw selection        
    Rectangle selRect = new Rectangle(m_selLeft,m_selTop,
                           m_selRight-m_selLeft,m_selBottom-m_selTop);
    e.FillRectangle(selBrush,selRect); 
    ControlPaint.DrawBorder(e,selRect,Colors.SelectedBorder,
                                         BorderStyles.Selected);
    selBrush.Dispose();
}

确实很简单,但我认为它为控件增添了不错的效果。

不同的选择模式

MonthCalendar 支持由 SelectionMode 属性指示的以下选择模式

  • None:不允许选择。
  • One:一次只能选择一个日期。
  • MultiSimple:可以连续选择一个日期块。
  • MultiExtended:与 MultiSimple 相同,但按下一个键可启用不连续日期的选择/取消选择。您可以使用 ExtendedSelectionKey 属性设置使用的键。

在代码中选择/取消选择日期时,请确保设置了适当的 SelectionMode;当取消选择多个日期时,必须将其设置为 MultiExtended,当选择多个日期时,至少需要 MultiSimple

检索选定/取消选定的日期

当选择/取消选择日期时,会引发 DaySelectedDayDeselected 事件。要检索日期,您可以使用 DaySelectedEventArgs 中包含的信息,或者使用 SelectedDates 集合。获取用户选择的日期可能看起来像这样

private void monthCalendar1_DaySelected(object sender, 
                            Pabo.Calendar.DaySelectedEventArgs e)
{
    // Using the days from the EventArgs
    string[] m_daysSelected = e.Days;
    
    // Using SelectedDates
    SelectedDatesCollection m_dates = monthCalendar1.SelectedDates;    
}

在 PropertyGrid 中动态创建下拉列表

有时,您希望为用户提供一个属性的可能选项列表。一种方法是使用枚举,它会自动为您提供一个包含可能选项的下拉列表。但是这种解决方案是静态的,列表将始终具有相同的内容。如果您希望选项根据用户做出的其他选择而变化,该怎么办?

解决方案是创建一个自定义 TypeConverter 并覆盖一些函数,这些函数将允许我们通过 StandardValuesCollection 提供自己的值。

GetStandardValuesSupported 决定是否支持标准值。返回 true 表示支持

public override bool GetStandardValuesSupported(
                     ITypeDescriptorContext context)
{
    // return true to allow standard values.
    return true;
}

GetStandardValuesExclusive 决定是否允许用户键入值。返回 false 表示允许键入

public override bool GetStandardValuesExclusive(
                       ITypeDescriptorContext context)
{
    // Allow user to enter values with keyboard
    return false;
}

现在,我们所要做的就是覆盖 GetStandardValuesCollection 并返回我们想要使用的列表

public override System.ComponentModel.TypeConverter.StandardValuesCollection 
       GetStandardValues(ITypeDescriptorContext context)
{
    activeMonth m = (activeMonth)context.Instance;
    return new StandardValuesCollection(m.Calendar.AllowedYears());
}

您还需要覆盖 OnConvertFromOnConvertTo 以验证输入,如果您不从 StringConverter 继承,则必须同时覆盖 CanConvertFromCanConvertTo

周数

计算周数有些问题,因为世界各国对其日历有不同的规则。在欧洲大部分地区,我们根据 ISO8601 日历计算周数,这意味着我们使用 FirstFourDayWeek 规则并将星期一作为一周的第一天。使用这些设置和瑞典文化,System.Globalization.Calendar 中找到的 GetWeekOfYear 函数并不总是能为您提供正确的周数(根据 ISO8601),因此 .NET 周计算例程中似乎存在错误。

我四处寻找,找到一个网页,它更详细地描述了这个问题,更重要的是,它提供了一个有效的函数

private int GetISO8601WeekNumber(DateTime date)
{
    // Calculates the ISO 8601 Weeknumber
    // In this scenario the first day of the week is monday, 
    // and the week rule states that:
    // [...] the first calendar week of a year is the one 
    // that includes the first Thursday of that year and 
    // [...] the last calendar week of a calendar year is 
    // the week immediately preceding the first 
    // calendar week of the next year.
    // The first week of the year may thus start in the 
    // preceding year

    const int JAN = 1;
    const int DEC = 12;
    const int LASTDAYOFDEC = 31;
    const int FIRSTDAYOFJAN = 1;
    const int THURSDAY = 4;
    bool Week53Flag = false;

    // Get the day number since the beginning of the year
    int DayOfYear = date.DayOfYear;

    // Get the numeric weekday of the first day of the 
    // year (using sunday as FirstDay)
    int StartWeekDayOfYear = 
      (int)(new DateTime(date.Year, JAN, FIRSTDAYOFJAN)).DayOfWeek;
    int EndWeekDayOfYear = 
      (int)(new DateTime(date.Year, DEC, LASTDAYOFDEC)).DayOfWeek;

    // Compensate for the fact that we are using monday
    // as the first day of the week

    if (m_calendar.m_dateTimeFormat.FirstDayOfWeek!=0)  
    {
        //if( StartWeekDayOfYear == 0)
            StartWeekDayOfYear = 8 - StartWeekDayOfYear;
        //if( EndWeekDayOfYear == 0)
            EndWeekDayOfYear = 8 - EndWeekDayOfYear;
    }

    // Calculate the number of days in the first and last week
    int DaysInFirstWeek = 8 - (StartWeekDayOfYear  );
    int DaysInLastWeek = 8 - (EndWeekDayOfYear );

    // If the year either starts or ends on a 
    // thursday it will have a 53rd week
    if (StartWeekDayOfYear == THURSDAY || 
                  EndWeekDayOfYear == THURSDAY)
        Week53Flag = true;

    // We begin by calculating the number of FULL 
    // weeks between the start of the year and
    // our date. The number is rounded up, so the 
    // smallest possible value is 0.
    int FullWeeks = 
      (int) Math.Ceiling((DayOfYear - (DaysInFirstWeek))/7.0);

    int WeekNumber = FullWeeks; 

    // If the first week of the year has at least four days, 
    // then the actual week number for our date
    // can be incremented by one.

    if (DaysInFirstWeek >= THURSDAY)
        WeekNumber = WeekNumber +1;

    // If week number is larger than week 52 (and the year 
    // doesn't either start or end on a thursday)
    // then the correct week number is 1. 
    if (WeekNumber > 52 && !Week53Flag)
        WeekNumber = 1;

    // If week number is still 0, it means that we are trying 
    // to evaluate the week number for a
    // week that belongs in the previous year (since that week 
    // has 3 days or less in our date's year).
    // We therefore make a recursive call using the last day of 
    // the previous year.
    if (WeekNumber == 0)
        WeekNumber = GetISO8601WeekNumber(
            new DateTime(date.Year-1, DEC, LASTDAYOFDEC));
    return WeekNumber;
}

如果您使用 ISO8601 日历(欧洲大部分地区),MonthCalendar 将为您提供正确的周数;否则,您将获得 Microsoft 周数,这可能根据您的区域设置而正确或错误。

使用回调计算周数

如果您没有得到正确的周数,也并非毫无希望,您可以覆盖日历的标准周例程,并通过 WeeknumberCallback 属性提供自己的回调函数来计算周数。回调函数必须将 DateTime 作为唯一参数,并返回一个整数

public int GetWeek(DateTime dt)
{
  // Calculate weeknumber here
}

如果上面的函数是我们的回调,那么设置它可能看起来像这样

private void Form1_Load(object sender, System.EventArgs e)
{
  monthCalendar1.WeeknumberCallBack = 
                new WeekCallBack(this.GetWeek);  
}

已知问题

  • 由于此控件使用 ImageList,它受到臭名昭著的 ImageList 错误的影响,该错误会在设计模式下将图像添加到 ImageList 时破坏 32 位图像的 alpha 通道。解决此问题的方法可以是使用 Tom Guinter 的 ImageSet 等控件(可在此处获取)替换 ImageList,或者在运行时添加图像。希望这在 Visual Studio 2005 中得到修复,因此可能不值得麻烦。

结论

我希望这个控件对您有用。我确信还有很大的改进空间和附加功能,因此如果您有任何想法或建议,请发表评论。

历史

  • 2006年8月16日 - 版本 1.8.2
    • 修复了年度模式计算问题。
    • 修复了 BeforeDayDeselected 事件未正确引发的问题。
    • 修复了 SelectedDates 集合未正确更新的问题。
    • 其他修复。
  • 2006年6月19日 - 版本 1.8.1
    • 修复了最小-最大日期和页眉按钮问题。
    • 修复了 BeforeMonthChanged 事件的问题。
  • 2006年6月6日 - 版本 1.8.0
    • 修改了 ISO8601 周数计算。
    • 添加了键盘支持。
    • 添加了 BeforeMonthChanged 事件,使其能够防止选择某些月份。
    • 添加了 BeforeDaySelected 事件。
    • 添加了 BeforeDayDeselected 事件。
    • 为几乎所有背景添加了渐变支持。
    • 页眉按钮现在尽可能使用视觉样式渲染(仅限 VS2005)。
    • 为周末颜色添加了 SundaySaturday 属性。
    • 添加了 DateItem.BackgroundImage 属性。
    • 添加了 QueryDayInfo 事件(参见文章)。
    • 其他修复。
  • 2006年4月4日 - 版本 1.7.0
    • 添加了 Weeknumber.Align 属性。
    • 添加了 SelectedDates 集合。
    • 添加了 Month.EnableImageClick 属性和 ImageClick 事件。
    • 添加了 DayMouseMove 事件。
    • 将鼠标坐标添加到 DayClick 事件。
    • 其他修复。
  • 2006年1月17日 - 版本 1.6.0
    • 修复了 DateItemCollection 在设计模式下无法正常工作的问题。
    • 添加了 FirstDayOfWeek 属性。
    • 添加了 Header.MonthContextMenu 属性。
    • 将旧的 DateItem.Image 属性重命名为 DateItem.ImageListIndex,并添加了一个新的 DateItem.Image 属性,以允许不使用 ImageList 分配图像。
    • 首次放置在窗体上时具有适当的默认大小。
    • CollectionEditor (DateItems) 关闭时,日历现在会更新。
    • 键盘钩子仅在 SelectionMode = MultiExtendendExtendedSelectionKey != None 时激活。
    • 添加了 Month.BackgroundImage 属性。
    • Month.Transparency 更改为 Month.Transparency.TextMonth.Transparency.Background
    • 其他修复。
    • VS2005 和 NET 2.0 的第一个版本。
  • 2005年10月25日 - 版本 1.5.0
    • 添加了 DayRender 事件(参见文章)。
    • YearSelectors 添加到标题中,用 Header.YearSelectorsHeader.MonthSelector 属性替换了 Header.ShowSelectors 属性。
    • 添加了 SaveAsImage 方法。
    • 添加了 Copy 方法。
    • 添加了 ExtendedSelectionKey 属性。
    • 为日期、文本和图像添加了更多对齐方式。
    • 修复了 ShowToday 属性的问题(又来了..)。
  • 2005年10月1日 - 版本 1.4.1.0
    • 修复了 SelectionMode 默认值的问题。
    • 修复了使用“上一个/下一个”按钮更改年份时 MonthChanged 事件引发两次的问题。
    • 修复了当 SelectionMode = OneSelectDate 无法工作的问题。
    • 修复了无法更改 ShowToday 属性的问题。
    • 修复了即使 ShowFocusfalseFocusDateFocusText 也会应用的问题。
    • 删除了不必要的绘图,这可能导致 CPU 使用率过高。
    • 修复了 WeekNumberCallBack 的序列化问题。
  • 2005年8月24日 - 版本 1.4.0.0
    • 添加了对重复日期的支持(NoneDailyWeeklyMonthlyYearly)。
    • GetDateInfo 现在返回匹配的 DateIem 数组。
    • RemoveDateItem 添加了一个重载,该重载将 DateItem 作为参数。
    • 修复了更改最小/最大日期时“上一个/下一个”按钮不更新的错误。
    • SelectionMode 属性替换了 MultiSelect 属性。
    • 添加了不连续多选(选择日期时按住 CTRL 键)。
    • SelectDate 现在会引发 DaySelected 事件。
    • 添加了一些可自定义的颜色:SelectTextSelectDateFocusTextFocusDate
    • 添加了 IsSelected 函数。
    • 修复了使用“上一个/下一个”按钮时跳过月份的问题。
    • 实现了用于检索周数的回调。
    • 添加了 SelectAreaDeselectAreaSelectRangeDeselectRange 方法。
    • 添加了 SelectWeekDeselectWeekSelectWeekDayDeselectWeekDay 方法。
  • 2005年7月12日 - 版本 1.3.0.0
    • 通过实现 [DefaultValue] 增加了设计时支持。
    • 修复了在设计时对某些属性(ActiveMonthMonth.BorderStyleMonth.Color)所做的更改无法持久保存的错误。
    • 修复了填充问题。
    • 修复了即使将 ShowTrailingDates 设置为 false,今天标记也会绘制在拖尾日期上的错误。
    • 更改月份时,现有选择现在会清除。
    • 添加了 DayDeselected 事件。
    • 添加了 ClearSelection 方法。
    • 修复了今天标记对齐问题。
  • 2005年7月8日 - 版本 1.2.0.0
    • 添加了打印支持(Print 方法)。
    • 添加了 Snaphot 函数,返回当前日历的位图。
    • 修复了使用主题时,选定边框的颜色与选定背景的颜色相同的问题。
    • 修复了在运行时创建的格式化日期不会设置其 Calendar 属性的错误。
    • 添加了几个可自定义的颜色:DateTrailingDateWeekendDateWeekendTextDisabledBackgroundDisabledDateDisabledText
    • 添加了对粗体日期的支持。
    • 添加了对禁用日期的支持。
  • 2005年6月30日 - 版本 1.1.0.0
    • 修复了使用“上一个/下一个”按钮时年份不会更新的错误。
    • 修复了更改 Culture 时月份未重绘的错误。
  • 2005年6月29日 - 版本 1.0.0.0
    • 首次发布。
© . All rights reserved.