在管理软件研发过程中,特别是涉及项目管理或者生产计划方面,都需要一款类似微软project的控件对项目下的分解任务进行图形展示(甘特图)。下面介绍一下在WindowsForm下如何实现类似微软project软件的甘特图?最终的效果如下所示:

1、VS2012创建一个Windows应用程序GanttChartControl,并添加甘特图控件库,结构如下:

(注:此处甘特图控件是一款开源库,源码可从网上自行下载)

2、自定义任务类和资源类,代码如下:

  #region 自定义任务和资源类
/// <summary>
///自定义资源
/// </summary>
[Serializable]
public class MyResource
{
public string Name { get; set; }
}
/// <summary>
/// 自定义任务类,继承 Task 接口 (可选)
/// </summary>
[Serializable]
public class MyTask : Task
{
public MyTask(ProjectManager manager) : base()
{
Manager = manager;
} private ProjectManager Manager { get; set; } public new int Start { get { return base.Start; } set { Manager.SetStart(this, value); } }
public new int End { get { return base.End; } set { Manager.SetEnd(this, value); } }
public new int Duration { get { return base.Duration; } set { Manager.SetDuration(this, value); } }
public new float Complete { get { return base.Complete; } set { Manager.SetComplete(this, value); } }
}
#endregion

为了能够保存甘特图信息,需要将任务类和资源类标志为[Serializable]。自定义任务类需要继承Task接口。
3、创建项目和任务,代码如下:

             // 创建项目和任务
_mManager = new ProjectManager();
var p项目= new MyTask(_mManager) { Name = "项目" };
_mManager.Add(p项目);
var t售前任务组 = new MyTask(_mManager) { Name = "售前阶段" };
var t项目启动 = new MyTask(_mManager) { Name = "项目启动会" };
var t需求调研 = new MyTask(_mManager) { Name = "需求调研" };
var t方案撰写 = new MyTask(_mManager) { Name = "方案撰写" };
var t方案确认 = new MyTask(_mManager) { Name = "方案确认" };
// 用ProjectManager类Add方法添加任务
_mManager.Add(t售前任务组);
_mManager.Add(t项目启动);
_mManager.Add(t需求调研);
_mManager.Add(t方案撰写);
_mManager.Add(t方案确认);

首先需要用 ProjectManager  _mManager = new ProjectManager();创建一个ProjectManager,后续所有的任务都需要添加到此类Add方法添加任务进行管理。

任务的其他属性用下述代码进行设置:

          // 用ProjectManager类SetDuration方法设置任务工期
_mManager.SetDuration(t项目启动, );
_mManager.SetDuration(t需求调研, );
_mManager.SetDuration(t方案撰写, );
_mManager.SetDuration(t方案确认, );
_mManager.SetDuration(tUI设计, );
_mManager.SetDuration(t数据库设计, ); //用ProjectManager类Split方法对任务进行分段
_mManager.Split(t数据库设计, new MyTask(_mManager), new MyTask(_mManager), ); // 设置任务的完成率
t项目启动.Complete = 0.9f;
t需求调研.Complete = 0.5f;
t方案撰写.Complete = 0.4f;

4、任务分组和任务衔接关系定义

有了任务后,可以对任务的上下关系(分组)和接替关系进行定义,代码如下:

 // 给任务分组
_mManager.Group(p项目, t售前任务组);
_mManager.Group(p项目, t开发阶段);
_mManager.Group(p项目, t实施阶段); _mManager.Group(t售前任务组, t项目启动);
_mManager.Group(t售前任务组, t需求调研);
_mManager.Group(t售前任务组, t方案撰写);
_mManager.Group(t售前任务组, t方案确认);
//任务衔接关系设置,不支持类型
_mManager.Relate(t项目启动, t需求调研);
_mManager.Relate(t需求调研, t方案撰写);
_mManager.Relate(t方案撰写, t方案确认); _mManager.Group(t开发阶段, tUI设计);
_mManager.Group(t开发阶段, t数据库设计);
_mManager.Group(t开发阶段, t编码);
_mManager.Group(t开发阶段, t测试); _mManager.Relate(t方案确认, tUI设计); _mManager.Relate(tUI设计, t数据库设计);
_mManager.Relate(t数据库设计, t编码);
_mManager.Relate(t编码, t测试); _mManager.Group(t实施阶段, t系统安装和初始化);
_mManager.Group(t实施阶段, t系统培训);
_mManager.Group(t实施阶段, t系统验收);
_mManager.Group(t实施阶段, t结项); _mManager.Relate(t测试, t系统安装和初始化);
_mManager.Relate(t系统安装和初始化, t系统培训);
_mManager.Relate(t系统培训, t系统验收);
_mManager.Relate(t系统验收, t结项);

