在一些应用场景中, 我们可能需要记录某一天, 某个时段的日程安排, 那么这个时候就需要引入了 DevExpress 的日程控件 XtraScheduler 了, 这个控件功能非常强大, 提供了很好的界面展现方式, 以及很多的事件属性给我们定制修改, 能很好满足我们的日程计划安排的需求, 本文全面分析并使用这 个控件, 希望把其中的经验与大家分享
| 立即下载 DevExpress 安装包, 免费体验 30 天!
1 日程控件的表现效果
整个日程控件, 可以分为日视图周视图月视图等等, 当然还有一些不常用的时间线甘特图等, 本例我们来关注控件的使用以及这几个视图的处理先来看看他们的界面效果, 如下所示
日视图:
在视图里面, 默认可以打开响应的日程事件进行编辑的
周视图:
月视图:
2 日程控件 XtraScheduler 的使用
我们在上面展示了这个控件的几个视图的界面, 一般情况下的控件使用还是很方便的, 也就是直接拖拉 SchedulerControl 到 Winform 界面即可, 但是我们为了符合我们的使用需求, 还是需要设置不少属性或者事件的处理的
1)几种视图的切换
由于控件, 默认也是提供右键菜单, 对几种控件视图进行切换的, 如下菜单所示
但是我们也可以通过代码进行切换处理, 具体代码很简单, 该控件已经进行了很好的封装, 直接使用即可
- 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)设置禁用编辑新增等功能处理
该日程控件, 可以通过控件属性, 对日程记录的新增编辑删除等菜单功能进行屏蔽或者开放(默认是开放的)
通过控件属性的方式, 操作如下所示
当然我们也可以通过代码对这些属性进行设置, 如下代码所示
- 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)日程控件的头部日期显示处理
默认的日程控件, 其日视图周视图的头部默认显示的是日期, 如下所示
如果需要把它修改为我们想要的头部内容(如加上星期几), 那么就需要对这个头部显示进行自定义的处理才可以了
有两种方式可以实现这个功能, 其一是引入一个自定义类, 如下所示
- 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")));
- }
- }
然后在控件初始化后, 添加对这个处理实现即可
- // 重载头部显示
- 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 事件进行修改处理, 如下所示(推荐采用上面一种)
- 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 即可, 这个事件在菜单显示前进行处理, 如下面所示代码
- 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("测试菜单功能");
- }
3 日程控件 XtraScheduler 的数据绑定
在日程控件里面, 我们最重要, 最关注的莫过于它的数据绑定及内容显示了, 因为只有这样, 我们才可以用于实价的应用当中, 为用户显示他所需的数据, 并存储我们所需要的数据
在日程控件里面, 有相应的引导我们进行这样的处理, 还是非常不错的
数据的绑定, 我们需要了解日程控件的默认处理方式, 因为它也提供了一些数据字段的信息, 我们从控件的对象里面, 看到有创建数据库的信息, 里面有一些 表的字段, 我们可以参考来创建我们的数据存储信息, 其中就包括了资源 Resource 的存储, 日程事件安排 Appointments 的存储, 如下所示
根据这个里面的字段信息, 我们可以建立自己的数据库模型如下所示
在数据库里面创建这两个表, 并根据这些表对象, 使用代码生成工具 Database2Sharp 进行代码的快速生成, 然后复制生成的代码到具体的测试项目里面, 生成的代码无需任何修改即可直接使用在具体项目里面, 测试项目如下代码结构所示
如日程资源对象的数据库信息, 就会转换为具体的实体类信息, 供我们在界面中使用了, 这样也符合我的 Winform 开发框架的实体类绑定规则, 提高我们数据的强类型约束
如资源对象的实体类代码生成如下所示
- /// <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 处理
- /// <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";
- }
确定控件属性和实体类之间关系后, 我们就需要从数据库里面加载信息了我们在窗体的代码里面增加两个资源对象的集合列表, 如下代码所示
- // 日程资源集合和事件列表
- private List < AppResourceInfo > ResourceList = new List < AppResourceInfo > ();
- private List < UserAppointmentInfo > EventList = new List < UserAppointmentInfo > ();
然后就是把数据从数据库里面, 通过开发框架底层的工厂类进行数据的提取, 如下代码所示
- 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("数据库不存在记录");
- }
- }
而保存数据, 我们把对象里面的集合存储到数据库里面即可
- 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 生成的代码, 直接具有数据存储和获取的功能, 例子就很容易明白和处理了, 在实际的项目中, 我们可能 还需要存储用户的额外信息, 如公司部门自定义信息等等, 当然也可以通过这样的模式进行快速的开发, 从而实现高效统一稳定的系统开发过程
但是, 言归正传, 我们前面介绍的字段, 都是控件里面有的内容, 如果是控件里面没有, 我们需要增加的自定义属性, 那么我们应该如何处理呢, 还有默认的日程界面可以修改吗, 等等这些也是我们经常会碰到的问题
首先我们在日程控件界面上, 通过连接按钮的方式, 创建一个自定义的日程窗体, 如下所示:
这样我们就可以看到, 在项目里面增加了一个日程编辑框了, 打开窗体界面, 并增加一个自定义的控件内容, 最终界面如下所示
默认的后台代码里面, 具有了 LoadFormData 和 SaveFormData 两个重载的方法, 这里就是留给我们对自定义属性进行处理的方法体了
我们在其中增加部分自定义属性字段的映射处理即可, 如下代码所示
- /// <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);
- }
然后我们记得在主体窗体的映射里面, 为他们增加对应的字段映射即可, 映射代码如下所示
- AppointmentCustomFieldMappingCollection appointCust = control.Storage.Appointments.CustomFieldMappings;
- appointCust.Add(new AppointmentCustomFieldMapping("CustomField1", "CustomField1"));
这样就构成了一个完整的映射信息
- /// <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 的经验总结, 其中已经考虑了数据存储和显示, 以及快速开发的几个方面, 当然我们可以根据这些案例, 做出更好的日程应用来了
来源: https://www.devexpresscn.com/post/1078.html