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

快速简单的 WPF 月视图日历 - 更新

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2009年8月21日

CPOL

5分钟阅读

viewsIcon

84379

downloadIcon

4823

一个非常简单的、基于XAML的月视图日历,可以显示约会、公开事件,并允许在当前月份内拖动约会。

介绍 

这是我于2009年3月11日发布的“快速简单的WPF月视图日历”的一个略微更新的版本(我本来想把它做成那个项目的新版本,但我好像太笨了,弄不明白怎么做。笨!)。除了使用绑定来设置约会文本和标签值外,唯一显著的改变是,现在你可以将约会拖动到当前可见月份的各个日期之间。 此操作会引发一个新事件,“AppointmentMoved”。 

来自我三月份写的原始介绍: 在二月份做一个自由项目时,我需要在应用程序的几个地方使用一个基本的月视图日历控件。我搜索了一下,找到了使用WPF创建Outlook日历(作者:rudigobbler),这是一个很棒的日视图。我还发现了Richard Gavel的优秀连载(我希望还在进行中)使用WPF创建Outlook UI,这是一个7部分组成的系列(现在可能还有其他WPF日历控件)。 这两者都是更严肃的努力,旨在为其他开发者创建真正可重用的组件——但都没有月视图。当时我也时间紧迫,不想花800多美元购买某个供应商的一套WPF工具。由于我真的只需要非常基本的功能,我花了一个下午写了这个。

更新:我又花了一个半天实现了拖动功能——部分工作是将Bea Stollnitz在她出色的博客上发布的、大量精简过的C# `DragDropHelper`类,塞进了一个“精简版”的VB.NET类,用于示例代码中。 

背景

本文假设您对.NET和WPF有基本的了解。代码并不复杂,也不长。我使用了lambda函数(`System.Predicate`)来查找每天的约会,但也就仅此而已了。请注意,这需要.NET Framework 3.x——我是基于3.5 SP1构建的。 

Using the Code

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

xmlns:cal="clr-namespace:QuickWPFMonthCalendar"

Window1.xaml在Visual Studio 2008的设计模式下,`Window`开始和结束标签之间只包含一行标记。

<cal:MonthView x:Name="AptCalendar" VerticalAlignment="Stretch" 
               VerticalContentAlignment="Stretch"/> 

这是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`属性将自动重绘日历以显示当前选定的月份。还有一个`public`属性,`DisplayStartDate`(类型为`Date`),用于设置要显示的月份和年份(忽略日期和时间)。设置`DisplayStartDate`**不会**(可能令人困惑)导致日历用该月份重新渲染;这是因为在我的应用程序中,我总是设置一些约会(即使我为`MonthAppointments`分配了一个空的`List(Of Appointment)`)。 

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

兴趣点 

为了知道用户双击了哪里(因为可能是双击了约会,或者空白的日期格子控件),我重新利用了一个我在应用程序其他部分使用的函数,该函数最初是用C#编写的,来自Bea Stollnitz的博客。

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

请记住,这个月视图是一个快速粗糙的解决方案,它肯定有缺点。一个主要缺点是,如果一天中有太多的约会,它们就放不下,也不会给你任何视觉提示。另外,该控件是用XAML构建块创建的,尽管您可以获取中间构建的`*.xaml.g.vb`文件并创建一个包含所有代码的新项目,或者将其构建为DLL然后引用它。目前,它不支持样式,而且这个示例版本不支持拖动到Outlook或桌面等(我的实际应用程序确实实现了这一点,但可能对我来说,其实现方式对其他人用处不大)。希望这个示例能启发比我更擅长WPF的人在WPF工具包中构建一些东西 ;-)

历史

  • 版本0.2 - 发布于2009年8月21日(今天早些时候创建,我的时区!)作者:Kirk Davis
    • 这是对原始项目(我已链接)的增强,增加了在显示月份中拖动约会的能力,并在执行此操作时引发新事件。
  • 版本0.1 - 发布于2009年3月11日(创建于2009年2月)作者:Kirk Davis 
© . All rights reserved.