在一些應用場景中,我們可能需要記錄某一天,某個時段的日程安排,那么這個時候就需要引入了DevExpress的日程控件XtraScheduler了,這個控件功能非常強大,提供了很好的界面展現方式,以及很多的事件、屬性給我們定制修改,能很好滿足我們的日程計划安排的需求,本文全面分析並使用這個控件,希望把其中的經驗與大家分享。
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的經驗總結,其中已經考慮了數據存儲和顯示,以及快速開發的幾個方面,當然我們可以根據這些案例,做出更好的日程應用來了。