创建 Outlook 日历(使用 WPF,第一部分)






4.83/5 (70投票s)
使用WPF重新创建Outlook日历。
引言
微软Office无疑是当今世界上最畅销的产品之一!他们总是在尝试创新…… 在这篇CodeProject文章中,我将尝试使用WPF重新创建Microsoft Outlook日历控件。
WPF中的所有元素/控件都是无外观的! 这减少了创建自定义控件的需求。 按钮是一个支持点击并触发Click
事件的元素,但是对按钮的外观**没有**限制!
因此,在我的WPF思考帽的帮助下,我试图找到一个我可以重新设置样式的控件以适应我的日历控件……在尝试了一些想法之后,我决定创建自定义控件。
以下是我的自定义Calendar
控件的基本元素
CalendarLedgerItem和CalendarLedger
分类帐指示时间槽时间。
CalendarTimeslotItem
每一天被分成30分钟的时隙(由CalendarTimeslotItem
表示)。 CalendarTimeslotItem
提供了一个非常简单的悬停样式,以提示用户,通过点击时隙,您可以添加一个预约。 CalendarTimeslotItem
还公开(并引发)AddAppointmentEvent
,该事件被冒泡到日历(通过点击时隙)。 CalendarTimeslotItem
派生自ButtonBase
。
public static readonly RoutedEvent AddAppointmentEvent =
EventManager.RegisterRoutedEvent("AddAppointment", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(CalendarTimeslotItem));
CalendarAppointmentItem
CalendarAppointmentItem
是一个非常通用的容器,用于显示预约(类似于ListboxItem
)。 我有点作弊,假设绑定到CalendarDay
的每个“item”都将具有StartTime
和EndTime
属性!
<Setter Property="StartTime" Value="{Binding StartTime}" />
<Setter Property="EndTime" Value="{Binding EndTime}" />
[注意] 我知道这有点糟糕…… 我将在第2部分中解决这个问题!
CalendarDay
CalendarDay
是我们Outlook日历的核心。 CalendarDay
派生自ItemsControl
。
protected override DependencyObject GetContainerForItemOverride()
{
return new CalendarAppointmentItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is CalendarAppointmentItem);
}
添加到CalendarDay
的每个“item”都将隐式地将其CalendarAppointmentItem
作为其容器。 只要绑定到ItemsControl
的对象具有StartTime
和EndTime
属性,这一切都将“神奇地”工作!
TimeslotPanel
在查看日历控件之前,我们需要介绍的最后一部分是TimeslotPanel
。 这个自定义布局面板将根据其开始时间和结束时间在CalendarDay
控件中定位每个“item”!
<ItemsPanelTemplate>
<local:TimeslotPanel />
</ItemsPanelTemplate>
日历
Calendar
为CalendarTimeslotItem.AddAppointmentEvent
添加了一个所有者。
public static readonly RoutedEvent AddAppointmentEvent =
CalendarTimeslotItem.AddAppointmentEvent.AddOwner(typeof(CalendarDay));
目前,Calendar
控件是唯一明确“知道”日期是什么的控件! 它有一个CurrentDate
属性。
public static readonly DependencyProperty CurrentDateProperty =
DependencyProperty.Register("CurrentDate", typeof(DateTime), typeof(Calendar),
new FrameworkPropertyMetadata((DateTime)DateTime.Now,
new PropertyChangedCallback(OnCurrentDateChanged)));
Calendar
控件还公开了两个可用于更改当前日期的命令
public static readonly RoutedCommand NextDay =
new RoutedCommand("NextDay", typeof(Calendar));
public static readonly RoutedCommand PreviousDay =
new RoutedCommand("PreviousDay", typeof(Calendar));
这些命令是不言自明的!
我还创建了一个非常基本的模型
此模型用作数据源!
public static readonly DependencyProperty AppointmentsProperty =
DependencyProperty.Register("Appointments",
typeof(IEnumerable<Appointment>), typeof(Calendar),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(Calendar.OnAppointmentsChanged)));
现在,唯一棘手的部分是如何将数据绑定到我的Appointments
属性到我的ItemsControl
中?
引入过滤器…… Rob Conery 的 MVC Storefront 使用了类似的方法!
public static class Filters
{
public static IEnumerable<Appointment> ByDate(
thisIEnumerable<Appointment> appointments, DateTime date)
{
var app = froma inappointments
wherea.StartTime.Date == date.Date
selecta;
return app;
}
}
这个扩展方法允许我按日期“过滤”我的预约(忽略时间)。 这是目前的使用方式
private void FilterAppointments()
{
DateTime byDate = CurrentDate;
CalendarDay day = this.GetTemplateChild("day") as CalendarDay;
day.ItemsSource = Appointments.ByDate(byDate);
TextBlock dayHeader = this.GetTemplateChild("dayHeader") as TextBlock;
dayHeader.Text = byDate.DayOfWeek.ToString();
}
这种方法允许我相对容易地扩展日历控件以支持日视图或周视图(只需添加多个CalendarDay
控件)。
好了,这就是第1部分的内容... 如果您觉得这篇文章有趣,请为它投票,并访问我的博客!