在一些应用场景中,咱们可能须要记录某一天,某个时段的日程安排,那么这个时候就须要引入了 DevExpress 的日程控件XtraScheduler 了,这个控件功能很是强大,提供了很好的界面展示方式,以及不少的事件、属性给咱们定制修改,能很好知足咱们的日程计划安排的需求,本文全面分析并使用这 个控件,但愿把其中的经验与你们分享。html
| 当即下载DevExpress安装包,免费体验30天!数据库
一、日程控件的表现效果
整个日程控件,能够分为日视图、周视图、月视图等等,固然还有一些不经常使用的时间线、甘特图等,本例咱们来关注控件的使用以及这几个视图的处理。先来看看他们的界面效果,以下所示。微信
日视图:app

在视图里面,默承认以打开响应的日程事件进行编辑的。框架

周视图:ide

月视图:函数

二、日程控件XtraScheduler的使用
咱们在上面展现了这个控件的几个视图的界面,通常状况下的控件使用仍是很方便的,也就是直接拖拉SchedulerControl到Winform界面便可,可是咱们为了符合咱们的使用需求,仍是须要设置很多属性或者事件的处理的。工具
1)几种视图的切换
因为控件,默认也是提供右键菜单,对几种控件视图进行切换的,以下菜单所示。测试

可是咱们也能够经过代码进行切换处理,具体代码很简单,该控件已经进行了很好的封装,直接使用便可。this
[csharp] view plain copy
- private void btnDayView_Click(object sender, EventArgs e)
- {
- //须要为日视图类型
- this.schedulerControl1.ActiveViewType = SchedulerViewType.Day;
- }
-
- private void btnWeekView_Click(object sender, EventArgs e)
- {
- //须要为周视图类型
- this.schedulerControl1.ActiveViewType = SchedulerViewType.FullWeek;
- }
-
- private void btnMonthView_Click(object sender, EventArgs e)
- {
- //须要为周视图类型
- this.schedulerControl1.ActiveViewType = SchedulerViewType.Month;
- }
2)设置禁用编辑、新增等功能处理
该日程控件,能够经过控件属性,对日程记录的新增、编辑、删除等菜单功能进行屏蔽或者开放(默认是开放的)。
经过控件属性的方式,操做以下所示。

固然咱们也能够经过代码对这些属性进行设置,以下代码所示。
[csharp] view plain copy
- SchedulerControl control = this.schedulerControl1;
-
- //禁用日程增长、删除、修改、拖拉等操做
- control.OptionsCustomization.AllowAppointmentCreate = DevExpress.XtraScheduler.UsedAppointmentType.None;
- control.OptionsCustomization.AllowAppointmentDelete = DevExpress.XtraScheduler.UsedAppointmentType.None;
- control.OptionsCustomization.AllowAppointmentEdit = DevExpress.XtraScheduler.UsedAppointmentType.None;
- control.OptionsCustomization.AllowAppointmentDrag = DevExpress.XtraScheduler.UsedAppointmentType.None;
- control.OptionsCustomization.AllowAppointmentMultiSelect = false;
- control.OptionsRangeControl.AllowChangeActiveView = false;
- control.Views.MonthView.CompressWeekend = false;
- control.OptionsBehavior.ShowRemindersForm = false;
3)日程控件的头部日期显示处理
默认的日程控件,其日视图、周视图的头部默认显示的是日期,以下所示。

若是须要把它修改成咱们想要的头部内容(如加上星期几),那么就须要对这个头部显示进行自定义的处理才能够了。

