devexpress schedulerControl Gantt View 使用
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Linq; using System.Windows.Forms; using DevExpress.XtraEditors; using F.Studio.DevExpressUI; using DevExpress.XtraScheduler; using com.geelyhd.MFG.IService; using com.geelyhd.MFG.EFModel; using F.Studio.Prime.Util; using F.Studio.Prime.IService; using F.Studio.Infrastructure; using DevExpress.Utils.Menu; namespace com.geelyhd.MFG.WR.UI.GA { /// <summary> /// 有3分数据(考虑CustomAppointmentForm) /// A是BindingSource中的,用户自己的对象 /// B是ScheduleStorage.Appointments 中的数据,类型是Appointment /// C是CustomerAppointmentForm 中的LoadFormData,SaveFormData /// 这两个函数中传入appointment的hashcode 跟B中是不一样的,所以这里的appointment.id是null /// B中的更改在AppointmentInserting中还位写入到A中,需要等到AppointmentsInserted时写入 /// 在SC中添加新的Appointment后除了要设置Appointment.Id外还需要设置GA_Task.RecId /// 否则GA_Task.RecId会一致是0 /// 是否给Appointment.Id设置映射并保证其有值,会决定针对该apppointment是否可创建依赖 /// /// 所以针对发布功能以下代码会触发2次changing /// apt.LabelId = 1; //设置2个属性,触发了两次Changing /// apt.CustomFields["IsPub"] = true; ///所以CustomAppointmentForm会复制一appointment防止多次触发 /// </summary> public partial class frmGA_PlanAdmin : AdminFormBase { private long _RecId4New = 0; public frmGA_PlanAdmin() { InitializeComponent(); MainBindingSource = this.gAPositionBindingSource; ConfigGrid(gridControl1); #region gridview gridView1.OptionsSelection.EnableAppearanceFocusedCell = false; //整行选中,不显示单独单元格 gridView1.OptionsBehavior.ReadOnly = true; gridView1.OptionsBehavior.Editable = false; #endregion CfgSchedulerControl(schedulerControl1); #region 班组选择变动 MainBindingSource.CurrentChanged += (s, e) => { var cur = MainBindingSource.Current as GA_Position; if (cur == null) return; var iv = new TimeInterval(schedulerControl1.GanttView.SelectedInterval.Start, TimeSpan.FromDays(0.0d));// schedulerControl1.GanttView.SelectedInterval; var res= schedulerControl1.Storage.Resources.Items.FirstOrDefault(ent => ent.Id == cur.Position); if (res == null) return; schedulerControl1.GanttView.SetSelection(iv,res); }; #endregion } protected override void LoadData() { Action act = () => { var sc = schedulerControl1; var positionList = Fetch<IGA_PositionService>().GetList(); var appointmentList = LoadRangeAppointmentList(); var positionVsHarnessList = Fetch<IGA_PositionVsHarnessService>().GetList(); foreach (var p in positionList) { p.HarnessList=positionVsHarnessList.Where(ent => ent.Position == p.Position).ToList(); } SyncContext.Post(o => { //MainBindingSource.Clear(); //gATaskBindingSource.Clear(); MainBindingSource.DataSource = positionList; gATaskBindingSource.DataSource = appointmentList; },null); }; InvokeService(act, "数据加载..."); } public static string CFT(string name,Appointment apt) { return CUtil.TryParserV2<String>( apt.CustomFields[name],""); } public static string CFT4Dec(string name, Appointment apt, string format) { var v= CUtil.TryParserV2<decimal>(apt.CustomFields[name], 0.0m); return v.ToString(format); } public static void UpdateScheduleAmount(long planRecId) { var sql = string.Format(" select SUM(ISNULL(Amount,0)) from GA_Task where PlanRecId={0}", planRecId); var scSum =CUtil.V( ExeScalar<IGA_TaskService, decimal?>(sql)); //更新分配量 var sql_update = string.Format("update GA_Plan Set ScheduledAmount={0} where PlanRecId={1}", scSum, planRecId); ServiceLocator.Fetch<IGA_PlanService>().ExeSql(sql_update); } private void CfgSchedulerControl(SchedulerControl sc) { sc.OptionsCustomization.AllowAppointmentDrag = DevExpress.XtraScheduler.UsedAppointmentType.None; sc.OptionsBehavior.ShowRemindersForm = false; sc.OptionsCustomization.AllowInplaceEditor = UsedAppointmentType.None; sc.OptionsCustomization.AllowAppointmentConflicts = AppointmentConflictsMode.Forbidden; sc.GroupType = SchedulerGroupType.Resource; sc.Storage.Appointments.AutoReload = true; //没有这句databindings中的数据部会自动加载到schedulerStorage #region 设置时间区间 var monthFirstDay = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1); sc.LimitInterval.Start = monthFirstDay.AddDays(-5); sc.LimitInterval.End = monthFirstDay.AddDays(36); #endregion #region 日尺度单元格长度 var scales4Day = sc.GanttView.Scales.OfType<DevExpress.XtraScheduler.TimeScaleDay>().FirstOrDefault(); scales4Day.Width = 200; #endregion #region 显示内容 sc.InitAppointmentDisplayText += (s, e) => { var sb = new StringBuilder(); var apt=e.Appointment; sb.AppendFormat("{0},{1}\r\n",CFT("Batno",apt),CFT("CarType",apt)); sb.AppendFormat("{0},{1}件\r\n", CFT("MNo", apt), CFT4Dec("Amount", apt,"F0")); sb.AppendFormat("{0} ",CFT("MName",apt)); e.Text = sb.ToString(); }; #endregion sc.GanttView.ResourcesPerPage = 4;//资源(泳道)显示数 sc.GanttView.AppointmentDisplayOptions.AppointmentAutoHeight = true; #region 右键菜单 sc.PopupMenuShowing += (s, e) => { if (e.Menu.Id == DevExpress.XtraScheduler.SchedulerMenuItemId.DefaultMenu) { e.Menu.RemoveMenuItem(SchedulerMenuItemId.NewRecurringAppointment); e.Menu.RemoveMenuItem(SchedulerMenuItemId.NewRecurringEvent); e.Menu.RemoveMenuItem(SchedulerMenuItemId.NewAllDayEvent); e.Menu.RemoveMenuItem(SchedulerMenuItemId.SwitchViewMenu); SchedulerMenuItem item = e.Menu.GetMenuItemById(SchedulerMenuItemId.NewAppointment); if (item != null) item.Caption = "新建作业"; } else if (e.Menu.Id == SchedulerMenuItemId.AppointmentMenu) { e.Menu.RemoveMenuItem(SchedulerMenuItemId.AppointmentDependencyCreation); e.Menu.RemoveMenuItem(SchedulerMenuItemId.LabelSubMenu); e.Menu.RemoveMenuItem(SchedulerMenuItemId.StatusSubMenu); #region 放行 //创建一个新的自定义事件菜单 DXMenuItem menuPub= new SchedulerMenuItem("放行"); menuPub.Click += new EventHandler(menuPub_Click); menuPub.BeginGroup = true; e.Menu.Items.Insert(1,menuPub); #endregion } }; #endregion #region Storage CURD #region 新记录初始化属性 sc.InitNewAppointment += (s, e) => { var dbTime = Fetch<IInitService>().Now(); sc.Storage.SetAppointmentId(e.Appointment, _RecId4New); e.Appointment.AllDay = false; e.Appointment.CustomFields["AddTime"] = dbTime; e.Appointment.CustomFields["AddEmpNo"] = UserSetting.UserName; e.Appointment.CustomFields["Status"] = "Init"; e.Appointment.CustomFields["IsPub"] = false; e.Appointment.CustomFields["IsClose"] = false; e.Appointment.CustomFields["ExeStatus"] = 0; e.Appointment.CustomFields["Priority"] = 100; e.Appointment.CustomFields["MUnit"] = "PC"; e.Appointment.CustomFields["OriginalPosition"] = CUtil.TryParserV2<String>(e.Appointment.ResourceId, "OriginalPosition"); e.Appointment.CustomFields["TaskNo"] = Fetch<IInitService>().GetOrderId("0000004838_TaskNo", 10); //这里获取到taskEnt==null //var taskEnt= e.Appointment.GetSourceObject(sc.Storage); _RecId4New--; }; #endregion sc.Storage.AppointmentInserting += (s, e) => { try { Console.WriteLine("Inserting..."); var apt = e.Object as Appointment; var taskEnt = new GA_Task(); AppointEntMap(sc, apt, taskEnt); #region 数据校验 var plan = Fetch<IGA_PlanService>().Get("PlanRecId", taskEnt.PlanRecId); if (plan == null) throw new Exception("PlanRecId对应的GA_Plan记录不存在"); var sql=string.Format(" select SUM(ISNULL(Amount,0)) from GA_Task where PlanRecId={0}",taskEnt.PlanRecId); var scSum =CUtil.V( ExeScalar<IGA_TaskService, decimal?>(sql)); if ((plan.Amount - scSum) < taskEnt.Amount) throw new Exception("未分配量不足"); #endregion var dbIt = Fetch<IGA_TaskService>().Add(taskEnt); UpdateScheduleAmount(dbIt.PlanRecId.Value); sc.Storage.SetAppointmentId(apt, dbIt.RecId); Console.WriteLine("End Inserting"); e.Cancel = false; } catch (Exception ex) { ErrMsg(ex.Message); e.Cancel = true; } }; sc.Storage.AppointmentsInserted += (s, e) => { var apt = e.Objects[0] as Appointment; var row = apt.GetRow(sc.Storage); var taskEnt = apt.GetSourceObject(sc.Storage) as GA_Task; if (taskEnt != null) { taskEnt.RecId = CUtil.TryParserV2<long>(apt.Id, 0); } }; sc.Storage.AppointmentDeleting += (s, e) => { try { var apt = e.Object as Appointment; if (MessageBox.Show("确定删除选中记录,该操作不可恢复!", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != System.Windows.Forms.DialogResult.Yes) { e.Cancel = true; return; } var recId = CUtil.TryParserV2<long>(apt.Id, 0); var dbIt = Fetch<IGA_TaskService>().Get("RecId", recId); if (dbIt == null) throw new Exception("记录已经被删除了,请刷新"); if (dbIt.IsPub == true) throw new Exception("已放行记录不允许删除!"); Fetch<IGA_TaskService>().Delete(dbIt); UpdateScheduleAmount(dbIt.PlanRecId.Value); } catch (Exception ex) { ErrMsg(ex.Message); e.Cancel = true; } }; sc.Storage.AppointmentChanging += (s, e) => { try { var taskEnt = new GA_Task(); var apt = e.Object as Appointment; AppointEntMap(sc, apt, taskEnt); #region 数据校验 var plan = Fetch<IGA_PlanService>().Get("PlanRecId", taskEnt.PlanRecId); if (plan == null) throw new Exception("PlanRecId对应的GA_Plan记录不存在"); var sql = string.Format(" select SUM(ISNULL(Amount,0)) from GA_Task where PlanRecId={0} And RecId!={1}", taskEnt.PlanRecId,taskEnt.RecId); var scSum =CUtil.V( ExeScalar<IGA_TaskService, decimal?>(sql)); if ((plan.Amount - scSum) < taskEnt.Amount) throw new Exception("未分配量不足"); #endregion Fetch<IGA_TaskService>().Update(taskEnt); UpdateScheduleAmount(taskEnt.PlanRecId.Value); e.Cancel = false; } catch (Exception ex) { ErrMsg(ex.Message); e.Cancel = true; } }; sc.Storage.AppointmentsChanged += (s, e) => { Console.WriteLine("Changed:" + (e.Objects[0] as Appointment).LabelId); }; #endregion #region 标签 sc.Storage.Appointments.Labels.Clear(); sc.Storage.Appointments.Labels.Add(new AppointmentLabel(Color.FromArgb(0xee,0xee,0xee), "未放行")); sc.Storage.Appointments.Labels.Add(new AppointmentLabel(Color.FromArgb(200, 200, 140), "待生产")); sc.Storage.Appointments.Labels.Add(new AppointmentLabel(Color.Orange, "生产中")); sc.Storage.Appointments.Labels.Add(new AppointmentLabel(Color.LawnGreen, "生产完成")); sc.Storage.Appointments.Labels.Add(new AppointmentLabel(Color.Red, "逾期")); #endregion #region 数据映射 { AppointmentMappingInfo appoint = sc.Storage.Appointments.Mappings; appoint.AllDay = "S_AllDay"; appoint.Description = "S_Description"; appoint.End = "S_End"; appoint.Label = "S_Label"; appoint.Location = "S_Location"; appoint.ResourceId = "Position"; appoint.Start = "S_Start"; appoint.Status = "S_Status"; appoint.Subject = "S_Subject"; appoint.Type = "S_Type"; appoint.AppointmentId = "RecId"; appoint.PercentComplete = "CompletedPercent"; AppointmentCustomFieldMappingCollection appointCust = sc.Storage.Appointments.CustomFieldMappings; var ptyList = typeof(GA_Task).GetProperties().Where(ent => !CUtil.StartsWith(ent.Name, "S_,RecId,CompletedPercent,Position,EntityState,EntityKey")).ToList(); foreach (var pty in ptyList) { appointCust.Add(new AppointmentCustomFieldMapping(pty.Name, pty.Name)); } ResourceMappingInfo res = sc.Storage.Resources.Mappings; res.Caption = "Name"; res.Id = "Position"; } #endregion } void menuPub_Click(object sender, EventArgs e) { var sc = schedulerControl1; if (sc.SelectedAppointments == null || sc.SelectedAppointments.Count <= 0) return; var apt = sc.SelectedAppointments[0]; try { if (MessageBox.Show("确定放行选中记录,该操作不可恢复!", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != System.Windows.Forms.DialogResult.Yes) return; var recId = CUtil.TryParserV2<long>(apt.Id, 0); var dbIt = Fetch<IGA_TaskService>().Get("RecId", recId); if (dbIt == null) throw new Exception("记录已经被删除了,请刷新"); if (dbIt.IsPub == true) throw new Exception("已放行记录无需再次执行!"); dbIt.IsPub = true; dbIt.S_Label = 1; Fetch<IGA_TaskService>().Update(dbIt); var taskEnt = apt.GetSourceObject(sc.Storage) as GA_Task; taskEnt.S_Label = 1; taskEnt.IsPub = true; sc.RefreshData();//指的是从bindingSource数据源读取到Appointment } catch (Exception ex) { ErrMsg(ex.Message); } } public static void AppointEntMap(SchedulerControl sc, Appointment apt,GA_Task ent) { AppointmentMappingInfo aptMaps = sc.Storage.Appointments.Mappings; AppointmentCustomFieldMappingCollection aptCustomMaps = sc.Storage.Appointments.CustomFieldMappings; ent.S_AllDay = apt.AllDay; ent.S_Description = apt.Description; ent.S_End = apt.End; ent.S_Label = apt.LabelId; ent.S_Location = apt.Location; ent.S_Start = apt.Start; ent.S_Status = apt.StatusId; ent.S_Subject = apt.Subject; ent.S_Type =(int) apt.Type; ent.Position = CUtil.TryParserV2<String>(apt.ResourceId, ""); ent.CompletedPercent = apt.PercentComplete; ent.RecId = CUtil.TryParserV2<long>(apt.Id, 0); var ptys=ent.GetType().GetProperties(); foreach (var item in aptCustomMaps) { var pty=ptys.FirstOrDefault(it=>it.Name==item.Name); if(pty==null)continue; pty.SetValue(ent, item.GetValue(apt), null); } } private List<GA_Task> LoadRangeAppointmentList() { var sc = schedulerControl1; var bTime = sc.LimitInterval.Start; var eTime = sc.LimitInterval.End; var where = string.Format("it.S_Start>=DateTime'{0}' And it.S_Start<=DateTime'{1}'", bTime.ToString("yyyy-MM-dd HH:mm:ss"), eTime.ToString("yyyy-MM-dd HH:mm:ss")); var list = Fetch<IGA_TaskService>().GetList(where, "it.RecId"); return list; } private void frmGA_PlanAdmin_Load(object sender, EventArgs e) { LoadData(); } private void schedulerControl1_EditAppointmentFormShowing(object sender, AppointmentFormEventArgs e) { DevExpress.XtraScheduler.SchedulerControl scheduler = ((DevExpress.XtraScheduler.SchedulerControl)(sender)); com.geelyhd.MFG.WR.UI.GA.CustomAppointmentForm form = new com.geelyhd.MFG.WR.UI.GA.CustomAppointmentForm(scheduler, e.Appointment, e.OpenRecurrenceForm); try { e.DialogResult = form.ShowDialog(); e.Handled = true; } finally { form.Dispose(); } } } }
#region Note /* {**************************************************************************************************************} { This file is automatically created when you open the Scheduler Control smart tag } { *and click Create Customizable Appointment Dialog. } { It contains a a descendant of the default appointment editing form created by visual inheritance. } { In Visual Studio Designer add an editor that is required to edit your appointment custom field. } { Modify the LoadFormData method to get data from a custom field and fill your editor with data. } { Modify the SaveFormData method to retrieve data from the editor and set the appointment custom field value. } { The code that displays this form is automatically inserted } { *in the EditAppointmentFormShowing event handler of the SchedulerControl. } { } {**************************************************************************************************************} */ #endregion Note using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using DevExpress.XtraScheduler; using F.Studio.Prime.Util; using DevExpress.XtraEditors; using F.Studio.Infrastructure; using F.Studio.DevExpressUI; using com.geelyhd.MFG.EFModel; namespace com.geelyhd.MFG.WR.UI.GA { public partial class CustomAppointmentForm : DevExpress.XtraScheduler.UI.AppointmentForm { private Appointment _Apt = null; private GA_Plan _SelectedPlan = null; public CustomAppointmentForm() { InitializeComponent(); } public CustomAppointmentForm(SchedulerControl control, Appointment apt) : base(control, apt) { InitializeComponent(); } public CustomAppointmentForm(SchedulerControl control, Appointment apt, bool openRecurrenceForm) : base(control, apt, openRecurrenceForm) { InitializeComponent(); _Apt = apt; } /// <summary> /// Add your code to obtain a custom field value and fill the editor with data. /// 这里的appointment不是构造函数传入的apt /// 这里的apt.Id=null /// </summary> public override void LoadFormData(Appointment appointment) { #region foreach (Control ctl in this.Controls) { var bCtl = ctl as BaseEdit; if (bCtl == null) continue; if (CUtil.StartsWith(bCtl.Name, "f_")) { var field = bCtl.Name.Replace("f_", ""); //Console.WriteLine(string.Format("V:[{0}]:{1}", field, appointment.CustomFields[field])); ReflectorUtil.TrySetProperty(bCtl, "EditValue", appointment.CustomFields[field]); } } #endregion //这里会Id丢失 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(Appointment appointment) { if ( !IsModify && _SelectedPlan == null) { MessageBox.Show("未指定计划信息", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } #region 输入框内容 foreach (Control ctl in this.Controls) { var bCtl = ctl as BaseEdit; if (bCtl == null) continue; if (CUtil.StartsWith(bCtl.Name, "f_")) { var field = bCtl.Name.Replace("f_", ""); var v = ReflectorUtil.TryGetProperty(bCtl, "EditValue"); appointment.CustomFields[field] = v; } } #endregion #region 计划其他字段 if (_SelectedPlan != null ) { appointment.CustomFields["PlanSN"] = _SelectedPlan.PlanSN; appointment.CustomFields["PlanRecId"] = _SelectedPlan.PlanRecId; appointment.CustomFields["OriginalAmount"] = appointment.CustomFields["Amount"]; } #endregion var amount = CUtil.TryParserV2<decimal>(appointment.CustomFields["Amount"], 0.0m); if (amount <= 0) { MessageBox.Show("数量必需大于零", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } //需要调整一下appointment的一个属性否则可能不会触发changeing事件 appointment.Subject = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); return base.SaveFormData(appointment); } private bool IsModify { get { //var v = CUtil.TryParserV2<long>(_Apt.CustomFields["RecId"], 0); var v = CUtil.TryParserV2<long>(_Apt.Id, 0); return v > 0; } } private void CustomAppointmentForm_Load(object sender, EventArgs e) { } protected override void OnLoad(EventArgs e) { base.OnLoad(e); #region 设置视图,在Form_Load事件里调用有问题 SetFormView(); f_Amount.Focus(); if (_Apt != null) { var isPub = CUtil.TryParserV2<bool>(_Apt.CustomFields["IsPub"], false); if (isPub) { btnOk.Enabled = false; btnLoadPlan.Enabled = false; } } #endregion } private void SetFormView() { btnRecurrence.Visible = false; btnDelete.Visible = false; edtLabel.Properties.ReadOnly = true; edtShowTimeAs.Properties.ReadOnly = true; tbProgress.Properties.ReadOnly = true; chkAllDay.Properties.ReadOnly = true; chkReminder.Properties.ReadOnly = true; if (IsModify) { btnLoadPlan.Enabled = false; } foreach (Control ctl in this.Controls) { var bCtl = ctl as BaseEdit; if (bCtl == null) continue; Console.WriteLine(bCtl.Name); bCtl.Properties.ReadOnly = true; if (CUtil.InList(bCtl.Name, "f_Amount,tbDescription,edtStartDate,edtStartTime,edtEndDate,edtEndTime,edtResource")) { bCtl.Properties.ReadOnly = false; } } } private void btnOk_Click(object sender, EventArgs e) { } private void btnLoadPlan_Click(object sender, EventArgs e) { #region 测试代码 //var subfix=Guid.NewGuid().ToString("N").Substring(0,5); //f_MNo.EditValue = "MNo" + subfix; //f_MName.EditValue = "MName" + subfix; //f_CarType.EditValue = "CarType" + subfix; //f_Batno.EditValue = "Batno" + subfix; //f_ProductData.EditValue = DateTime.Now; //f_Amount4Plan.EditValue = 400.0m; #endregion try { using (var frm = new frmGA_PlanSelector()) { frm.Owner = this; if (frm.ShowDialog() == System.Windows.Forms.DialogResult.OK) { var plan = frm.SelectedItem; f_MNo.EditValue = plan.MNo; f_MName.EditValue = plan.MName; f_CarType.EditValue = plan.CarType; f_Batno.EditValue = plan.Batno; f_ProductData.EditValue = plan.ProductData; f_Amount4Plan.EditValue = plan.Amount; f_Amount.EditValue = CUtil.V(plan.Amount) - CUtil.V(plan.ScheduledAmount); _SelectedPlan = plan; } } } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }
自定义窗口里需要设置一下Appointment的属性(这里设置了Subject属性)否则不会触发changing事件