using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.WPF; using Microsoft.Win32; using net.sf.mpxj; using net.sf.mpxj.MpxjUtilities; using net.sf.mpxj.reader; using Syncfusion.Windows.Controls.Gantt; using Syncfusion.Windows.Controls.Grid; using Syncfusion.XlsIO; using GridSelectionMode = Syncfusion.Windows.Controls.Grid.GridSelectionMode; using Resource = Syncfusion.Windows.Controls.Gantt.Resource; namespace PRSDesktop { /// /// Interaction logic for JobStagesGrid.xaml /// public partial class JobStagesPanel : UserControl, IPanel, IJobControl { private CoreTable _relationships; private readonly ObservableCollection _resources = new(); private CoreTable _stages; private readonly ObservableCollection _tasks = new(); private JobStagesGrid grid; public JobStagesPanel() { InitializeComponent(); Gantt.ItemsSource = _tasks; Gantt.ResourceCollection = _resources; AddStage.Content = new Image { Source = PRSDesktop.Resources.add.AsBitmapImage() }; EditStage.Content = new Image { Source = PRSDesktop.Resources.pencil.AsBitmapImage() }; ImportFileImage.Source = PRSDesktop.Resources.download.AsBitmapImage(); ExportFileImage.Source = PRSDesktop.Resources.upload.AsBitmapImage(); DeleteStage.Content = new Image { Source = PRSDesktop.Resources.delete.AsBitmapImage() }; } public Guid JobID { get; set; } public bool IsReady { get; set; } public Dictionary Selected() { return new Dictionary(); } public string SectionName => "Job Stages"; public DataModel DataModel(Selection selection) { return new BaseDataModel(new Filter(x => x.ID).IsEqualTo(JobID)); } public void Setup() { AddStage.Visibility = Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; EditStage.Visibility = Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; ImportFile.Visibility = Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; ExportFile.Visibility = Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; DeleteStage.Visibility = Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; } public void CreateToolbarButtons(IPanelHost host) { } public void Shutdown() { } public void Refresh() { var query = new MultiQuery(); query.Add( new QueryDef( new Filter(x => x.Job.ID).IsEqualTo(JobID), null, new SortOrder(x => x.Sequence) ), typeof(JobStage) ); query.Add( new QueryDef( new Filter(x => x.Parent.ID).IsEqualTo(JobID), null, null ), typeof(JobStageRelationship) ); query.Query(); _stages = query.Get(typeof(JobStage)); _relationships = query.Get(typeof(JobStageRelationship)); LoadStages(Guid.Empty); LoadRelationships(); } public event DataModelUpdateEvent OnUpdateDataModel; public void Heartbeat(TimeSpan time) { } private void Gantt_TemplateApplied(object sender, TemplateAppliedEventArgs args) { if (Gantt.GanttGrid != null) { Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One; Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true; Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true; Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star; Gantt.GanttGrid.RowHeaderWidth = 0; Gantt.GanttGrid.ShowRowHeader = false; Gantt.GanttGrid.Columns.Clear(); //Gantt.GanttGrid.Columns.Add(new Syncfusion.Windows.Controls.Grid.GridTreeColumn("TaskId") { Width = 60F, HeaderText = "#", StyleInfo = new Syncfusion.Windows.Controls.Grid.GridStyleInfo() { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("TaskName") { Width = 220F, PercentWidth = 100F, HeaderText = "Description", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("StartDate") { Width = 70F, HeaderText = "Start", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Manpower") { Width = 60F, HeaderText = "Hrs", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("Percentage") { Width = 40F, HeaderText = "%", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center } }); Gantt.GanttGrid.Columns.Add(new GridTreeColumn("FinishDate") { Width = 70F, HeaderText = "Due", StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center } }); } } public void LoadStages(Guid parentid) { var rows = _stages.Rows.Where(r => r.Get(c => c.Parent.ID).Equals(parentid)); foreach (var row in rows) { var parent = _tasks.FirstOrDefault(x => x.ID.Equals(parentid)); var task = new GanttTask { TaskName = row.Get(c => c.Name), StartDate = row.Get(c => c.StartDate), FinishDate = row.Get(c => c.EndDate), Manpower = TimeSpan.FromHours(row.Get(c => c.TotalHours)), ID = row.Get(c => c.ID), TaskId = (int)row.Get(c => c.Sequence) }; if (parent != null) parent.Child.Add(task); else _tasks.Add(task); LoadStages(task.ID); } } private void LoadRelationships() { foreach (var row in _relationships.Rows) { var predtask = _tasks.FirstOrDefault(x => x.ID == row.Get(c => c.Predecessor.ID)); var succtask = _tasks.FirstOrDefault(x => x.ID == row.Get(c => c.Successor.ID)); if (predtask != null && succtask != null) { var type = row.Get(x => x.Type); var relationship = type == GanttRelationshipType.FinishToFinish ? GanttTaskRelationship.FinishToFinish : type == GanttRelationshipType.FinishToStart ? GanttTaskRelationship.FinishToStart : type == GanttRelationshipType.StartToFinish ? GanttTaskRelationship.StartToFinish : GanttTaskRelationship.StartToStart; succtask.Predecessor.Add(new Predecessor { GanttTaskIndex = predtask.TaskId, GanttTaskRelationship = relationship }); } } } private void DeleteStage_Click(object sender, RoutedEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; if (task == null) { MessageBox.Show("Selected Item is Null!"); return; } var stageids = new List { task.ID }; var relids = _relationships.Rows .Where(r => r.Get(c => c.Predecessor.ID).Equals(task.ID) || r.Get(c => c.Successor.ID).Equals(task.ID)) .Select(r => r.Get(c => c.ID)).ToList(); GetChildren(task.ID, stageids, relids); var stages = stageids.Select(x => new JobStage { ID = x }).ToArray(); new Client().Delete(stages, ""); var relationships = relids.Select(x => new JobStageRelationship { ID = x }).ToArray(); new Client().Delete(relationships, ""); Refresh(); } private void GetChildren(Guid taskid, List stages, List relationships) { var stageids = _stages.Rows.Where(r => r.Get(c => c.Parent.ID).Equals(taskid)) .Select(r => r.Get(c => c.ID)); var relids = _relationships.Rows .Where(r => r.Get(c => c.Predecessor.ID).Equals(taskid) || r.Get(c => c.Successor.ID).Equals(taskid)) .Select(r => r.Get(c => c.ID)); relationships.AddRange(relids.Where(x => !relationships.Contains(x))); stageids = stageids.Where(x => !stages.Contains(x)).ToArray(); foreach (var stageid in stageids) { stages.Add(stageid); GetChildren(stageid, stages, relationships); } } private void AddStage_Click(object sender, RoutedEventArgs e) { var menu = new ContextMenu(); var addstage = new MenuItem { Header = "Add Stage to Job" }; addstage.Click += NewStage_Click; menu.Items.Add(addstage); if (Gantt.SelectedItems.Any()) { var addtask = new MenuItem { Header = "Add Task to Stage" }; addtask.Click += NewTaskClick; menu.Items.Add(addtask); } menu.IsOpen = true; } private void DoEdit(JobStage stage) { if (grid == null) using (new WaitCursor()) { grid = new JobStagesGrid(); grid.OnCustomiseEditor += (o, i, c, e) => { if (c.ColumnName.Equals("StartDate") || c.ColumnName.Equals("EndDate") || c.ColumnName.Equals("WorkDays")) { var haschild = stage.ID != Guid.Empty && _stages.Rows.Any(r => r.Get(x => x.Parent.ID) == stage.ID); e.Editable = haschild ? Editable.Hidden : Editable.Enabled; } }; } if (grid.EditItems(new[] { stage })) Refresh(); } //private void CheckParentDates(JobStage stage) //{ // JobStage parent = Stages.FirstOrDefault(x => x.ID.Equals(stage.Parent.ID)); // if (parent != null) // { // // Calculate Start / Finish Dates // DateTime start = stage.StartDate; // DateTime finish = stage.EndDate; // foreach (var child in Stages.Where(x => x.Parent.ID.Equals(parent.ID))) // { // start = start > child.StartDate ? child.StartDate : start; // finish = finish < child.EndDate ? child.EndDate : finish; // } // // Synchronise Parent Start / Finish Dates // if (parent.StartDate != start) // parent.StartDate = start; // if (parent.EndDate != finish) // parent.EndDate = finish; // // Update if necessary // if (parent.IsChanged()) // { // new Client().Save(parent, ""); // CheckParentDates(parent); // } // } //} private void NewStage_Click(object sender, RoutedEventArgs e) { var stage = new JobStage(); stage.Name = "New Stage"; stage.Job.ID = JobID; stage.StartDate = DateTime.Today; stage.EndDate = DateTime.Today; DoEdit(stage); } private void NewTaskClick(object sender, RoutedEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; if (task == null) { MessageBox.Show("Selected Item is Null!"); return; } var parent = _stages.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(task.ID)); if (parent == null) { MessageBox.Show("Cannot find Parent!"); return; } var stage = new JobStage(); stage.Name = "New Task"; stage.Job.ID = JobID; stage.Parent.ID = task.ID; stage.StartDate = _stages.Rows.Any(r => r.Get(c => c.Parent.ID).Equals(task.ID)) ? task.FinishDate : task.StartDate; stage.EndDate = task.FinishDate; DoEdit(stage); } private void EditStage_Click(object sender, RoutedEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; if (task == null) { MessageBox.Show("Selected Item is Null!"); return; } var stage = _stages.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(task.ID)); if (stage == null) { MessageBox.Show("Cannot find Stage!"); return; } DoEdit(stage.ToObject()); } private void ImportFile_Click(object sender, RoutedEventArgs e) { var dlg = new OpenFileDialog(); dlg.Filter = "Microsoft Project Files (*.mpp)|*.mpp"; if (dlg.ShowDialog() == true) { using (new WaitCursor()) { Progress.Show("Clearing Old Stages (0%)"); var dstages = _stages.Rows.Select(x => x.ToObject()).ToArray(); new Client().Delete(dstages, "Deleting Due to Import"); _stages.Rows.Clear(); var drels = _relationships.Rows.Select(x => x.ToObject()).ToArray(); new Client().Delete(drels, "Deleting Due to Import"); _relationships.Rows.Clear(); Progress.SetMessage("Loading Project File (20%)"); var stages = new List(); var relationships = new List(); ProjectReader reader = new UniversalProjectReader(); var project = reader.read(dlg.FileName); Progress.SetMessage("Loading Stages (50%)"); // Create the raw stages foreach (Task task in project.getTasks()) { var stage = new JobStage { Name = task.getName(), StartDate = task.getStart().ToDateTime().Date, EndDate = task.getFinish().ToDateTime().Date, Sequence = task.getID().longValue() }; stage.Job.ID = JobID; stages.Add(stage); } if (stages.Any()) new Client().Save(stages, "Imported From File"); Progress.SetMessage("Building Links (80%)"); // Set up the parent links foreach (Task task in project.getTasks()) { var stage = stages.FirstOrDefault(x => x.Sequence.Equals(task.getID().longValue())); if (task.getParentTask() != null) { var parent = stages.FirstOrDefault(x => x.Sequence.Equals(task.getParentTask().getID().longValue())); stage.Parent.ID = parent.ID; } stage.IsHeader = task.hasChildTasks(); stage.TradesHours = task.getWork().getDuration(); if (task.getPredecessors() != null && !task.getPredecessors().isEmpty()) { var rels = task.getPredecessors().ToIEnumerable().ToArray(); foreach (var rel in rels) { var predecessor = stages.FirstOrDefault(x => x.Sequence.Equals(rel.getTargetTask().getID().longValue())); var successor = stages.FirstOrDefault(x => x.Sequence.Equals(rel.getSourceTask().getID().longValue())); if (predecessor != null && successor != null) { var newrel = new JobStageRelationship(); newrel.Parent.ID = JobID; newrel.Predecessor.ID = predecessor.ID; newrel.Successor.ID = successor.ID; newrel.Type = rel.getType() == RelationType.FINISH_FINISH ? GanttRelationshipType.FinishToFinish : rel.getType() == RelationType.FINISH_START ? GanttRelationshipType.FinishToStart : rel.getType() == RelationType.START_FINISH ? GanttRelationshipType.StartToFinish : GanttRelationshipType.StartToStart; newrel.Offset = (int)rel.getLag().getDuration(); relationships.Add(newrel); } } } } if (stages.Any(x => x.IsChanged())) new Client().Save(stages.Where(x => x.IsChanged()), ""); if (relationships.Any()) new Client().Save(relationships, "Imported from Project File"); Progress.Close(); } MessageBox.Show("Project File Imported Successfully!"); Refresh(); } } private void ExportFile_Click(object sender, RoutedEventArgs e) { var dlg = new SaveFileDialog(); //dlg.Filter = "Microsoft Project Files (*.mspdi)|*.mspdi;Excel Files (*.xlsx)|*.xlsx"; dlg.Filter = "Excel Files (*.xlsx)|*.xlsx"; dlg.FileName = "Project Plan"; if (dlg.ShowDialog() == true) { if (dlg.FilterIndex == 0) ExportToExcel(dlg.FileName); else if (dlg.FilterIndex == 1) ExportToProject(dlg.FileName); else MessageBox.Show(string.Format("Unknown Filter Index: {0}", dlg.FilterIndex)); } } private void ExportToProject(string filename) { MessageBox.Show("Not Yet Implemented"); } private void GetLevel(CoreRow row, ref int level) { if (row == null || !Entity.IsEntityLinkValid(x => x.Parent, row)) return; level++; GetLevel(_stages.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(row.Get(c => c.Parent.ID))), ref level); } private void ExportToExcel(string filename) { var excelEngine = new ExcelEngine(); var application = excelEngine.Excel; var myWorkbook = excelEngine.Excel.Workbooks.Add(); myWorkbook.Version = ExcelVersion.Excel2007; var mySheet = myWorkbook.Worksheets[0]; mySheet.Name = "Project Planning"; var startcol = 1; foreach (var row in _stages.Rows) { var level = 1; GetLevel(row, ref level); if (level > startcol) startcol = level; mySheet.Range[row.Index + 2, level].Value2 = row.Get(c => c.Name); } foreach (var row in _stages.Rows) { mySheet.Range[row.Index + 2, startcol + 1].Value2 = string.Format("{0:dd/MM/yyyy}", row.Get(c => c.StartDate)); mySheet.Range[row.Index + 2, startcol + 2].Value2 = string.Format("{0:dd/MM/yyyy}", row.Get(c => c.EndDate)); } for (var iCol = 1; iCol < startcol; iCol++) mySheet.SetColumnWidth(iCol, 2); mySheet.SetColumnWidth(startcol, 30); mySheet.SetColumnWidth(startcol + 1, 12); mySheet.SetColumnWidth(startcol + 2, 12); mySheet.Range[1, 1].Text = "Stage Description"; mySheet.Range[1, startcol + 1].Text = "Start Date"; mySheet.Range[1, startcol + 2].Text = "Finish Date"; foreach (var row in mySheet.UsedRange.Rows) { row.RowHeight += 5; row.VerticalAlignment = ExcelVAlign.VAlignCenter; } try { myWorkbook.SaveAs(filename, ExcelSaveType.SaveAsXLS); Process.Start(new ProcessStartInfo(filename) { UseShellExecute = true }); } catch (Exception e2) { MessageBox.Show("Error saving spreadsheet!\n\n" + e2.Message); } } private void Task_PropertyChanged(object sender, PropertyChangedEventArgs args) { if (!IsReady) return; if (!string.Equals(args.PropertyName, "StartDate") && !string.Equals(args.PropertyName, "FinishDate")) return; var task = sender as GanttTask; if (task == null) return; var row = _stages.Rows.FirstOrDefault(r => r.Get(x => x.ID) == task.ID); if (row == null) return; var stage = row?.ToObject(); var bChanged = false; if (string.Equals(args.PropertyName, "FinishDate")) { stage.EndDate = task.FinishDate.Date; bChanged = true; } else if (string.Equals(args.PropertyName, "StartDate")) { stage.StartDate = task.StartDate.Date; bChanged = true; } if (bChanged) { row.Set(x => x.EndDate, stage.EndDate.Date); row.Set(x => x.StartDate, stage.StartDate.Date); new Client().Save(stage, "Updated by Planner", (o, e) => { }); } } private void Gantt_RelationshipEstablished(object sender, GanttRelationshipEstablishedEventArgs args) { var pred = args.StartTask as GanttTask; var succ = args.EndTask as GanttTask; if (pred == null || pred.IsMileStone) throw new Exception("Cannot make a connection here"); if (succ == null || succ.IsMileStone) throw new Exception("Cannot make a connection here"); var relationship = new JobStageRelationship(); relationship.Parent.ID = JobID; relationship.Predecessor.ID = pred.ID; relationship.Successor.ID = succ.ID; relationship.Type = args.Relationship == GanttTaskRelationship.FinishToFinish ? GanttRelationshipType.FinishToFinish : args.Relationship == GanttTaskRelationship.FinishToStart ? GanttRelationshipType.FinishToStart : args.Relationship == GanttTaskRelationship.StartToFinish ? GanttRelationshipType.StartToFinish : GanttRelationshipType.StartToStart; new Client().Save(relationship, "", (o, e) => { var row = _relationships.NewRow(); _relationships.LoadRow(row, relationship); _relationships.Rows.Add(row); }); } private void GanttContextMenu_Opened(object sender, RoutedEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; UnlinkTaskMenu.IsEnabled = task != null && task.Predecessor.Any(); UnlinkTaskMenu.Tag = task; } private void UnlinkTaskMenu_Click(object sender, RoutedEventArgs e) { var successor = (sender as MenuItem).Tag as GanttTask; if (successor == null) return; var deletes = new List(); foreach (var link in successor.Predecessor) { var predecessor = _tasks.FirstOrDefault(x => x.TaskId == link.GanttTaskIndex); if (predecessor != null) { var rows = _relationships.Rows.Where(r => r.Get(c => c.Successor.ID).Equals(successor.ID) && r.Get(c => c.Predecessor.ID).Equals(predecessor.ID)).ToArray(); foreach (var row in rows) { deletes.Add(row.ToObject()); _relationships.Rows.Remove(row); } } } if (deletes.Any()) new Client().Delete(deletes, "", (o, args) => { }); successor.Predecessor.Clear(); } private void GanttGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; if (task == null) return; var row = _stages.Rows.FirstOrDefault(r => r.Get(x => x.ID) == task.ID); var stage = row?.ToObject(); if (stage != null) DoEdit(stage); } private void EditTask_Click(object sender, RoutedEventArgs e) { var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask; if (task == null) return; var row = _stages.Rows.FirstOrDefault(r => r.Get(x => x.ID) == task.ID); var stage = row?.ToObject(); if (stage != null) DoEdit(stage); } } }