WPF月视图日历控件

在2009年3月11日,发布了一个名为"WPF Month-view Calendar"的项目。由于无法弄清楚如何将其作为新版本发布,所以只能以这种方式分享。除了使用绑定来设置预约文本和标签值之外,唯一重要的变化是现在可以在当前可见的月份中拖动预约。移动会触发一个新的事件"AppointmentMoved"。

在2009年3月的原始介绍中,写道:在2月份的一个自由职业项目中,需要在应用程序的几个地方使用一个基本的月视图日历控件。搜索并找到了"Creating an Outlook Calendar usingWPF"(由rudigobbler创建),这是一个很棒的日视图。还发现了Richard Gavel的优秀作品"Creating the Outlook UI with WPF",这是一个7部分的系列(现在可能还有其他WPF日历控件)。这两个都是更严肃的努力,为其他开发者创建真正的、可重用的组件,但它们都没有月视图。由于时间紧迫,不想支付800美元以上购买一套WPF工具。由于真正需要的只是非常基本的功能,所以花了一个下午编写了这个。

更新:花了另外半天时间实现了拖动功能——这部分是将Bea Stollnitz在她的出色博客上最初发布的一个大大简化的C# "DragDropHelper"类塞进一个"lite" VB.NET类中,以在示例代码中使用。

本文假设您对.NET和WPF有基本的了解。代码并不复杂,也不长。使用了lambda函数(System.Predicate)来为每天查找预约,但这就是最复杂的地方。请注意,这需要.NET Framework 3.x——在3.5 SP1上构建了它。

使用代码

示例应用程序(见顶部的下载)展示了日历的基本使用。注意,在Window1.xaml中,只包含了对本地程序集的引用,如下所示:

xmlns:cal="clr-namespace:QuickWPFMonthCalendar"

Window1.xaml在Window打开和关闭标签之间只包含一行标记:

这是在Visual Studio 2008的设计模式中的Window1.xaml。正如您所看到的,使用它非常简单:

MonthView控件公开了几个事件,这些事件在MonthView.xaml.vb中声明如下(请注意,AppointmentMoved是这个版本的新事件):

Public Event DisplayMonthChanged(ByVal e As MonthChangedEventArgs)
Public Event DayBoxDoubleClicked(ByVal e As NewAppointmentEventArgs)
Public Event AppointmentDblClicked(ByVal Appointment_Id As Integer)
Public Event AppointmentMoved(ByVal Appointment_Id As Integer, ByVal OldDay As Integer, ByVal NewDay As Integer)

使用了两个自定义的EventArgs结构,MonthChangedEventArgs和NewAppointmentEventArgs。这主要是因为在构建的应用程序中使用了其他信息;您只需传递一个日期,或一个ID,或一个实际的预约对象。

要处理事件,只需像处理任何控件一样编写事件处理程序。在示例应用程序的Window1.xaml.vb中,使用了以下内容:

Private Sub DayBoxDoubleClicked_event(ByVal e As NewAppointmentEventArgs) Handles AptCalendar.DayBoxDoubleClicked
MessageBox.Show("You double-clicked on day " & CDate(e.StartDate).ToShortDateString(), "Calendar Event", MessageBoxButton.OK)
End Sub
Private Sub AppointmentDblClicked(ByVal Appointment_Id As Integer) Handles AptCalendar.AppointmentDblClicked
MessageBox.Show("You double-clicked on appointment with ID = " & Appointment_Id, "Calendar Event", MessageBoxButton.OK)
End Sub
Private Sub AppointmentChanged_event(ByVal Appointment_Id As Integer, ByVal OldDayOfMonth As Integer, ByVal NewDayOfMOnth As Integer) Handles AptCalendar.AppointmentMoved
MessageBox.Show("You moved appointment with ID = " & Appointment_Id & " from day " & OldDayOfMonth & " to day " & NewDayOfMOnth, "Calendar Event", MessageBoxButton.OK)
End Sub
Private Sub DisplayMonthChanged(ByVal e As MonthChangedEventArgs) Handles AptCalendar.DisplayMonthChanged
Call SetAppointments()
End Sub

在实际应用程序(这是其中一小部分)中,Appointment类是一个LINQ-to-SQL类,并且有更多的字段和功能。对于这个演示,剥离了所有LINQ特定的内容。MonthView将预约存储为List(Of Appointment),您可以通过MonthAppointments属性设置(理想情况下,您只传递日历需要显示的那个月的预约)。在这个演示中,在Window的Loaded事件中有一个循环,在当前年份的50个随机日创建预约,并且只传递一个过滤后的列表(使用List(Of T).FindAll)到MonthAppointments。

设置MonthAppointments属性将自动重绘图标以显示当前选定的月份。还有一个其他公共属性,DisplayStartDate(Date类型),用于设置要显示的月份和年份(忽略日期和时间)。设置DisplayStartDate并不(可能令人困惑地)导致日历重新渲染为该月份;这是因为在应用程序中,总是设置一些预约(即使分配一个空的List(Of Appointment)到MonthAppointments)。

有一个标签显示当前显示的月份和年份,以及前进和后退按钮,这些按钮更新DisplayStartDate,然后触发DisplayMonthChanged事件。最后,在这个版本中添加的一点——利用了一个附加属性(在DragDropHelper中定义),它将事件处理程序附加到事件PreviewMouseLeftButtonDown、PreviewMouseLeftButtonUp和PreviewMouseMove。实际的Appointment对象存储在剪贴板中作为一个DataObject,格式名称设置为GetType(Appointment).FullName,以便它可以很好地与您使用的任何命名空间一起使用。

要点

为了确定用户双击的位置(因为它可能在预约上,也可能在daybox控件的空白部分),重新利用了在应用程序的另一部分中使用的一个函数,它最初是用C#编写的,来自Bea Stollnitz的博客。

此外,在MonthView.xaml.vb中包含了一小段代码,显示了设计模式中的一些随机预约(当System.ComponentModel.DesignerProperties.GetIsInDesignMode(Me)为true时)——这样就可以在不构建/运行的情况下看到预约的外观。显然,您可以安全地移除它。

请记住,这个月视图是一个快速且肮脏的解决方案,它肯定有缺陷。一个主要的缺陷是,如果您在一天中预约太多,它们就不适合,也不会给您任何视觉提示。此外,控件是用XAML构建块创建的,尽管您可以抓取中间构建的*.xaml.g.vb文件,并创建一个包含所有代码的新项目,或者只是将其构建为DLL然后引用它。目前,它不支持样式,这个示例版本不支持拖动到Outlook或桌面等(应用程序确实实现了这一点,但可能对其他人来说并不像那样有用)。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485