有两种方式能够实现这个功能, 其一是引入一个自定义类,以下所示。
[csharp] view plain copy
- public class CustomHeaderCaptionService : HeaderCaptionServiceWrapper
- {
- public CustomHeaderCaptionService(IHeaderCaptionService service)
- : base(service)
- {
- }
-
- public override string GetDayColumnHeaderCaption(DayHeader header)
- {
- DateTime date = header.Interval.Start.Date;
- return string.Format("{0:M}({1})", date, date.ToString("dddd",new System.Globalization.CultureInfo("zh-cn")));
- }
- }
而后在控件初始化后,添加对这个处理实现便可。
[csharp] view plain copy
- //重载头部显示
- IHeaderCaptionService headerCaptionService = (IHeaderCaptionService)control.GetService(typeof(IHeaderCaptionService));
- if (headerCaptionService != null)
- {
- CustomHeaderCaptionService customHeaderCaptionService = new CustomHeaderCaptionService(headerCaptionService);
- control.RemoveService(typeof(IHeaderCaptionService));
- control.AddService(typeof(IHeaderCaptionService), customHeaderCaptionService);
- }
或者也能够重载CustomDrawDayHeader事件进行修改处理,以下所示。(推荐采用上面一种)
[csharp] view plain copy
- private void schedulerControl1_CustomDrawDayHeader(object sender, CustomDrawObjectEventArgs e)
- {
- //重绘Header部分,设置日程头部显示格式
- SchedulerControl control = this.schedulerControl1;
- SchedulerViewType svt = control.ActiveViewType;
- if (svt == SchedulerViewType.Day || svt == SchedulerViewType.FullWeek ||
- svt == SchedulerViewType.Week || svt == SchedulerViewType.WorkWeek)
- {
- DayHeader header = e.ObjectInfo as DayHeader;
- DateTime date = header.Interval.Start;
- header.Caption = string.Format("{0}({1})", date.ToString("MM月d日"), date.ToString("dddd", new System.Globalization.CultureInfo("zh-cn")));
- }
- }
4)自定义菜单的处理
在日程控件XtraScheduler的使用中,咱们也能够获取到控件的菜单对象,并对它进行修改、删除,或者新增本身的菜单事件也是能够的,咱们实现事件PopupMenuShowing便可,这个事件在菜单显示前进行处理,以下面所示代码。
[csharp] view plain copy
- private void schedulerControl1_PopupMenuShowing(object sender, PopupMenuShowingEventArgs e)
- {
- //对日程的右键菜单进行修改
- SchedulerControl control = this.schedulerControl1;
- if (e.Menu.Id == DevExpress.XtraScheduler.SchedulerMenuItemId.DefaultMenu)
- {
- //隐藏【视图更改成】菜单
- SchedulerPopupMenu itemChangeViewTo = e.Menu.GetPopupMenuById(SchedulerMenuItemId.SwitchViewMenu);
- itemChangeViewTo.Visible = false;
-
- //删除【新建全部当天事件】菜单
- e.Menu.RemoveMenuItem(SchedulerMenuItemId.NewAllDayEvent);
-
- //设置【新建按期日程安排】菜单为不可用
- e.Menu.DisableMenuItem(SchedulerMenuItemId.NewRecurringAppointment);
-
- //更名【新建日程安排】菜单为自定义名称
- SchedulerMenuItem item = e.Menu.GetMenuItemById(SchedulerMenuItemId.NewAppointment);
- if (item != null) item.Caption = "新建一个计划";
-
- //建立一个新项,用内置的命令
- ISchedulerCommandFactoryService service =
- (ISchedulerCommandFactoryService)control.GetService(typeof(ISchedulerCommandFactoryService));
- SchedulerCommand cmd = service.CreateCommand(SchedulerCommandId.PrintPreview);//打印预览
- SchedulerMenuItemCommandWinAdapter menuItemCommandAdapter = new SchedulerMenuItemCommandWinAdapter(cmd);
- DXMenuItem menuItem = (DXMenuItem)menuItemCommandAdapter.CreateMenuItem(DXMenuItemPriority.Normal);
- menuItem.BeginGroup = true;
- e.Menu.Items.Add(menuItem);
-
- //建立一个新的自定义事件菜单
- DXMenuItem menuTest = new SchedulerMenuItem("测试菜单");
- menuTest.Click += menuItem2_Click;
- menuTest.BeginGroup = true;
- e.Menu.Items.Add(menuTest);
- }
-
- }
-
- void menuItem2_Click(object sender, EventArgs e)
- {
- MessageDxUtil.ShowTips("测试菜单功能");
- }
三、日程控件XtraScheduler的数据绑定
在日程控件里面,咱们最重要,最关注的莫过于它的数据绑定及内容显示了,由于只有这样,咱们才能够用于实价的应用当中,为用户显示他所需的数据,并存储咱们所须要的数据。
在日程控件里面,有相应的引导咱们进行这样的处理,仍是很是不错的。
数据的绑定,咱们须要了解日程控件的默认处理方式,由于它也提供了一些数据字段的信息,咱们从控件的对象里面,看到有建立数据库的信息,里面有一些 表的字段,咱们能够参考来建立咱们的数据存储信息,其中就包括了资源Resource的存储,日程事件安排Appointments的存储,以下所示。

根据这个里面的字段信息,咱们能够创建本身的数据库模型以下所示。

在数据库里面建立这两个表,并根据这些表对象,使用代码生成工具Database2Sharp进行代码的快速生成,而后复制生成的代码到具体的测试项目里面,生成的代码无需任何修改便可直接使用在具体项目里面,测试项目以下代码结构所示。