5、资源分配

           // 创建并设置任务资源(人、材料、设备等),任务资源可自定义
var jake = new MyResource() { Name = "Jake" };
var peter = new MyResource() { Name = "Peter" };
var john = new MyResource() { Name = "John" };
var lucas = new MyResource() { Name = "Lucas" };
var james = new MyResource() { Name = "James" };
var mary = new MyResource() { Name = "Mary" };
// 给任务分配资源
_mManager.Assign(t项目启动, jake);
_mManager.Assign(t项目启动, peter);
_mManager.Assign(t项目启动, john);
_mManager.Assign(t需求调研, jake);
_mManager.Assign(t需求调研, james);
_mManager.Assign(t数据库设计, james);
_mManager.Assign(t数据库设计, lucas);
_mManager.Assign(t方案撰写, mary);
_mManager.Assign(t方案撰写, lucas);
_mManager.Assign(t方案撰写, john);

6、甘特图初始化

             //甘特图初始化并创建任务代理
_mChart.Init(_mManager);
_mChart.CreateTaskDelegate = delegate() { return new MyTask(_mManager); }; // 任务事件监听
_mChart.TaskMouseOver += new EventHandler<TaskMouseEventArgs>(_mChart_TaskMouseOver);
_mChart.TaskMouseOut += new EventHandler<TaskMouseEventArgs>(_mChart_TaskMouseOut);
_mChart.TaskSelected += new EventHandler<TaskMouseEventArgs>(_mChart_TaskSelected);
_mChart.PaintOverlay += _mOverlay.ChartOverlayPainter;
//允许任务拖拽
_mChart.AllowTaskDragDrop = true; // 设置任务提示内容,可自定义
_mChart.SetToolTip(t项目启动, string.Join(", ", _mManager.ResourcesOf(t项目启动).Select(x => (x as MyResource).Name)));
_mChart.SetToolTip(t需求调研, string.Join(", ", _mManager.ResourcesOf(t需求调研).Select(x => (x as MyResource).Name)));
_mChart.SetToolTip(t数据库设计, string.Join(", ", _mManager.ResourcesOf(t数据库设计).Select(x => (x as MyResource).Name)));
_mChart.SetToolTip(t方案撰写, string.Join(", ", _mManager.ResourcesOf(t方案撰写).Select(x => (x as MyResource).Name))); //设置图形日期信息
_mManager.TimeScale = TimeScale.Day;
var span = DateTime.Today - _mManager.Start;
_mManager.Now = (int)Math.Round(span.TotalDays);
// 设置图形标题显示日期格式
_mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek;

7、完成后的界面为:

