简单快捷的 WPF 月视图日历
一个非常简单的基于 XAML 的月视图日历,支持显示约会并公开事件。
引言
最近(上个月)在一个自由职业项目中,我需要一个基本的月视图日历控件在应用程序的几个地方使用。我搜索了一下,找到了使用 WPF 创建 Outlook 日历(由 rudigobbler 编写),这是一个很棒的日视图。我还发现了 Richard Gavel 正在进行的优秀工作(我希望如此)使用 WPF 创建 Outlook UI(到目前为止,这是一个 7 部分的系列文章)。
这两者都是为了为其他开发人员创建真正可重用的组件而做出的更严肃的努力——但两者都没有月视图。我像往常一样时间紧迫,也不想花 800 多美元购买某个供应商的一套 WPF 工具。因为我只需要非常基本的功能,所以我花了一个下午写了这个。
背景
本文假设您对 .NET 和 WPF 有基本的了解。代码并不复杂,也不长。我使用 lambda 函数 (System.Predicate
) 来查找每天的约会,但这几乎就是最复杂的部分了。请注意,这需要 .NET Framework 3.x - 我是在 3.5 SP1 上构建的。
使用代码
示例应用程序(参见顶部的下载)显示了日历的基本用法。请注意,在Window1.xaml中,我只包含对本地程序集的引用,如下所示
xmlns:cal="clr-namespace:QuickWPFMonthCalendar"
Window1.xaml在Window
的开始和结束标签之间只有一行标记
<cal:MonthView x:Name="AptCalendar" VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"/>
这是在 Visual Studio 2008 的设计模式下的Window1.xaml。如您所见,使用它非常简单
MonthView
控件公开多个事件,这些事件在MonthView.xaml.vb中声明如下
Public Event DisplayMonthChanged(ByVal e As MonthChangedEventArgs)
Public Event DayBoxDoubleClicked(ByVal e As NewAppointmentEventArgs)
Public Event AppointmentDblClicked(ByVal Appointment_Id 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 DisplayMonthChanged(ByVal e As MonthChangedEventArgs) _
Handles AptCalendar.DisplayMonthChanged
Call SetAppointments()
End Sub
在我为其创建的实际应用程序中,Appointment
类是由 Visual Studio 的 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
事件。
关注点
为了确定用户双击的位置(因为它可能在约会上,也可能在日历框控件的空白部分),我重新利用了我正在应用程序的另一部分使用的函数,该函数最初是用 C# 编写的,来自 Bea Stollnitz 的博客。
此外,我在MonthView.xaml.vb中的代码中添加了一个小检查,它在设计模式下显示一些随机约会——这样我就可以看到约会的样式而无需构建/运行。该代码块如下所示
'-- for design mode, add appointments to random days for show...
If System.ComponentModel.DesignerProperties.GetIsInDesignMode(Me) Then
If Microsoft.VisualBasic.Rnd(1) < 0.25 Then
Dim apt As New DayBoxAppointmentControl()
apt.DisplayText.Text = "Apt on " & i & "th"
dayBox.DayAppointmentsStack.Children.Add(apt)
End If
ElseIf ....
显然,您可以安全地将其删除。
请记住,这个月视图是对问题的快速而简陋的解决方案,它肯定有一些缺陷。一个主要缺陷是,如果您一天中的约会过多,它们就不能正常显示,也不会给出任何视觉提示。此外,所有这些都是用 XAML 构建块完成的,尽管您可以获取中间构建的*.xaml.g.vb文件并创建一个包含所有代码的新项目,或者将其构建到 DLL 中然后引用它。截至目前,它不支持样式或任何其他花哨的功能——我只是在做这个项目的同时学习 WPF。
希望这个相当粗糙的尝试能够促使 WPF 专家创建一个更完整、更具样式的控件,我们都可以使用!
历史
- 版本 0.1 - 2009 年 3 月 11 日发布(2009 年 2 月创建)作者:Kirk Davis。