如日程资源对象的数据库信息,就会转换为具体的实体类信息,供咱们在界面中使用了,这样也符合个人Winform开发框架的实体类绑定规则,提升咱们数据的强类型约束。
如资源对象的实体类代码生成以下所示。
[csharp] view plain copy
- /// <summary>
- /// 日程资源
- /// </summary>
- [DataContract]
- public class AppResourceInfo : BaseEntity
- {
- /// <summary>
- /// 默认构造函数(须要初始化属性的在此处理)
- /// </summary>
- public AppResourceInfo()
- {
- this.ID = 0;
- this.ResourceId = 0;
- this.Color = 0;
- this.Image = new byte[] { };
- }
-
- #region Property Members
-
- [DataMember]
- public virtual int ID { get; set; }
-
- /// <summary>
- /// 资源ID
- /// </summary>
- [DataMember]
- public virtual int ResourceId { get; set; }
-
- /// <summary>
- /// 资源名称
- /// </summary>
- [DataMember]
- public virtual string ResourceName { get; set; }
-
- /// <summary>
- /// 颜色
- /// </summary>
- [DataMember]
- public virtual int Color { get; set; }
-
- /// <summary>
- /// 图形
- /// </summary>
- [DataMember]
- public virtual byte[] Image { get; set; }
-
- /// <summary>
- /// 自定义
- /// </summary>
- [DataMember]
- public virtual string CustomField1 { get; set; }
-
-
- #endregion
-
- }
有了这些对象,咱们还须要作的就是绑定控件和保存控件数据到数据库里面的处理。
可是这里还须要注意一个问题就是,这个日程控件数据是经过字段映射的方式进行数据绑定的,也就是它自己也提供了几个常规字段的信息,所以咱们须要把它们的属性和数据库的字段(这里是实体类)的信息进行匹配。
如咱们能够经过绑定以下,事项Appointments和Resources的Mappings处理。
[csharp] view plain copy
- /// <summary>
- /// 设置日程控件的字段映射
- /// </summary>
- /// <param name="control">日程控件</param>
- private void SetMappings(SchedulerControl control)
- {
- AppointmentMappingInfo appoint = control.Storage.Appointments.Mappings;
- appoint.AllDay = "AllDay";
- appoint.Description = "Description";
- appoint.End = "EndDate";
- appoint.Label = "AppLabel";
- appoint.Location = "Location";
- appoint.RecurrenceInfo = "RecurrenceInfo";
- appoint.ReminderInfo = "ReminderInfo";
- appoint.ResourceId = "ResourceId";
- appoint.Start = "StartDate";
- appoint.Status = "Status";
- appoint.Subject = "Subject";
- appoint.Type = "EventType";
-
- ResourceMappingInfo res = control.Storage.Resources.Mappings;
- res.Caption = "ResourceName";
- res.Color = "Color";
- res.Id = "ResourceId";
- res.Image = "Image";
- }
肯定控件属性和实体类之间关系后,咱们就须要从数据库里面加载信息了。咱们在窗体的代码里面增长两个资源对象的集合列表,以下代码所示。
[csharp] view plain copy
- //日程资源集合和事件列表
- private List<AppResourceInfo> ResourceList = new List<AppResourceInfo>();
- private List<UserAppointmentInfo> EventList = new List<UserAppointmentInfo>();
而后就是把数据从数据库里面,经过开发框架底层的工厂类进行数据的提取,以下代码所示。
[csharp] view plain copy
- private void btnLoadData_Click(object sender, EventArgs e)
- {
- //从数据库加载日程信息
- List<AppResourceInfo> resouceList = BLLFactory<AppResource>.Instance.GetAll();
- this.schedulerStorage1.Resources.DataSource = resouceList;
-
- List<UserAppointmentInfo> eventList = BLLFactory<UserAppointment>.Instance.GetAll();
- this.schedulerStorage1.Appointments.DataSource = eventList;
-
- if (resouceList.Count > 0)
- {
- MessageDxUtil.ShowTips("数据加载成功");
- }
- else
- {
- MessageDxUtil.ShowTips("数据库不存在记录");
- }
- }
而保存数据,咱们把对象里面的集合存储到数据库里面便可。
[csharp] view plain copy
- private void btnSave_Click(object sender, EventArgs e)
- {
- int count = BLLFactory<AppResource>.Instance.GetRecordCount();
- if (count == 0)
- {
- try
- {
- foreach (AppResourceInfo info in ResourceList)
- {
- BLLFactory<AppResource>.Instance.Insert(info);
- }
-
- foreach (UserAppointmentInfo info in EventList)
- {
- BLLFactory<UserAppointment>.Instance.Insert(info);
- }
-
- MessageDxUtil.ShowTips("数据保存成功");
- }
- catch (Exception ex)
- {
- LogTextHelper.Error(ex);
- MessageDxUtil.ShowError(ex.Message);
- }
- }
- else
- {
- MessageDxUtil.ShowTips("数据库已存在数据");
- }
- }
这样,经过代码工具Database2Sharp生成的代码,直接具备数据存储和获取的功能,例子就很容易明白和处理了,在实际的项目中,咱们可能 还须要存储用户的额外信息,如公司、部门、自定义信息等等,固然也能够经过这样的模式进行快速的开发,从而实现高效、统1、稳定的系统开发过程。
可是,言归正传,咱们前面介绍的字段,都是控件里面有的内容,若是是控件里面没有,咱们须要增长的自定义属性,那么咱们应该如何处理呢,还有默认的日程界面能够修改吗,等等这些也是咱们常常会碰到的问题。
首先咱们在日程控件界面上,经过链接按钮的方式,建立一个自定义的日程窗体,以下所示:

这样咱们就能够看到,在项目里面增长了一个日程编辑框了,打开窗体界面,并增长一个自定义的控件内容,最终界面以下所示。

默认的后台代码里面,具备了LoadFormData和SaveFormData两个重载的方法,这里就是留给咱们对自定义属性进行处理的方法体了。
咱们在其中增长部分自定义属性字段的映射处理便可,以下代码所示。
[csharp] view plain copy
- /// <summary>
- /// Add your code to obtain a custom field value and fill the editor with data.
- /// </summary>
- public override void LoadFormData(DevExpress.XtraScheduler.Appointment appointment)
- {
- //加载自定义属性
- txtCustom.Text = (appointment.CustomFields["CustomField1"] == null) ? "" : appointment.CustomFields["CustomField1"].ToString();
-
- base.LoadFormData(appointment);
- }
-
- /// <summary>
- /// Add your code to retrieve a value from the editor and set the custom appointment field.
- /// </summary>
- public override bool SaveFormData(DevExpress.XtraScheduler.Appointment appointment)
- {
- //保存自定义属性
- appointment.CustomFields["CustomField1"] = txtCustom.Text;
-
- return base.SaveFormData(appointment);
- }
而后咱们记得在主体窗体的映射里面,为他们增长对应的字段映射便可,映射代码以下所示。
[csharp] view plain copy
- AppointmentCustomFieldMappingCollection appointCust = control.Storage.Appointments.CustomFieldMappings;
- appointCust.Add(new AppointmentCustomFieldMapping("CustomField1","CustomField1"));
这样就构成了一个完整的映射信息。
[csharp] view plain copy
- /// <summary>
- /// 设置日程控件的字段映射
- /// </summary>
- /// <param name="control">日程控件</param>
- private void SetMappings(SchedulerControl control)
- {
- AppointmentMappingInfo appoint = control.Storage.Appointments.Mappings;
- appoint.AllDay = "AllDay";
- appoint.Description = "Description";
- appoint.End = "EndDate";
- appoint.Label = "AppLabel";
- appoint.Location = "Location";
- appoint.RecurrenceInfo = "RecurrenceInfo";
- appoint.ReminderInfo = "ReminderInfo";
- appoint.ResourceId = "ResourceId";
- appoint.Start = "StartDate";
- appoint.Status = "Status";
- appoint.Subject = "Subject";
- appoint.Type = "EventType";
-
- AppointmentCustomFieldMappingCollection appointCust = control.Storage.Appointments.CustomFieldMappings;
- appointCust.Add(new AppointmentCustomFieldMapping("CustomField1","CustomField1"));
-
- ResourceMappingInfo res = control.Storage.Resources.Mappings;
- res.Caption = "ResourceName";
- res.Color = "Color";
- res.Id = "ResourceId";
- res.Image = "Image";
- }
以上就是我在整合日程控件XtraScheduler的经验总结,其中已经考虑了数据存储和显示,以及快速开发的几个方面,固然咱们能够根据这些案例,作出更好的日程应用来了。
by 伍华聪

===============================================================
更多精彩预告请持续关注DevExpress中文网!
扫描关注DevExpress中文网微信公众号,及时获取最新动态及最新资讯
