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);
}
}
}