一款开源且功能强大的C#甘特图控件.NET Winforms Gantt Chart Control
一款开源且功能强大的C#甘特图控件.NET Winforms Gantt Chart Control
甘特图在项目管理中非常重要,甘特图的思想比较简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间。它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比。管理者由此可便利地弄清一项任务(项目)还剩下哪些工作要做,并可评估工作进度。甘特图可以显示几个部门、机器或设备的运行和闲置情况。这表示了该系统的有关工作负荷状况,这样可使管理人员了解何种调整是恰当的。
由于项目需要,在网上找了很久,经过大量的对比和评估,发现一款真正开源且功能强大的C#甘特图控件.NET Winforms Gantt Chart Control(http://ganttchart.codeplex.com/),效果图如下:
该款甘特图控件具有如下特征:
1、独立的时间单位数据结构规范;
2、支持单任务,分组任务,先例/依赖的任务,可以对任务进行拆分,并附加资源信息;
3、打印支持;
4、可对任务的计划和实际进行对比,以百分比进行进度跟踪;
5、在直接在甘特图上,对各种鼠标事件进行UI定制;
6、可以通过继承来修改默认的鼠标命令;
7、支持关键路径。
官方演示代码为:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9
10 namespace Braincase.GanttChart
11 {
12 /// <summary>
13 /// An elaborate example on how the chart control might be used.
14 /// Start by collapsing all regions and then read the constructor.
15 /// Refer to IProjectManager interface for method description.
16 /// </summary>
17 public partial class ExampleFull : Form
18 {
19 OverlayPainter _mOverlay = new OverlayPainter();
20
21 ProjectManager _mManager = null;
22
23 /// <summary>
24 /// Example starts here
25 /// </summary>
26 public ExampleFull()
27 {
28 InitializeComponent();
29
30 // Create a Project and some Tasks
31 _mManager = new ProjectManager();
32 var work = new MyTask(_mManager) { Name = "Prepare for Work" };
33 var wake = new MyTask(_mManager) { Name = "Wake Up" };
34 var teeth = new MyTask(_mManager) { Name = "Brush Teeth" };
35 var shower = new MyTask(_mManager) { Name = "Shower" };
36 var clothes = new MyTask(_mManager) { Name = "Change into New Clothes" };
37 var hair = new MyTask(_mManager) { Name = "Blow My Hair" };
38 var pack = new MyTask(_mManager) { Name = "Pack the Suitcase" };
39
40 _mManager.Add(work);
41 _mManager.Add(wake);
42 _mManager.Add(teeth);
43 _mManager.Add(shower);
44 _mManager.Add(clothes);
45 _mManager.Add(hair);
46 _mManager.Add(pack);
47
48 // Create another 1000 tasks for stress testing
49 Random rand = new Random();
50 for (int i = 0; i < 1000; i++)
51 {
52 var task = new MyTask(_mManager) { Name = string.Format("New Task {0}", i.ToString()) };
53 _mManager.Add(task);
54 _mManager.SetStart(task, rand.Next(300));
55 _mManager.SetDuration(task, rand.Next(50));
56 }
57
58 // Set task durations, e.g. using ProjectManager methods
59 _mManager.SetDuration(wake, 3);
60 _mManager.SetDuration(teeth, 5);
61 _mManager.SetDuration(shower, 7);
62 _mManager.SetDuration(clothes, 4);
63 _mManager.SetDuration(hair, 3);
64 _mManager.SetDuration(pack, 5);
65
66 // demostrate splitting a task
67 _mManager.Split(pack, new MyTask(_mManager), new MyTask(_mManager), 2);
68
69 // Set task complete status, e.g. using newly created properties
70 wake.Complete = 0.9f;
71 teeth.Complete = 0.5f;
72 shower.Complete = 0.4f;
73
74 // Give the Tasks some organisation, setting group and precedents
75 _mManager.Group(work, wake);
76 _mManager.Group(work, teeth);
77 _mManager.Group(work, shower);
78 _mManager.Group(work, clothes);
79 _mManager.Group(work, hair);
80 _mManager.Group(work, pack);
81 _mManager.Relate(wake, teeth);
82 _mManager.Relate(wake, shower);
83 _mManager.Relate(shower, clothes);
84 _mManager.Relate(shower, hair);
85 _mManager.Relate(hair, pack);
86 _mManager.Relate(clothes, pack);
87
88 // Create and assign Resources.
89 // MyResource is just custom user class. The API can accept any object as resource.
90 var jake = new MyResource() { Name = "Jake" };
91 var peter = new MyResource() { Name = "Peter" };
92 var john = new MyResource() { Name = "John" };
93 var lucas = new MyResource() { Name = "Lucas" };
94 var james = new MyResource() { Name = "James" };
95 var mary = new MyResource() { Name = "Mary" };
96 // Add some resources
97 _mManager.Assign(wake, jake);
98 _mManager.Assign(wake, peter);
99 _mManager.Assign(wake, john);
100 _mManager.Assign(teeth, jake);
101 _mManager.Assign(teeth, james);
102 _mManager.Assign(pack, james);
103 _mManager.Assign(pack, lucas);
104 _mManager.Assign(shower, mary);
105 _mManager.Assign(shower, lucas);
106 _mManager.Assign(shower, john);
107
108 // Initialize the Chart with our ProjectManager and CreateTaskDelegate
109 _mChart.Init(_mManager);
110 _mChart.CreateTaskDelegate = delegate() { return new MyTask(_mManager); };
111
112 // Attach event listeners for events we are interested in
113 _mChart.TaskMouseOver += new EventHandler<TaskMouseEventArgs>(_mChart_TaskMouseOver);
114 _mChart.TaskMouseOut += new EventHandler<TaskMouseEventArgs>(_mChart_TaskMouseOut);
115 _mChart.TaskSelected += new EventHandler<TaskMouseEventArgs>(_mChart_TaskSelected);
116 _mChart.PaintOverlay += _mOverlay.ChartOverlayPainter;
117 _mChart.AllowTaskDragDrop = true;
118
119 // set some tooltips to show the resources in each task
120 _mChart.SetToolTip(wake, string.Join(", ", _mManager.ResourcesOf(wake).Select(x => (x as MyResource).Name)));
121 _mChart.SetToolTip(teeth, string.Join(", ", _mManager.ResourcesOf(teeth).Select(x => (x as MyResource).Name)));
122 _mChart.SetToolTip(pack, string.Join(", ", _mManager.ResourcesOf(pack).Select(x => (x as MyResource).Name)));
123 _mChart.SetToolTip(shower, string.Join(", ", _mManager.ResourcesOf(shower).Select(x => (x as MyResource).Name)));
124
125 // Set Time information
126 _mManager.TimeScale = TimeScale.Day;
127 var span = DateTime.Today - _mManager.Start;
128 _mManager.Now = (int)Math.Round(span.TotalDays); // set the "Now" marker at the correct date
129 _mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek; // Set the chart to display days of week in header
130
131 // Init the rest of the UI
132 _InitExampleUI();
133 }
134
135 void _mChart_TaskSelected(object sender, TaskMouseEventArgs e)
136 {
137 _mTaskGrid.SelectedObjects = _mChart.SelectedTasks.Select(x => _mManager.IsPart(x) ? _mManager.SplitTaskOf(x) : x).ToArray();
138 _mResourceGrid.Items.Clear();
139 _mResourceGrid.Items.AddRange(_mManager.ResourcesOf(e.Task).Select(x => new ListViewItem(((MyResource)x).Name)).ToArray());
140 }
141
142 void _mChart_TaskMouseOut(object sender, TaskMouseEventArgs e)
143 {
144 lblStatus.Text = "";
145 _mChart.Invalidate();
146 }
147
148 void _mChart_TaskMouseOver(object sender, TaskMouseEventArgs e)
149 {
150 lblStatus.Text = string.Format("{0} to {1}", _mManager.GetDateTime(e.Task.Start).ToLongDateString(), _mManager.GetDateTime(e.Task.End).ToLongDateString());
151 _mChart.Invalidate();
152 }
153
154 private void _InitExampleUI()
155 {
156 TaskGridView.DataSource = new BindingSource(_mManager.Tasks, null);
157 mnuFilePrint200.Click += (s, e) => _PrintDocument(2.0f);
158 mnuFilePrint150.Click += (s, e) => _PrintDocument(1.5f);
159 mnuFilePrint100.Click += (s, e) => _PrintDocument(1.0f);
160 mnuFilePrint80.Click += (s, e) => _PrintDocument(0.8f);
161 mnuFilePrint50.Click += (s, e) => _PrintDocument(0.5f);
162 mnuFilePrint25.Click += (s, e) => _PrintDocument(0.25f);
163 mnuFilePrint10.Click += (s, e) => _PrintDocument(0.1f);
164
165 mnuFileImgPrint100.Click += (s, e) => _PrintImage(1.0f);
166 mnuFileImgPrint50.Click += (s, e) => _PrintImage(0.5f);
167 mnuFileImgPrint10.Click += (s, e) => _PrintImage(0.1f);
168 }
169
170 #region Main Menu
171
172 private void mnuFileSave_Click(object sender, EventArgs e)
173 {
174 using (var dialog = new SaveFileDialog())
175 {
176 dialog.InitialDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
177 if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
178 {
179 using (var fs = System.IO.File.OpenWrite(dialog.FileName))
180 {
181 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
182 bf.Serialize(fs, _mManager);
183 }
184 }
185 }
186 }
187
188 private void mnuFileOpen_Click(object sender, EventArgs e)
189 {
190 using (var dialog = new OpenFileDialog())
191 {
192 dialog.InitialDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
193 if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
194 {
195 using (var fs = System.IO.File.OpenRead(dialog.FileName))
196 {
197 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
198 _mManager = bf.Deserialize(fs) as ProjectManager;
199 if (_mManager == null)
200 {
201 MessageBox.Show("Unable to load ProjectManager. Data structure might have changed from previous verions", "Gantt Chart", MessageBoxButtons.OK, MessageBoxIcon.Error);
202 }
203 else
204 {
205 _mChart.Init(_mManager);
206 _mChart.Invalidate();
207 }
208 }
209 }
210 }
211 }
212
213 private void mnuFileExit_Click(object sender, EventArgs e)
214 {
215 this.Close();
216 }
217
218 private void mnuViewDaysDayOfWeek_Click(object sender, EventArgs e)
219 {
220 _mManager.TimeScale = TimeScale.Day;
221 _mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek;
222 _mChart.Invalidate();
223 }
224
225 private void mnuFileNew_Click(object sender, EventArgs e)
226 {
227 // start a new Project and init the chart with the project
228 _mManager = new ProjectManager();
229 _mManager.Add(new Task() { Name = "New Task" });
230 _mChart.Init(_mManager);
231 _mChart.Invalidate();
232 }
233
234 private void mnuHelpAbout_Click(object sender, EventArgs e)
235 {
236 if (MessageBox.Show("Please visit http://www.jakesee.com/net-c-winforms-gantt-chart-control/ for more help and details", "Braincase Solutions - Gantt Chart", MessageBoxButtons.OKCancel) == System.Windows.Forms.DialogResult.OK)
237 {
238 System.Diagnostics.Process.Start("http://www.jakesee.com/net-c-winforms-gantt-chart-control/");
239 }
240 }
241
242 private void mnuViewRelationships_Click(object sender, EventArgs e)
243 {
244 _mChart.ShowRelations = mnuViewRelationships.Checked = !mnuViewRelationships.Checked;
245 _mChart.Invalidate();
246 }
247
248 private void mnuViewSlack_Click(object sender, EventArgs e)
249 {
250 _mChart.ShowSlack = mnuViewSlack.Checked = !mnuViewSlack.Checked;
251 _mChart.Invalidate();
252 }
253
254 private void mnuViewIntructions_Click(object sender, EventArgs e)
255 {
256 _mOverlay.PrintMode = !(mnuViewIntructions.Checked = !mnuViewIntructions.Checked);
257 _mChart.Invalidate();
258 }
259
260 #region Timescale Views
261 private void mnuViewDays_Click(object sender, EventArgs e)
262 {
263 _mManager.TimeScale = TimeScale.Day;
264 mnuViewDays.Checked = true;
265 mnuViewWeek.Checked = false;
266 _mChart.Invalidate();
267 }
268
269 private void mnuViewWeek_Click(object sender, EventArgs e)
270 {
271 _mManager.TimeScale = TimeScale.Week;
272 mnuViewDays.Checked = false;
273 mnuViewWeek.Checked = true;
274 _mChart.Invalidate();
275 }
276
277 private void mnuViewDayOfWeek_Click(object sender, EventArgs e)
278 {
279 _mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfWeek;
280 mnuViewDayOfWeek.Checked = true;
281 mnuViewDayOfMonth.Checked = false;
282 mnuViewWeekOfYear.Checked = false;
283 _mChart.Invalidate();
284 }
285
286 private void mnuViewDayOfMonth_Click(object sender, EventArgs e)
287 {
288 _mChart.TimeScaleDisplay = TimeScaleDisplay.DayOfMonth;
289 mnuViewDayOfWeek.Checked = false;
290 mnuViewDayOfMonth.Checked = true;
291 mnuViewWeekOfYear.Checked = false;
292 _mChart.Invalidate();
293 }
294
295 private void mnuViewWeekOfYear_Click(object sender, EventArgs e)
296 {
297 _mChart.TimeScaleDisplay = TimeScaleDisplay.WeekOfYear;
298 mnuViewDayOfWeek.Checked = false;
299 mnuViewDayOfMonth.Checked = false;
300 mnuViewWeekOfYear.Checked = true;
301 _mChart.Invalidate();
302 }
303 #endregion Timescale Views
304
305 #endregion Main Menu
306
307 #region Sidebar
308
309 private void _mDateTimePicker_ValueChanged(object sender, EventArgs e)
310 {
311 _mManager.Start = _mStartDatePicker.Value;
312 var span = DateTime.Today - _mManager.Start;
313 _mManager.Now = (int)Math.Round(span.TotalDays);
314 if (_mManager.TimeScale == TimeScale.Week) _mManager.Now = (_mManager.Now % 7) * 7;
315 _mChart.Invalidate();
316 }
317
318 private void _mPropertyGrid_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
319 {
320 _mChart.Invalidate();
321 }
322
323 private void _mNowDatePicker_ValueChanged(object sender, EventArgs e)
324 {
325 TimeSpan span = _mNowDatePicker.Value - _mStartDatePicker.Value;
326 _mManager.Now = span.Days + 1;
327 if (_mManager.TimeScale == TimeScale.Week) _mManager.Now = _mManager.Now / 7 + (_mManager.Now % 7 > 0 ? 1 : 0);
328 _mChart.Invalidate();
329 }
330
331 private void _mScrollDatePicker_ValueChanged(object sender, EventArgs e)
332 {
333 _mChart.ScrollTo(_mScrollDatePicker.Value);
334 _mChart.Invalidate();
335 }
336
337 private void _mTaskGridView_SelectionChanged(object sender, EventArgs e)
338 {
339 if (TaskGridView.SelectedRows.Count > 0)
340 {
341 var task = TaskGridView.SelectedRows[0].DataBoundItem as Task;
342 _mChart.ScrollTo(task);
343 }
344 }
345
346 #endregion Sidebar
347
348 #region Print
349
350 private void _PrintDocument(float scale)
351 {
352 using (var dialog = new PrintDialog())
353 {
354 dialog.Document = new System.Drawing.Printing.PrintDocument();
355 if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
356 {
357 // set the print mode for the custom overlay painter so that we skip printing instructions
358 dialog.Document.BeginPrint += (s, arg) => _mOverlay.PrintMode = true;
359 dialog.Document.EndPrint += (s, arg) => _mOverlay.PrintMode = false;
360
361 // tell chart to print to the document at the specified scale
362 _mChart.Print(dialog.Document, scale);
363 }
364 }
365 }
366
367 private void _PrintImage(float scale)
368 {
369 using (var dialog = new SaveFileDialog())
370 {
371 dialog.Filter = "Bitmap (*.bmp) | *.bmp";
372 if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
373 {
374 // set the print mode for the custom overlay painter so that we skip printing instructions
375 _mOverlay.PrintMode = true;
376 // tell chart to print to the document at the specified scale
377
378 var bitmap = _mChart.Print(scale);
379 _mOverlay.PrintMode = false; // restore printing overlays
380
381 bitmap.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
382 }
383 }
384 }
385
386 #endregion Print
387
388
389 }
390
391 #region overlay painter
392 /// <summary>
393 /// An example of how to encapsulate a helper painter for painter additional features on Chart
394 /// </summary>
395 public class OverlayPainter
396 {
397 /// <summary>
398 /// Hook such a method to the chart paint event listeners
399 /// </summary>
400 /// <param name="sender"></param>
401 /// <param name="e"></param>
402 public void ChartOverlayPainter(object sender, ChartPaintEventArgs e)
403 {
404 // Don't want to print instructions to file
405 if (this.PrintMode) return;
406
407 var g = e.Graphics;
408 var chart = e.Chart;
409
410 // Demo: Static billboards begin -----------------------------------
411 // Demonstrate how to draw static billboards
412 // "push matrix" -- save our transformation matrix
413 e.Chart.BeginBillboardMode(e.Graphics);
414
415 // draw mouse command instructions
416 int margin = 300;
417 int left = 20;
418 var color = chart.HeaderFormat.Color;
419 StringBuilder builder = new StringBuilder();
420 builder.AppendLine("THIS IS DRAWN BY A CUSTOM OVERLAY PAINTER TO SHOW DEFAULT MOUSE COMMANDS.");
421 builder.AppendLine("*******************************************************************************************************");
422 builder.AppendLine("Left Click - Select task and display properties in PropertyGrid");
423 builder.AppendLine("Left Mouse Drag - Change task starting point");
424 builder.AppendLine("Right Mouse Drag - Change task duration");
425 builder.AppendLine("Middle Mouse Drag - Change task complete percentage");
426 builder.AppendLine("Left Doubleclick - Toggle collaspe on task group");
427 builder.AppendLine("Right Doubleclick - Split task into task parts");
428 builder.AppendLine("Left Mouse Dragdrop onto another task - Group drag task under drop task");
429 builder.AppendLine("Right Mouse Dragdrop onto another task part - Join task parts");
430 builder.AppendLine("SHIFT + Left Mouse Dragdrop onto another task - Make drop task precedent of drag task");
431 builder.AppendLine("ALT + Left Dragdrop onto another task - Ungroup drag task from drop task / Remove drop task from drag task precedent list");
432 builder.AppendLine("SHIFT + Left Mouse Dragdrop - Order tasks");
433 builder.AppendLine("SHIFT + Middle Click - Create new task");
434 builder.AppendLine("ALT + Middle Click - Delete task");
435 builder.AppendLine("Left Doubleclick - Toggle collaspe on task group");
436 var size = g.MeasureString(builder.ToString(), e.Chart.Font);
437 var background = new Rectangle(left, chart.Height - margin, (int)size.Width, (int)size.Height);
438 background.Inflate(10, 10);
439 g.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(background, Color.LightYellow, Color.Transparent, System.Drawing.Drawing2D.LinearGradientMode.Vertical), background);
440 g.DrawRectangle(Pens.Brown, background);
441 g.DrawString(builder.ToString(), chart.Font, color, new PointF(left, chart.Height - margin));
442
443
444 // "pop matrix" -- restore the previous matrix
445 e.Chart.EndBillboardMode(e.Graphics);
446 // Demo: Static billboards end -----------------------------------
447 }
448
449 public bool PrintMode { get; set; }
450 }
451 #endregion overlay painter
452
453 #region custom task and resource
454 /// <summary>
455 /// A custom resource of your own type (optional)
456 /// </summary>
457 [Serializable]
458 public class MyResource
459 {
460 public string Name { get; set; }
461 }
462 /// <summary>
463 /// A custom task of your own type deriving from the Task interface (optional)
464 /// </summary>
465 [Serializable]
466 public class MyTask : Task
467 {
468 public MyTask(ProjectManager manager)
469 : base()
470 {
471 Manager = manager;
472 }
473
474 private ProjectManager Manager { get; set; }
475
476 public new int Start { get { return base.Start; } set { Manager.SetStart(this, value); } }
477 public new int End { get { return base.End; } set { Manager.SetEnd(this, value); } }
478 public new int Duration { get { return base.Duration; } set { Manager.SetDuration(this, value); } }
479 public new float Complete { get { return base.Complete; } set { Manager.SetComplete(this, value); } }
480 }
481 #endregion custom task and resource
482 }
演示效果图为:
水平有限,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!
出处:http://www.cnblogs.com/isaboy/
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号