后台完整代码为:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Braincase.GanttChart;
namespace GanttChartControl
{
/// <summary>
/// 甘特图控件使用演示
/// </summary>
public partial class ExampleFull : Form
{
OverlayPainter _mOverlay = new OverlayPainter(); ProjectManager _mManager = null; /// <summary>
/// Example starts here
/// </summary>
public ExampleFull()
{
InitializeComponent(); // 创建项目和任务
_mManager = new ProjectManager();
var p项目= new MyTask(_mManager) { Name = "项目" };
_mManager.Add(p项目);
var t售前任务组 = new MyTask(_mManager) { Name = "售前阶段" };
var t项目启动 = new MyTask(_mManager) { Name = "项目启动会" };
var t需求调研 = new MyTask(_mManager) { Name = "需求调研" };
var t方案撰写 = new MyTask(_mManager) { Name = "方案撰写" };
var t方案确认 = new MyTask(_mManager) { Name = "方案确认" };
// 用ProjectManager类Add方法添加任务
_mManager.Add(t售前任务组);
_mManager.Add(t项目启动);
_mManager.Add(t需求调研);
_mManager.Add(t方案撰写);
_mManager.Add(t方案确认);
var t开发阶段 = new MyTask(_mManager) { Name = "开发阶段" };
var tUI设计 = new MyTask(_mManager) { Name = "UI设计" };
var t数据库设计 = new MyTask(_mManager) { Name = "数据库设计" };
var t编码 = new MyTask(_mManager) { Name = "编码" };
var t测试 = new MyTask(_mManager) { Name = "测试" };
_mManager.Add(t开发阶段);
_mManager.Add(tUI设计);
_mManager.Add(t数据库设计);
_mManager.Add(t编码);
_mManager.Add(t测试);
var t实施阶段 = new MyTask(_mManager) { Name = "实施阶段" };
var t系统安装和初始化 = new MyTask(_mManager) { Name = "系统安装和初始化" };
var t系统培训 = new MyTask(_mManager) { Name = "系统培训" };
var t系统验收 = new MyTask(_mManager) { Name = "系统验收" };
var t结项 = new MyTask(_mManager) { Name = "结项" };
_mManager.Add(t实施阶段);
_mManager.Add(t系统安装和初始化);
_mManager.Add(t系统培训);
_mManager.Add(t系统验收);
_mManager.Add(t结项); // 用ProjectManager类SetDuration方法设置任务工期
_mManager.SetDuration(t项目启动, );
_mManager.SetDuration(t需求调研, );
_mManager.SetDuration(t方案撰写, );
_mManager.SetDuration(t方案确认, );
_mManager.SetDuration(tUI设计, );
_mManager.SetDuration(t数据库设计, ); //用ProjectManager类Split方法对任务进行分段
_mManager.Split(t数据库设计, new MyTask(_mManager), new MyTask(_mManager), ); // 设置任务的完成率
t项目启动.Complete = 0.9f;
t需求调研.Complete = 0.5f;
t方案撰写.Complete = 0.4f; // 给任务分组
_mManager.Group(p项目, t售前任务组);
_mManager.Group(p项目, t开发阶段);
_mManager.Group(p项目, t实施阶段); _mManager.Group(t售前任务组, t项目启动);
_mManager.Group(t售前任务组, t需求调研);
_mManager.Group(t售前任务组, t方案撰写);
_mManager.Group(t售前任务组, t方案确认);
//任务衔接关系设置,不支持类型
_mManager.Relate(t项目启动, t需求调研);
_mManager.Relate(t需求调研, t方案撰写);
_mManager.Relate(t方案撰写, t方案确认); _mManager.Group(t开发阶段, tUI设计);
_mManager.Group(t开发阶段, t数据库设计);
_mManager.Group(t开发阶段, t编码);
_mManager.Group(t开发阶段, t测试); _mManager.Relate(t方案确认, tUI设计); _mManager.Relate(tUI设计, t数据库设计);
_mManager.Relate(t数据库设计, t编码);
_mManager.Relate(t编码, t测试); _mManager.Group(t实施阶段, t系统安装和初始化);
_mManager.Group(t实施阶段, t系统培训);
_mManager.Group(t实施阶段, t系统验收);
_mManager.Group(t实施阶段, t结项); _mManager.Relate(t测试, t系统安装和初始化);
_mManager.Relate(t系统安装和初始化, t系统培训);
_mManager.Relate(t系统培训, t系统验收);
_mManager.Relate(t系统验收, t结项); // 创建并设置任务资源(人、材料、设备等),任务资源可自定义
var jake = new MyResource() { Name = "Jake" };
var peter = new MyResource() { Name = "Peter" };
var john = new MyResource() { Name = "John" };
var lucas = new MyResource() { Name = "Lucas" };
var james = new MyResource() { Name = "James" };
var mary = new MyResource() { Name = "Mary" };
// 给任务分配资源
_mManager.Assign(t项目启动, jake);
_mManager.Assign(t项目启动, peter);
_mManager.Assign(t项目启动, john);
_mManager.Assign(t需求调研, jake);
_mManager.Assign(t需求调研, james);
_mManager.Assign(t数据库设计, james);
_mManager.Assign(t数据库设计, lucas);
_mManager.Assign(t方案撰写, mary);
_mManager.Assign(t方案撰写, lucas);
_mManager.Assign(t方案撰写, john); //甘特图初始化并创建任务代理
_mChart.Init(_mManager);
_mChart.CreateTaskDelegate = delegate() { return new MyTask(_mManager); }; // 任务事件监听
_mChart.TaskMouseOver += new EventHandler<TaskMouseEventArgs>(_mChart_TaskMouseOver);
_mChart.TaskMouseOut += new EventHandler<TaskMouseEventArgs>(_mChart_TaskMouseOut);
_mChart.TaskSelected += new EventHandler<TaskMouseEventArgs>(_mChart_TaskSelected);
_mChart.PaintOverlay += _mOverlay.ChartOverlayPainter;
//允许任务拖拽
_mChart.AllowTaskDragDrop = true; // 设置任务提示内容,可自定义
_mChart.SetToolTip(t项目启动, string.Join(", ", _mManager.ResourcesOf(t项目启动).Select(x => (x as MyResource).Name)));
_mChart.SetToolTip(t需求调研, string.Join(", ", _mManager.ResourcesOf(t需求调研).Select(x => (x as MyResource).Name)));
_mChart.SetToolTip(t数据库设计, string.Join(", ", _mManager.ResourcesOf(t数据库设计).Select(x => (x as MyResource).Name)));
_mChart.SetToolTip(t方案撰写, string.Join(", ", _mManager.ResourcesOf(t方案撰写).Select(x => (x as MyResource).Name))); //设置图形日期信息
_mManager.TimeScale = TimeScale.Day;
var span = DateTime.Today - _mManager.Start;
_mManager.Now = (int)Math.Round(span.TotalDays);
// 设置图形标题显示日期格式
_mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek; // UI初始化
_InitExampleUI();
//隐藏说明(打印模式隐藏)
_mOverlay.PrintMode = true; } void _mChart_TaskSelected(object sender, TaskMouseEventArgs e)
{
_mTaskGrid.SelectedObjects = _mChart.SelectedTasks.Select(x => _mManager.IsPart(x) ? _mManager.SplitTaskOf(x) : x).ToArray();
_mResourceGrid.Items.Clear();
_mResourceGrid.Items.AddRange(_mManager.ResourcesOf(e.Task).Select(x => new ListViewItem(((MyResource)x).Name)).ToArray());
} void _mChart_TaskMouseOut(object sender, TaskMouseEventArgs e)
{
lblStatus.Text = "";
_mChart.Invalidate();
} void _mChart_TaskMouseOver(object sender, TaskMouseEventArgs e)
{
lblStatus.Text = string.Format("{0} to {1}", _mManager.GetDateTime(e.Task.Start).ToLongDateString(), _mManager.GetDateTime(e.Task.End).ToLongDateString());
_mChart.Invalidate();
}
/// <summary>
/// UI初始化
/// </summary>
private void _InitExampleUI()
{
//设置数据源
TaskGridView.DataSource = new BindingSource(_mManager.Tasks, null);
mnuFilePrint200.Click += (s, e) => _PrintDocument(2.0f);
mnuFilePrint150.Click += (s, e) => _PrintDocument(1.5f);
mnuFilePrint100.Click += (s, e) => _PrintDocument(1.0f);
mnuFilePrint80.Click += (s, e) => _PrintDocument(0.8f);
mnuFilePrint50.Click += (s, e) => _PrintDocument(0.5f);
mnuFilePrint25.Click += (s, e) => _PrintDocument(0.25f);
mnuFilePrint10.Click += (s, e) => _PrintDocument(0.1f); mnuFileImgPrint100.Click += (s, e) => _PrintImage(1.0f);
mnuFileImgPrint50.Click += (s, e) => _PrintImage(0.5f);
mnuFileImgPrint10.Click += (s, e) => _PrintImage(0.1f);
} #region 菜单设置 private void mnuFileSave_Click(object sender, EventArgs e)
{
using (var dialog = new SaveFileDialog())
{
dialog.InitialDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var fs = System.IO.File.OpenWrite(dialog.FileName))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
//序列化
bf.Serialize(fs, _mManager);
}
}
}
} private void mnuFileOpen_Click(object sender, EventArgs e)
{
using (var dialog = new OpenFileDialog())
{
dialog.InitialDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var fs = System.IO.File.OpenRead(dialog.FileName))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
//反序列化
_mManager = bf.Deserialize(fs) as ProjectManager;
if (_mManager == null)
{
MessageBox.Show("Unable to load ProjectManager. Data structure might have changed from previous verions", "Gantt Chart", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
_mChart.Init(_mManager);
_mChart.Invalidate();
}
}
}
}
} private void mnuFileExit_Click(object sender, EventArgs e)
{
this.Close();
} private void mnuViewDaysDayOfWeek_Click(object sender, EventArgs e)
{
_mManager.TimeScale = TimeScale.Day;
_mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek;
_mChart.Invalidate();
} private void mnuFileNew_Click(object sender, EventArgs e)
{
// start a new Project and init the chart with the project
_mManager = new ProjectManager();
_mManager.Add(new Task() { Name = "New Task" });
_mChart.Init(_mManager);
_mChart.Invalidate();
} private void mnuViewRelationships_Click(object sender, EventArgs e)
{
_mChart.ShowRelations = mnuViewRelationships.Checked = !mnuViewRelationships.Checked;
_mChart.Invalidate();
} private void mnuViewSlack_Click(object sender, EventArgs e)
{
_mChart.ShowSlack = mnuViewSlack.Checked = !mnuViewSlack.Checked;
_mChart.Invalidate();
}
/// <summary>
/// 甘特图快捷键说明
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void mnuViewIntructions_Click(object sender, EventArgs e)
{
_mOverlay.PrintMode = !(mnuViewIntructions.Checked = !mnuViewIntructions.Checked);
_mChart.Invalidate();
} #region Timescale Views
private void mnuViewDays_Click(object sender, EventArgs e)
{
_mManager.TimeScale = TimeScale.Day;
mnuViewDays.Checked = true;
mnuViewWeek.Checked = false;
_mChart.Invalidate();
} private void mnuViewWeek_Click(object sender, EventArgs e)
{
_mManager.TimeScale = TimeScale.Week;
mnuViewDays.Checked = false;
mnuViewWeek.Checked = true;
_mChart.Invalidate();
} private void mnuViewDayOfWeek_Click(object sender, EventArgs e)
{
_mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek;
mnuViewDayOfWeek.Checked = true;
mnuViewDayOfMonth.Checked = false;
mnuViewWeekOfYear.Checked = false;
_mChart.Invalidate();
} private void mnuViewDayOfMonth_Click(object sender, EventArgs e)
{
_mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfMonth;
mnuViewDayOfWeek.Checked = false;
mnuViewDayOfMonth.Checked = true;
mnuViewWeekOfYear.Checked = false;
_mChart.Invalidate();
} private void mnuViewWeekOfYear_Click(object sender, EventArgs e)
{
_mChart.TimeScaleDisplay = TimeScaleDisplay.WeekOfYear;
mnuViewDayOfWeek.Checked = false;
mnuViewDayOfMonth.Checked = false;
mnuViewWeekOfYear.Checked = true;
_mChart.Invalidate();
}
#endregion Timescale Views #endregion Main Menu #region Sidebar private void _mDateTimePicker_ValueChanged(object sender, EventArgs e)
{
_mManager.Start = _mStartDatePicker.Value;
var span = DateTime.Today - _mManager.Start;
_mManager.Now = (int)Math.Round(span.TotalDays);
if (_mManager.TimeScale == TimeScale.Week) _mManager.Now = (_mManager.Now % ) * ;
_mChart.Invalidate();
} private void _mPropertyGrid_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
{
_mChart.Invalidate();
} private void _mNowDatePicker_ValueChanged(object sender, EventArgs e)
{
TimeSpan span = _mNowDatePicker.Value - _mStartDatePicker.Value;
_mManager.Now = span.Days + ;
if (_mManager.TimeScale == TimeScale.Week) _mManager.Now = _mManager.Now / + (_mManager.Now % > ? : );
_mChart.Invalidate();
} private void _mScrollDatePicker_ValueChanged(object sender, EventArgs e)
{
_mChart.ScrollTo(_mScrollDatePicker.Value);
_mChart.Invalidate();
} private void _mTaskGridView_SelectionChanged(object sender, EventArgs e)
{
if (TaskGridView.SelectedRows.Count > )
{
var task = TaskGridView.SelectedRows[].DataBoundItem as Task;
_mChart.ScrollTo(task);
}
} #endregion Sidebar #region 打印设置 private void _PrintDocument(float scale)
{
using (var dialog = new PrintDialog())
{
dialog.Document = new System.Drawing.Printing.PrintDocument();
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
// set the print mode for the custom overlay painter so that we skip printing instructions
dialog.Document.BeginPrint += (s, arg) => _mOverlay.PrintMode = true;
dialog.Document.EndPrint += (s, arg) => _mOverlay.PrintMode = false; // tell chart to print to the document at the specified scale
_mChart.Print(dialog.Document, scale);
}
}
} private void _PrintImage(float scale)
{
using (var dialog = new SaveFileDialog())
{
dialog.Filter = "Bitmap (*.bmp) | *.bmp";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
// set the print mode for the custom overlay painter so that we skip printing instructions
_mOverlay.PrintMode = true;
// tell chart to print to the document at the specified scale var bitmap = _mChart.Print(scale);
_mOverlay.PrintMode = false; // restore printing overlays bitmap.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
}
}
} #endregion Print private void ExampleFull_Load(object sender, EventArgs e)
{ } } #region overlay painter
/// <summary>
/// An example of how to encapsulate a helper painter for painter additional features on Chart
/// </summary>
public class OverlayPainter
{
/// <summary>
/// Hook such a method to the chart paint event listeners
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void ChartOverlayPainter(object sender, ChartPaintEventArgs e)
{
// Don't want to print instructions to file
if (this.PrintMode) return; var g = e.Graphics;
var chart = e.Chart; // Demo: Static billboards begin -----------------------------------
// Demonstrate how to draw static billboards
// "push matrix" -- save our transformation matrix
e.Chart.BeginBillboardMode(e.Graphics); // draw mouse command instructions
int margin = ;
int left = ;
var color = chart.HeaderFormat.Color;
StringBuilder builder = new StringBuilder();
builder.AppendLine("THIS IS DRAWN BY A CUSTOM OVERLAY PAINTER TO SHOW DEFAULT MOUSE COMMANDS.");
builder.AppendLine("*******************************************************************************************************");
builder.AppendLine("Left Click - Select task and display properties in PropertyGrid");
builder.AppendLine("Left Mouse Drag - Change task starting point");
builder.AppendLine("Right Mouse Drag - Change task duration");
builder.AppendLine("Middle Mouse Drag - Change task complete percentage");
builder.AppendLine("Left Doubleclick - Toggle collaspe on task group");
builder.AppendLine("Right Doubleclick - Split task into task parts");
builder.AppendLine("Left Mouse Dragdrop onto another task - Group drag task under drop task");
builder.AppendLine("Right Mouse Dragdrop onto another task part - Join task parts");
builder.AppendLine("SHIFT + Left Mouse Dragdrop onto another task - Make drop task precedent of drag task");
builder.AppendLine("ALT + Left Dragdrop onto another task - Ungroup drag task from drop task / Remove drop task from drag task precedent list");
builder.AppendLine("SHIFT + Left Mouse Dragdrop - Order tasks");
builder.AppendLine("SHIFT + Middle Click - Create new task");
builder.AppendLine("ALT + Middle Click - Delete task");
builder.AppendLine("Left Doubleclick - Toggle collaspe on task group");
var size = g.MeasureString(builder.ToString(), e.Chart.Font);
var background = new Rectangle(left, chart.Height - margin, (int)size.Width, (int)size.Height);
background.Inflate(, );
g.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(background, Color.LightYellow, Color.Transparent, System.Drawing.Drawing2D.LinearGradientMode.Vertical), background);
g.DrawRectangle(Pens.Brown, background);
g.DrawString(builder.ToString(), chart.Font, color, new PointF(left, chart.Height - margin)); // "pop matrix" -- restore the previous matrix
e.Chart.EndBillboardMode(e.Graphics);
// Demo: Static billboards end -----------------------------------
} public bool PrintMode { get; set; }
}
#endregion overlay painter #region 自定义任务和资源类
/// <summary>
///自定义资源
/// </summary>
[Serializable]
public class MyResource
{
public string Name { get; set; }
}
/// <summary>
/// 自定义任务类,继承 Task 接口 (可选)
/// </summary>
[Serializable]
public class MyTask : Task
{
public MyTask(ProjectManager manager) : base()
{
Manager = manager;
} private ProjectManager Manager { get; set; } public new int Start { get { return base.Start; } set { Manager.SetStart(this, value); } }
public new int End { get { return base.End; } set { Manager.SetEnd(this, value); } }
public new int Duration { get { return base.Duration; } set { Manager.SetDuration(this, value); } }
public new float Complete { get { return base.Complete; } set { Manager.SetComplete(this, value); } }
}
#endregion
}

WindowsForm如何实现类似微软project软件的甘特图?的更多相关文章

  1. Project导入RedMine甘特图展示

    前端时间因公司业务需要使用RedMine来管理项目进度. 但是使用其自带的csv导入工具无法完成导入. 而我是java开发,并不会所谓的ruby语言,自然无法在段时间内完成此导入功能的修改. so,经 ...

  2. 微软Project Oxford帮助开发人员创建更智能的应用

    Oxford帮助开发人员创建更智能的应用" title="微软Project Oxford帮助开发人员创建更智能的应用"> 假设你是一名对关于健身的应用充满奇思妙想 ...

  3. CSS类似微软中国首页的竖向选项卡

    效果体验:http://hovertree.com/texiao/css/24/ 源码下载:http://hovertree.com/h/bjaf/hardklps.htm 代码如下: <!DO ...

  4. 《微软的软件测试之道》【PDF】下载

    <微软的软件测试之道>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382291 内容简介 书的第三部分探讨某些我们工作中使用过的 ...

  5. Microsoft Security Essential: 微软安全软件

    Microsoft Security Essential: 微软安全软件 这个杀毒软件终身免费

  6. 给力Mac下的思维整理软件,思维导图软件合辑

    给力Mac下的思维整理软件,思维导图软件合辑 1.Mindjet MindManager for mac 10.0.211 经典的头脑风暴思维导图软件 最新破解Mindjet MindManager ...

  7. 甘特图中与任务有关的操作(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 任务建好了不是马上就没事了,后面还有各种事,比如像我这种粗心的,冷不丁就会冒出个"通假字"什么的,难道 ...

  8. 什么是甘特图(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 名词解释:"甘特图(Gantt Chart)是一种图形化的项目活动及其他相关系统进度情况的水平方向的条状图.&q ...

  9. 2018-05-17-OAA-一种mermaid脚本驱动的软件项目模块图形化表述思路

    layout: post title: 2018-05-17-OAA-一种mermaid脚本驱动的软件项目模块图形化表述思路 key: 20180517 tags: OAA flow chart se ...

随机推荐

  1. java生成excel文件

    首先下载jexcelapi_2_6_12.tar.gz,解压后将里面的jxl.jar复制到WEB-INF/lib目录下面 String filePath = request.getParameter( ...

  2. Docker之Linux Cgroups

    Linux Cgroups介绍 上面是构建Linux容器的namespace技术,它帮进程隔离出自己单独的空间,但Docker又是怎么限制每个空间的大小,保证他们不会互相争抢呢?那么就要用到Linux ...

  3. 关于CPU Cache -- 程序员需要知道的那些事

    本文将介绍一些作为程序猿或者IT从业者应该知道的CPU Cache相关的知识.本章从"为什么会有CPU Cache","CPU Cache的大致设计架构",&q ...

  4. 整理的一些PHP面试题目

    1.strlen()和mb_strlen()的作用分别是什么? strlen()和mb_strlen()的作用都是来获取字符串的长度,其中strlen()只针对单字节编码字符,也就是计算字符串的总字节 ...

  5. 轻松自动化---selenium-webdriver(python) (七)

    本节知识点: 多层框架或窗口的定位: switch_to_frame() switch_to_window() 智能等待: implicitly_wait() 对于一个现代的web应用,经常会出现框架 ...

  6. .Net常见错误

    常见错误 #1: 把引用当做值来用,或者反过来 C++ 和其他很多语言的程序员,习惯了给变量赋值的时候,要么赋单纯的值,要么是现有对象的引用.然而,在C# 中,是值还是引用,是由写这个对象的程序员决定 ...

  7. Azure Backup (1) 将SQL Server 2012虚拟机中数据库备份到Azure Storage

    <Windows Azure Platform 系列文章目录> 本文介绍的是由国内世纪互联运维的China Azure,相比国外的Global Azure.主要区别是存储账号的DNS地址不 ...

  8. Azure REST API (2) Azure Storage

    <Windows Azure Platform 系列文章目录> 注意:本文适用于国内由世纪互联运维的Azure China. 本文将会介绍如何使用REST API来直接访问Storage ...

  9. oracle创建表相关

    --创建表 create table person( id number primary key, name ), birth date ); --创建序列 create sequence perso ...

  10. js基础篇——cookie使用要点

    1.Cookie数量和长度的限制.各个浏览器的限制不同IE7+和Firefox最大限制为50条,chrome和Safari无限制,IE6-最大限制20条.且所有浏览器限制每个cookie长度不能超过4 ...