diff --git a/GanttChart/GanttChart.bmp b/GanttChart/GanttChart.bmp new file mode 100644 index 0000000..59b77fa Binary files /dev/null and b/GanttChart/GanttChart.bmp differ diff --git a/GanttChart/GanttChart.csproj b/GanttChart/GanttChart.csproj new file mode 100644 index 0000000..7d3b358 --- /dev/null +++ b/GanttChart/GanttChart.csproj @@ -0,0 +1,105 @@ + + + + + Debug + AnyCPU + {DEA91CB9-1011-4EE7-B8E1-72428F731789} + Library + GanttChart + GanttChart + v4.7.2 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + Component + + + Component + + + Component + + + Component + + + Component + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/GanttChart/GanttChartBrushes.cs b/GanttChart/GanttChartBrushes.cs new file mode 100644 index 0000000..ef2cceb --- /dev/null +++ b/GanttChart/GanttChartBrushes.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图笔刷 + /// + class GanttChartBrushes: IDisposable + { + GanttChartView View { get; set; } + public GanttChartBrushes(GanttChartView view) + { + View = view; + this.GridBrush = new SolidBrush(view.GridColor); + + this.ColumnDateTextBrush = new SolidBrush(view.ColumnDateTextColor); + this.ColumnHourTextBrush = new SolidBrush(view.ColumnHourTextColor); + this.HeaderBackBrush = new SolidBrush(view.HeaderBackColor); + + this.BackBrush = new SolidBrush(view.BackColor); + this.ContentBackBrush = new SolidBrush(view.ContentBackColor); + + this.CurrentTimeLineBrush = new SolidBrush(view.CurrentTimeLineColor); + + this.ItemTextBrush = new SolidBrush(view.ItemTextColor); + this.ItemBackBrush = new SolidBrush(view.ItemBackColor); + this.ItemBorderBrush = new SolidBrush(view.ItemBorderColor); + this.ItemStartFlagBrush = new SolidBrush(view.ItemStartFlagColor); + this.ItemSubRangeBackBrush = new SolidBrush(view.ItemSubRangeBackColor); + this.ItemSelectionTextBrush = new SolidBrush(view.ItemSelectionTextColor); + this.ItemSelectionBackBrush = new SolidBrush(view.ItemSelectionBackColor); + this.ItemSelectionBorderBrush = new SolidBrush(view.ItemSelectionBorderColor); + this.ItemSelectionSubRangeBackBrush = new SolidBrush(view.ItemSelectionSubRangeBackColor); + this.ItemWarnningTextBrush = new SolidBrush(view.ItemWarnningTextColor); + + this.RowHeaderTextBrush = new SolidBrush(view.RowHeaderTextColor); + this.RowSelectionHeaderTextBrush = new SolidBrush(view.RowSelectionHeaderTextColor); + this.RowSelectionBorderBrush = new SolidBrush(view.RowSelectionBorderColor); + this.RowSelectionHeaderBackBrush = new SolidBrush(view.RowSelectionHeaderBackColor); + this.RowSelectionContentBackBrush = new SolidBrush(view.RowSelectionContentBackColor); + + this.TooltipTextBrush = new SolidBrush(view.TooltipTextColor); + this.TooltipBorderBrush = new SolidBrush(view.TooltipBorderColor); + this.TooltipBackBrush = new SolidBrush(view.TooltipBackColor); + + this.TimeRangeSelecetionBackBrush = new SolidBrush(view.TimeRangeSelecetionBackColor); + this.SelectionRectangleBackBrush = new SolidBrush(view.SelectionRectangleBackColor); + + this.GridPen = new Pen(this.GridBrush, 1); + this.CurrentTimeLinePen = new Pen(this.CurrentTimeLineBrush, 1); + this.ItemBorderPen = new Pen(this.ItemBorderBrush, 1); + this.ItemBackPen = new Pen(this.ItemBackBrush, 1); + this.ItemSelectionBorderPen = new Pen(this.ItemSelectionBorderBrush, 1); + this.RowSelectionBorderPen = new Pen(this.RowSelectionBorderBrush, 1); + this.TooltipBorderPen = new Pen(this.TooltipBorderBrush, 1); + this.ItemStartFlagPen = new Pen(this.ItemStartFlagBrush, 2); + this.ItemSubRangeBackPen = new Pen(this.ItemSubRangeBackBrush, 1); + this.ItemSelectionSubRangeBackPen = new Pen(this.ItemSelectionSubRangeBackBrush, 1); + } + public SolidBrush GridBrush { get; set; } + + public SolidBrush ColumnDateTextBrush { get; set; } + public SolidBrush ColumnHourTextBrush { get; set; } + public SolidBrush HeaderBackBrush { get; set; } + + public SolidBrush BackBrush { get; set; } + public SolidBrush ContentBackBrush { get; set; } + + public SolidBrush CurrentTimeLineBrush { get; set; } + + public SolidBrush ItemTextBrush { get; set; } + public SolidBrush ItemBackBrush { get; set; } + public SolidBrush ItemBorderBrush { get; set; } + public SolidBrush ItemStartFlagBrush { get; set; } + public SolidBrush ItemSubRangeBackBrush { get; set; } + public SolidBrush ItemSelectionTextBrush { get; set; } + public SolidBrush ItemSelectionBackBrush { get; set; } + public SolidBrush ItemSelectionBorderBrush { get; set; } + public SolidBrush ItemSelectionSubRangeBackBrush { get; set; } + public SolidBrush ItemWarnningTextBrush { get; set; } + + + public SolidBrush RowHeaderTextBrush { get; set; } + public SolidBrush RowSelectionHeaderTextBrush { get; set; } + public SolidBrush RowSelectionBorderBrush { get; set; } + public SolidBrush RowSelectionHeaderBackBrush { get; set; } + public SolidBrush RowSelectionContentBackBrush { get; set; } + + public SolidBrush TooltipTextBrush { get; set; } + public SolidBrush TooltipBorderBrush { get; set; } + public SolidBrush TooltipBackBrush { get; set; } + + public SolidBrush TimeRangeSelecetionBackBrush { get; set; } + public SolidBrush SelectionRectangleBackBrush { get; set; } + + public Pen GridPen { get; } + public Pen CurrentTimeLinePen { get; } + public Pen ItemBorderPen { get; } + public Pen ItemBackPen { get; } + public Pen ItemSelectionBorderPen { get; } + public Pen RowSelectionBorderPen { get; } + public Pen TooltipBorderPen { get; } + public Pen ItemStartFlagPen { get; } + public Pen ItemSubRangeBackPen { get; } + public Pen ItemSelectionSubRangeBackPen { get; } + + public void Refresh() + { + this.GridBrush.Color = View.GridColor; + + this.ColumnDateTextBrush.Color = View.ColumnDateTextColor; + this.ColumnHourTextBrush.Color = View.ColumnHourTextColor; + this.HeaderBackBrush.Color = View.HeaderBackColor; + + this.ContentBackBrush.Color = View.ContentBackColor; + + this.CurrentTimeLineBrush.Color = View.CurrentTimeLineColor; + + this.ItemTextBrush.Color = View.ItemTextColor; + this.ItemBackBrush.Color = View.ItemBackColor; + this.ItemBorderBrush.Color = View.ItemBorderColor; + this.ItemStartFlagBrush.Color = View.ItemStartFlagColor; + this.ItemSubRangeBackBrush.Color = View.ItemSubRangeBackColor; + this.ItemSelectionTextBrush.Color = View.ItemSelectionTextColor; + this.ItemSelectionBackBrush.Color = View.ItemSelectionBackColor; + this.ItemSelectionBorderBrush.Color = View.ItemSelectionBorderColor; + this.ItemSelectionSubRangeBackBrush.Color = View.ItemSelectionSubRangeBackColor; + this.ItemWarnningTextBrush.Color = View.ItemWarnningTextColor; + + this.RowHeaderTextBrush.Color = View.RowHeaderTextColor; + this.RowSelectionHeaderTextBrush.Color = View.RowSelectionHeaderTextColor; + this.RowSelectionBorderBrush.Color = View.RowSelectionBorderColor; + this.RowSelectionHeaderBackBrush.Color = View.RowSelectionHeaderBackColor; + this.RowSelectionContentBackBrush.Color = View.RowSelectionContentBackColor; + + this.TooltipTextBrush.Color = View.TooltipTextColor; + this.TooltipBorderBrush.Color = View.TooltipBorderColor; + this.TooltipBackBrush.Color = View.TooltipBackColor; + + this.TimeRangeSelecetionBackBrush.Color = View.TimeRangeSelecetionBackColor; + this.SelectionRectangleBackBrush.Color = View.SelectionRectangleBackColor; + } + + public void Dispose() + { + this.GridPen.Dispose(); + this.CurrentTimeLinePen.Dispose(); + this.ItemBorderPen.Dispose(); + this.ItemBackPen.Dispose(); + this.ItemSelectionBorderPen.Dispose(); + this.RowSelectionBorderPen.Dispose(); + this.TooltipBorderPen.Dispose(); + this.ItemStartFlagPen.Dispose(); + this.ItemSubRangeBackPen.Dispose(); + this.ItemSelectionSubRangeBackPen.Dispose(); + + this.BackBrush.Dispose(); + this.GridBrush.Dispose(); + + this.ColumnDateTextBrush.Dispose(); + this.ColumnHourTextBrush.Dispose(); + this.HeaderBackBrush.Dispose(); + + this.ContentBackBrush.Dispose(); + + this.CurrentTimeLineBrush.Dispose(); + + this.ItemTextBrush.Dispose(); + this.ItemBackBrush.Dispose(); + this.ItemBorderBrush.Dispose(); + this.ItemStartFlagBrush.Dispose(); + this.ItemSubRangeBackBrush.Dispose(); + this.ItemSelectionTextBrush.Dispose(); + this.ItemSelectionBackBrush.Dispose(); + this.ItemSelectionBorderBrush.Dispose(); + this.ItemSelectionSubRangeBackBrush.Dispose(); + this.ItemWarnningTextBrush.Dispose(); + + this.RowHeaderTextBrush.Dispose(); + this.RowSelectionHeaderTextBrush.Dispose(); + this.RowSelectionBorderBrush.Dispose(); + this.RowSelectionHeaderBackBrush.Dispose(); + this.RowSelectionContentBackBrush.Dispose(); + + this.TooltipTextBrush.Dispose(); + this.TooltipBorderBrush.Dispose(); + this.TooltipBackBrush.Dispose(); + + this.TimeRangeSelecetionBackBrush.Dispose(); + this.SelectionRectangleBackBrush.Dispose(); + } + } +} diff --git a/GanttChart/GanttChartCollection.cs b/GanttChart/GanttChartCollection.cs new file mode 100644 index 0000000..ea7c268 --- /dev/null +++ b/GanttChart/GanttChartCollection.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图像集合 + /// + /// + public class GanttChartCollection : ICollection + { + private List Items; + internal bool IsChanged { get; set; } + internal DateTime StartDate { get; set; } + public GanttChartCollection() + { + Items = new List(); + } + public GanttChartCollection(ICollection items):this() + { + Items.AddRange(items); + IsChanged = true; + } + public int Count => Items.Count; + + bool ICollection.IsReadOnly => false; + + public void Add(T item) + { + Items.Add(item); + IsChanged = true; + } + + public void Clear() + { + Items.Clear(); + IsChanged = false; + } + + public bool Contains(T item) => Items.Contains(item); + + public void CopyTo(T[] array, int arrayIndex) => Items.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() => Items.GetEnumerator(); + + public bool Remove(T item) + { + if (Items.Remove(item)) + { + IsChanged = true; + return true; + } + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Items.GetEnumerator(); + } + public T this[int index] + { + get + { + return Items[index]; + } + } + } +} diff --git a/GanttChart/GanttChartItemClickEventArgs.cs b/GanttChart/GanttChartItemClickEventArgs.cs new file mode 100644 index 0000000..a9f219b --- /dev/null +++ b/GanttChart/GanttChartItemClickEventArgs.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图项单击事件参数 + /// + public class GanttChartItemClickEventArgs : EventArgs + { + /// + /// 项 + /// + public GanttChartViewItem Item { get; private set; } + /// + /// 初始化 + /// + /// + public GanttChartItemClickEventArgs(GanttChartViewItem item) + { + Item = item; + } + } +} diff --git a/GanttChart/GanttChartItemDoubleClickEventArgs.cs b/GanttChart/GanttChartItemDoubleClickEventArgs.cs new file mode 100644 index 0000000..3400684 --- /dev/null +++ b/GanttChart/GanttChartItemDoubleClickEventArgs.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图项双击事件参数 + /// + public class GanttChartItemDoubleClickEventArgs : EventArgs + { + /// + /// 项 + /// + public GanttChartViewItem Item { get; private set; } + /// + /// 初始化 + /// + /// + public GanttChartItemDoubleClickEventArgs(GanttChartViewItem item) + { + Item = item; + } + } +} diff --git a/GanttChart/GanttChartPropertyChangeEventArgs.cs b/GanttChart/GanttChartPropertyChangeEventArgs.cs new file mode 100644 index 0000000..9bed63d --- /dev/null +++ b/GanttChart/GanttChartPropertyChangeEventArgs.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图属性改变事件参数 + /// + public class GanttChartPropertyChangeEventArgs : EventArgs + { + /// + /// 初始化 + /// + /// + /// + public GanttChartPropertyChangeEventArgs(string name, object value) + { + PropertyName = name; + PropertyValue = value; + } + /// + /// 属性名 + /// + public string PropertyName { get; set; } + /// + /// 属性值 + /// + public object PropertyValue { get; set; } + } +} diff --git a/GanttChart/GanttChartRangeSelectedEventArgs.cs b/GanttChart/GanttChartRangeSelectedEventArgs.cs new file mode 100644 index 0000000..9e1e715 --- /dev/null +++ b/GanttChart/GanttChartRangeSelectedEventArgs.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图范围选择事件参数 + /// + public class GanttChartRangeSelectedEventArgs : EventArgs + { + /// + /// 事件范围 + /// + public TimeRange Range { get; private set; } + /// + /// 初始化 + /// + /// + public GanttChartRangeSelectedEventArgs(TimeRange range) + { + Range = range; + } + /// + /// 初始化 + /// + /// + /// + public GanttChartRangeSelectedEventArgs(DateTime startTime, DateTime endTime) + { + Range = new TimeRange { StartTime = startTime, EndTime = endTime }; + } + } +} diff --git a/GanttChart/GanttChartRowSelectedEventArgs.cs b/GanttChart/GanttChartRowSelectedEventArgs.cs new file mode 100644 index 0000000..628b441 --- /dev/null +++ b/GanttChart/GanttChartRowSelectedEventArgs.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图行选中事件参数 + /// + public class GanttChartRowSelectedEventArgs : EventArgs + { + /// + /// 行 + /// + public GanttChartViewRow Row { get; private set; } + /// + /// 初始化 + /// + /// + public GanttChartRowSelectedEventArgs(GanttChartViewRow row) + { + Row = row; + } + } +} diff --git a/GanttChart/GanttChartView.Events.cs b/GanttChart/GanttChartView.Events.cs new file mode 100644 index 0000000..2cc5bf3 --- /dev/null +++ b/GanttChart/GanttChartView.Events.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace GanttChart +{ + /// + /// 甘特图控件 + /// 该部分类主要处理事件 + /// + public partial class GanttChartView + { + private bool _IsShiftPress = false; + private bool _IsCtrlPress = false; + private bool _IsAltPress = false; + private bool _IsMouseDown = false; + private bool _IsAjustSize = false; + private bool _IsDragItem = false; + private bool _IsShowTooltip = false; + private Point _DragStartLocation = Point.Empty; + private Point _DragEndLocation = Point.Empty; + private Point _ShowTooltipPoint = Point.Empty; + private Rectangle _SelectionRange = Rectangle.Empty; + private GanttChartViewItem _DragItem = null; + private GanttChartViewItem _ShowTooltipItem = null; + public event EventHandler PropertyChanged; + public event EventHandler ItemClick; + public event EventHandler ItemDoubleClick; + public event EventHandler RowSelected; + public event EventHandler RangeSelected; + + /// + /// 初始化事件 + /// + protected void InitEvent() + { + _VScrollBar.Scroll += new ScrollEventHandler(VScrollBar_Scroll); + _HScrollBar.Scroll += new ScrollEventHandler(HScrollBar_Scroll); + _ContentControl.Paint += OnContentControlPaint; + _ContentControl.MouseDown += OnMouseDown; + _ContentControl.MouseUp += OnMouseUp; + _ContentControl.MouseMove += OnMouseMove; + _ContentControl.MouseClick += OnMouseClick; + _ContentControl.MouseDoubleClick += OnMouseDoubleClick; + } + /// + /// 销毁事件 + /// + protected void DisposeEvent() + { + + } + /// + /// 键盘按下事件 + /// + /// + protected override void OnKeyDown(KeyEventArgs e) + { + _IsShiftPress = e.Shift; + _IsCtrlPress = e.Control; + _IsAltPress = e.Alt; + base.OnKeyDown(e); + } + /// + /// 键盘弹起事件 + /// + /// + protected override void OnKeyUp(KeyEventArgs e) + { + _IsShiftPress = e.Shift; + _IsCtrlPress = e.Control; + _IsAltPress = e.Alt; + base.OnKeyUp(e); + } + /// + /// 鼠标单击事件 + /// + /// + /// + protected void OnMouseClick(object sender, MouseEventArgs e) + { + if (_DragStartLocation != Point.Empty && e.X != _DragStartLocation.X && _DragStartLocation.Y != e.Y) + { + return; + } + if (IsHoverRowHeader(e.Location) || IsHoverRow(e.Location)) + { + _SelectionRow = GetRow(e.Location); + Invalidate(); + RowSelected?.Invoke(this, new GanttChartRowSelectedEventArgs(_SelectionRow)); + } + if (IsHoverItem(e.Location)) + { + _SelectionItem = GetItem(e.Location); + Invalidate(); + ItemClick?.Invoke(this, new GanttChartItemClickEventArgs(_SelectionItem)); + } + } + /// + /// 鼠标双击事件 + /// + /// + /// + protected void OnMouseDoubleClick(object sender, MouseEventArgs e) + { + if (IsHoverItem(e.Location)) + { + _SelectionItem = GetItem(e.Location); + Invalidate(); + if(ItemDoubleClick != null) + { + if (_IsShowTooltip) + { + _IsShowTooltip = false; + _ShowTooltipItem = null; + _ShowTooltipPoint = Point.Empty; + } + ItemDoubleClick(this, new GanttChartItemDoubleClickEventArgs(_SelectionItem)); + } + } + } + /// + /// 鼠标滚动事件 + /// + /// + protected override void OnMouseWheel(MouseEventArgs e) + { + if (_IsShowTooltip) + { + _IsShowTooltip = false; + _ShowTooltipItem = null; + _ShowTooltipPoint = Point.Empty; + } + //int i = e.Delta > 0 ? -1 : 1; + var scrollbar = _IsShiftPress ? _HScrollBar : (ScrollBar)_VScrollBar; + var h = scrollbar.Value - (int)(e.Delta * MouseWheelSensitivity); + if (h > scrollbar.Maximum) + { + h = scrollbar.Maximum; + } + if (h < scrollbar.Minimum) + { + h = scrollbar.Minimum; + } + scrollbar.Value = h; + Invalidate(); + base.OnMouseWheel(e); + } + /// + /// 鼠标按下事件 + /// + /// + /// + protected void OnMouseDown(object sender, MouseEventArgs e) + { + if (_SelectionRow != null || _SelectionItem != null || _DragItem != null) + { + _SelectionRow = null; + _SelectionItem = null; + _DragItem = null; + Invalidate(); + } + _IsMouseDown = e.Button == MouseButtons.Left && true; + if (_IsMouseDown) + { + _SelectionRange = Rectangle.Empty; + _SelectionTimeRange = null; + _DragStartLocation = e.Location; + if (IsHoverDateSplitLine(e.Location) && !IsHoverItem(e.Location)) + { + _IsAjustSize = true; + } + else if (IsHoverItem(e.Location)) + { + _IsDragItem = true; + _DragItem = GetItem(e.Location); + } + } + } + /// + /// 控件创建事件 + /// + protected override void OnCreateControl() + { + this.Brushes = new GanttChartBrushes(this); + _HScrollBar.Width = this.Width - VScrollWidth; + _VScrollBar.Height = this.Height - HScrollHeight; + AdjustScrollbar(); + base.OnCreateControl(); + } + /// + /// 控件大小变化事件 + /// + /// + protected override void OnSizeChanged(EventArgs e) + { + _HScrollBar.Width = this.Width - VScrollWidth; + _VScrollBar.Height = this.Height - HScrollHeight; + AdjustScrollbar(); + Invalidate(); + base.OnSizeChanged(e); + } + /// + /// 控件绘制事件 + /// + /// + /// + private void OnContentControlPaint(object sender, PaintEventArgs e) + { + int bottom = ColumnHeight + ItemHeight + RowSpacing * 2; + if (Rows != null && Rows.Any()) + { + var last = Rows.Last(); + bottom = last.Bottom; + } + DrawBack(e.Graphics, bottom); + e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; + DrawHeader(e.Graphics, bottom); + DrawColumnHeader(e.Graphics); + DrawTimeLine(e.Graphics, bottom); + + DrawSelectingRectangle(e.Graphics); + + DrawSelectionRange(e.Graphics); + + DrawRows(e.Graphics, bottom); + + DrawItemTooltip(e.Graphics); + } + /// + /// 鼠标弹起事件 + /// + /// + /// + protected void OnMouseUp(object sender, MouseEventArgs e) + { + if (_IsMouseDown) + { + if (_IsAjustSize) + { + _IsAjustSize = false; + var w = e.X - _DragStartLocation.X + DateWidth; + if (w < MinDateWidth) + { + DateWidth = MinDateWidth; + } + else + { + DateWidth = (w / 48 + (w % 48 == 0 ? 0 : 1)) * 48; + } + if(Rows != null) + { + Rows.IsChanged = true; + } + AdjustScrollbar(); + Invalidate(); + } + else if (_IsDragItem) + { + _IsDragItem = false; + //TODO:触发Drag事件 + Invalidate(); + } + else if(!_DragStartLocation.IsEmpty && e.X != _DragStartLocation.X && e.Y != _DragStartLocation.Y && _DragStartLocation.X > RowHeaderWidth && _DragStartLocation.Y > ColumnHeight) + { + _DragEndLocation = e.Location; + var row = GetRow(_DragStartLocation); + if (row != null) + { + var width = Math.Abs(_DragEndLocation.X - _DragStartLocation.X); + if (_DragEndLocation.X < RowHeaderWidth) + { + width -= RowHeaderWidth - _DragEndLocation.X; + } + else if (_DragEndLocation.X > ((int)(EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth + RowHeaderWidth - _HScrollBar.Value) + { + width = ((int)(EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth + RowHeaderWidth - _HScrollBar.Value - _DragStartLocation.X; + } + var start = new Point(Math.Min(_DragStartLocation.X, Math.Max(_DragEndLocation.X, RowHeaderWidth)), row.Top + 1); + _SelectionRange = new Rectangle(start, new Size(width, row.Bottom - row.Top - 2)); + + Invalidate(); + var mw = DateWidth / 24f / 60f; + var ts = StartDate.AddMinutes((int)((_DragStartLocation.X - RowHeaderWidth + _HScrollBar.Value) / mw)); + var te = StartDate.AddMinutes((int)((_DragEndLocation.X - RowHeaderWidth + _HScrollBar.Value) / mw)); + if (te < ts) + { + var tt = te; + te = ts; + ts = tt; + } + _SelectionTimeRange = new TimeRange { StartTime = ts, EndTime = te }; + RangeSelected?.Invoke(this, new GanttChartRangeSelectedEventArgs(ts, te)); + } + } + } + _IsMouseDown = false; + _DragStartLocation = Point.Empty; + _DragEndLocation = Point.Empty; + } + /// + /// 鼠标移动事件 + /// + /// + /// + protected void OnMouseMove(object sender, MouseEventArgs e) + { + if (_IsMouseDown) + { + if (_IsShowTooltip) + { + _IsShowTooltip = false; + _ShowTooltipItem = null; + _ShowTooltipPoint = Point.Empty; + + Invalidate(); + } + if (_IsDragItem) + { + + } + else if (_DragStartLocation.X > RowHeaderWidth && _DragStartLocation.Y > ColumnHeight) + { + //TODO: 画选择框 + _DragEndLocation = e.Location; + Invalidate(); + } + return; + } + else if (IsHoverDateSplitLine(e.Location) && !IsHoverItem(e.Location)) + { + Cursor = Cursors.VSplit; + } + //else if (IsHoverRowHeader(e)) + //{ + // Cursor = DefaultCursor; + //} + else if (IsHoverItem(e.Location)) + { + Cursor = Cursors.Hand; + //TODO:显示Tooltip + if (!_IsShowTooltip || GetItem(e.Location) != _ShowTooltipItem) + { + _IsShowTooltip = true; + _ShowTooltipItem = GetItem(e.Location); + _ShowTooltipPoint = ComputeTooltipLocation(e.Location); + + Invalidate(); + return; + } + else + { + return; + } + } + else + { + Cursor = DefaultCursor; + } + if (_IsShowTooltip) + { + _IsShowTooltip = false; + _ShowTooltipItem = null; + _ShowTooltipPoint = Point.Empty; + + Invalidate(); + } + } + + + /// + /// 横向滚动事件 + /// + /// + /// + private void HScrollBar_Scroll(object sender, ScrollEventArgs e) + { + Invalidate(); + } + /// + /// 纵向滚动事件 + /// + /// + /// + private void VScrollBar_Scroll(object sender, ScrollEventArgs e) + { + Invalidate(); + } + /// + /// 属性改变事件 + /// + /// + /// + private void OnPropertyChanged(string name, object value) + { + this.Brushes?.Refresh(); + var args = new GanttChartPropertyChangeEventArgs(name, value); + PropertyChanged?.Invoke(this, args); + if(name == nameof(BlockType)) + { + Rows.IsChanged = true; + } + AdjustScrollbar(); + Invalidate(); + } + } +} diff --git a/GanttChart/GanttChartView.Logic.cs b/GanttChart/GanttChartView.Logic.cs new file mode 100644 index 0000000..edea944 --- /dev/null +++ b/GanttChart/GanttChartView.Logic.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace GanttChart +{ + /// + /// 甘特图控件 + /// 该部分类处理甘特图逻辑 + /// + public partial class GanttChartView + { + /// + /// 鼠标是否经过日分割线 + /// + /// + /// + private bool IsHoverDateSplitLine(Point e) + { + var t = (e.X + _HScrollBar.Value - RowHeaderWidth + this.DateWidth) % this.DateWidth; + return e.X > this.RowHeaderWidth + 3 && e.X < ((EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth + RowHeaderWidth - _HScrollBar.Value && (this.DateWidth - 2 <= t || t <= 1); + } + /// + /// 鼠标是否经过行标题 + /// + /// + /// + private bool IsHoverRowHeader(Point e) + { + return e.X <= this.RowHeaderWidth && e.Y > this.ColumnHeight; + } + /// + /// 鼠标是否经过行 + /// + /// + /// + private bool IsHoverRow(Point e) + { + var y = e.Y + _VScrollBar.Value; + return e.X < ((EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth + RowHeaderWidth - _HScrollBar.Value && e.Y > this.ColumnHeight && Rows != null ? Rows.Any(r=>r.Top <= y && r.Bottom >= y) : false; + } + /// + /// 鼠标是否经过项 + /// + /// + /// + private bool IsHoverItem(Point e) + { + if(e.X > this.RowHeaderWidth && e.Y > this.ColumnHeight && Rows != null) + { + var row = GetRow(e); + if (row != null && row.Items != null && row.Items.Any()) + { + var x = e.X + _HScrollBar.Value; + var y = e.Y + _VScrollBar.Value; + return row.Items.Any(r=>r.Left<=x && x <= r.Left + Math.Max(r.Width, r.TextWidth) && r.Top <= y && y <= r.Top + ItemHeight); + } + } + return false; + } + /// + /// 获取项 + /// + /// + /// + private GanttChartViewItem GetItem(Point e) + { + if (e.X > this.RowHeaderWidth && e.X < ((EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth + RowHeaderWidth - _HScrollBar.Value && e.Y > this.ColumnHeight && Rows != null) + { + var row = GetRow(e); + if (row != null && row.Items != null && row.Items.Any()) + { + var x = e.X + _HScrollBar.Value; + var y = e.Y + _VScrollBar.Value; + return row.Items.OrderBy(r=>r.Left).LastOrDefault(r => r.Left <= x && x <= r.Left + Math.Max(r.Width, r.TextWidth) && r.Top <= y && y <= r.Top + ItemHeight); + } + } + return null; + } + /// + /// 获取行 + /// + /// + /// + private GanttChartViewRow GetRow(Point e) + { + if (e.X < ((EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth + RowHeaderWidth - _HScrollBar.Value && e.Y > this.ColumnHeight && e.Y + _VScrollBar.Value < (Rows == null || !Rows.Any() ? 0 : Rows.Max(r=>r.Bottom)) && Rows != null) + { + var y = e.Y + _VScrollBar.Value; + return Rows.FirstOrDefault(r => r.Top <= y && r.Bottom >= y); + } + return null; + } + /// + /// 计算所有行和项的大小 + /// + private void ComputeRowsAndItemsSize() + { + var minuteWidth = DateWidth / 24f / 60f; + var currentRowY = ColumnHeight; + foreach (var row in Rows) + { + row.Top = currentRowY; + var currentItemY = currentRowY + RowSpacing; + row.Bottom = currentRowY; + if(row.Items != null) + { + var beforeTop = row.Top + RowSpacing; + var beforeRight = 0; + var items = row.Items.OrderBy(r => r.StartTime).ToList(); + for ( int i = 0; i < items.Count; i++) + { + var item = items[i]; + if (item.EndTime >= StartTime && item.StartTime <= EndTime && item.EndTime >= item.StartTime) + { + item.Left = (int)((item.StartTime - StartTime).TotalMinutes * minuteWidth) + RowHeaderWidth; + item.Width = (int)((item.EndTime - item.StartTime).TotalMinutes * minuteWidth); + item.TextWidth = TextRenderer.MeasureText(item.Title, ItemTextFont, System.Drawing.Size.Empty).Width; + if (item.Left < beforeRight || BlockType == GanttChartViewItemBlockType.Block && i > 0) + { + var its = items.Take(i).GroupBy(r=>r.Top).Select(r=>r.OrderByDescending(p => p.Left + p.TextWidth).First()); + if(BlockType == GanttChartViewItemBlockType.Return && its.Any(r=>r.Left + r.TextWidth < item.Left)) + { + var it = its.First(r => r.Left + r.TextWidth < item.Left); + item.Top = it.Top; + beforeTop = it.Top; + } + else + { + if(BlockType == GanttChartViewItemBlockType.Return && its.Any()) + { + beforeTop = its.Max(r => r.Top); + } + item.Top = beforeTop + ItemHeight + ItemSpacing; + beforeTop += ItemHeight + ItemSpacing; + } + } + else + { + if (i > 0) + { + if (BlockType == GanttChartViewItemBlockType.Return && beforeTop != row.Top + RowSpacing) + { + beforeTop = row.Top + RowSpacing; + } + } + item.Top = beforeTop; + } + if (BlockType == GanttChartViewItemBlockType.Compact) + { + beforeRight = item.Left + item.Width; + } + else + { + beforeRight = item.Left + Math.Max(item.TextWidth, item.Width); + } + if (item.Ranges != null && item.Ranges.Any()) + { + var subs = item.Ranges.OrderBy(r => r.StartTime); + foreach (var sub in subs) + { + sub.Left = item.Left + (int)((sub.StartTime - item.StartTime).TotalMinutes * minuteWidth); + sub.Width = (int)((sub.EndTime - sub.StartTime).TotalMinutes * minuteWidth); + } + } + } + else + { + //row.Items.Remove(item); + //i--; + } + } + } + if (row.Items == null || !row.Items.Any(r=>r.Left > 0)) + { + row.Bottom = row.Top + ItemHeight + RowSpacing * 2; + } + else + { + row.Bottom = Math.Max(row.Items.Max(r => r.Top), ColumnHeight + RowSpacing) + ItemHeight + RowSpacing; + } + currentRowY = row.Bottom; + } + } + /// + /// 计算提示框的位置 + /// + /// + /// + private Point ComputeTooltipLocation(Point e) + { + var size = TooltipOuterSize; + var x = e.X; + if (e.X + size.Width > _ContentControl.Width) + { + var t = e.X - size.Width; + if (t < 0 && 0 - t < (e.X + size.Width) - _ContentControl.Width || t >= 0) + { + x = t; + } + } + var y = _ShowTooltipItem.Top - _VScrollBar.Value + ItemHeight + ItemSpacing; + if(y + size.Height > _ContentControl.Height) + { + var t = _ShowTooltipItem.Top - _VScrollBar.Value - ItemSpacing - size.Height; + if (t < 0 && 0 - t < (y + size.Height) - _ContentControl.Height || t >= 0) + { + y = t; + } + } + return new Point(x, y); + } + } +} diff --git a/GanttChart/GanttChartView.Properties.cs b/GanttChart/GanttChartView.Properties.cs new file mode 100644 index 0000000..ebc8ac3 --- /dev/null +++ b/GanttChart/GanttChartView.Properties.cs @@ -0,0 +1,756 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace GanttChart +{ + /// + /// 甘特图控件 + /// 该部分类主要是甘特图的属性 + /// + public partial class GanttChartView + { + #region Fonts + private Font _ColumnDateTextFont = DefaultFont; + + /// + /// 日期字体 + /// + public Font ColumnDateTextFont + { + get { return _ColumnDateTextFont; } + set { _ColumnDateTextFont = value; OnPropertyChanged(nameof(ColumnDateTextFont), value); } + } + + private Font _ColumnHourTextFont = DefaultFont; + + /// + /// 时间字体 + /// + public Font ColumnHourTextFont + { + get { return _ColumnHourTextFont; } + set { _ColumnHourTextFont = value; OnPropertyChanged(nameof(ColumnHourTextFont), value); } + } + + private Font _RowHeaderTextFont = DefaultFont; + + /// + /// 行标题字体 + /// + public Font RowHeaderTextFont + { + get { return _RowHeaderTextFont; } + set { _RowHeaderTextFont = value; OnPropertyChanged(nameof(RowHeaderTextFont), value); } + } + + private Font _ItemTextFont = DefaultFont; + + /// + /// 项字体 + /// + public Font ItemTextFont + { + get { return _ItemTextFont; } + set { _ItemTextFont = value; OnPropertyChanged(nameof(ItemTextFont), value); } + } + + private Font _TooltipTextFont = DefaultFont; + + /// + /// 提示文字字体 + /// + public Font TooltipTextFont + { + get { return _TooltipTextFont; } + set { _TooltipTextFont = value; OnPropertyChanged(nameof(TooltipTextFont), value); } + } + #endregion + #region Colors + private Color _GridColor = Color.FromArgb(210, 210, 210); + + /// + /// 网格颜色 + /// + public Color GridColor + { + get { return _GridColor; } + set { _GridColor = value; OnPropertyChanged(nameof(GridColor), value); } + } + + private Color _ColumnDateTextColor = Color.FromArgb(102, 102, 102); + + /// + /// 日期文字颜色 + /// + public Color ColumnDateTextColor + { + get { return _ColumnDateTextColor; } + set { _ColumnDateTextColor = value; OnPropertyChanged(nameof(ColumnDateTextColor), value); } + } + + private Color _ColumnHourTextColor = Color.FromArgb(102, 102, 102); + + /// + /// 小时文字颜色 + /// + public Color ColumnHourTextColor + { + get { return _ColumnHourTextColor; } + set { _ColumnHourTextColor = value; OnPropertyChanged(nameof(ColumnHourTextColor), value); } + } + + private Color _HeaderBackColor = Color.FromArgb(247, 247, 247); + + /// + /// 标题背景颜色 + /// + public Color HeaderBackColor + { + get { return _HeaderBackColor; } + set { _HeaderBackColor = value; OnPropertyChanged(nameof(HeaderBackColor), value); } + } + + private Color _ContentBackColor = Color.White; + + /// + /// 内容背景颜色 + /// + public Color ContentBackColor + { + get { return _ContentBackColor; } + set { _ContentBackColor = value; OnPropertyChanged(nameof(ContentBackColor), value); } + } + + private Color _CurrentTimeLineColor = Color.Red; + + /// + /// 当前时间线颜色 + /// + public Color CurrentTimeLineColor + { + get { return _CurrentTimeLineColor; } + set { _CurrentTimeLineColor = value; OnPropertyChanged(nameof(CurrentTimeLineColor), value); } + } + + private Color _ItemTextColor = Color.FromArgb(51, 51, 51); + + /// + /// 项文字颜色 + /// + public Color ItemTextColor + { + get { return _ItemTextColor; } + set { _ItemTextColor = value; OnPropertyChanged(nameof(ItemTextColor), value); } + } + + private Color _ItemBackColor = Color.FromArgb(190, 190, 190); + + /// + /// 项背景颜色 + /// + public Color ItemBackColor + { + get { return _ItemBackColor; } + set { _ItemBackColor = value; OnPropertyChanged(nameof(ItemBackColor), value); } + } + + private Color _ItemBorderColor = Color.FromArgb(18, 170, 11); + + /// + /// 项边框颜色 + /// + public Color ItemBorderColor + { + get { return _ItemBorderColor; } + set { _ItemBorderColor = value; OnPropertyChanged(nameof(ItemBorderColor), value); } + } + + private Color _ItemStartFlagColor = Color.Red; + + /// + /// 项首标志颜色 + /// + public Color ItemStartFlagColor + { + get { return _ItemStartFlagColor; } + set { _ItemStartFlagColor = value; OnPropertyChanged(nameof(ItemStartFlagColor), value); } + } + + private Color _ItemSubRangeBackColor = Color.FromArgb(153, 201, 255); + + /// + /// 项时间范围背景颜色 + /// + public Color ItemSubRangeBackColor + { + get { return _ItemSubRangeBackColor; } + set { _ItemSubRangeBackColor = value; OnPropertyChanged(nameof(ItemSubRangeBackColor), value); } + } + + private Color _ItemSelectionTextColor = Color.FromArgb(18, 170, 11); + + /// + /// 选中项文字颜色 + /// + public Color ItemSelectionTextColor + { + get { return _ItemSelectionTextColor; } + set { _ItemSelectionTextColor = value; OnPropertyChanged(nameof(ItemSelectionTextColor), value); } + } + + private Color _ItemSelectionBackColor = Color.FromArgb(229, 249, 229); + + /// + /// 选中项背景颜色 + /// + public Color ItemSelectionBackColor + { + get { return _ItemSelectionBackColor; } + set { _ItemSelectionBackColor = value; OnPropertyChanged(nameof(ItemSelectionBackColor), value); } + } + + private Color _ItemSelectionBorderColor = Color.FromArgb(18, 170, 11); + + /// + /// 选中项边框颜色 + /// + public Color ItemSelectionBorderColor + { + get { return _ItemSelectionBorderColor; } + set { _ItemSelectionBorderColor = value; OnPropertyChanged(nameof(ItemSelectionBorderColor), value); } + } + + private Color _ItemSelectionSubRangeBackColor = Color.FromArgb(153, 201, 255); + + /// + /// 选中项时间范围背景颜色 + /// + public Color ItemSelectionSubRangeBackColor + { + get { return _ItemSelectionSubRangeBackColor; } + set { _ItemSelectionSubRangeBackColor = value; OnPropertyChanged(nameof(ItemSelectionSubRangeBackColor), value); } + } + + private Color _RowHeaderTextColor = Color.FromArgb(102, 102, 102); + + /// + /// 行标题文字颜色 + /// + public Color RowHeaderTextColor + { + get { return _RowHeaderTextColor; } + set { _RowHeaderTextColor = value; OnPropertyChanged(nameof(RowHeaderTextColor), value); } + } + + private Color _RowSelectionHeaderTextColor = Color.FromArgb(102, 102, 102); + + /// + /// 选中行标题文字颜色 + /// + public Color RowSelectionHeaderTextColor + { + get { return _RowSelectionHeaderTextColor; } + set { _RowSelectionHeaderTextColor = value; OnPropertyChanged(nameof(RowSelectionHeaderTextColor), value); } + } + + private Color _RowSelectionBorderColor = Color.FromArgb(247, 247, 247); + + /// + /// 选中行边框颜色 + /// + public Color RowSelectionBorderColor + { + get { return _RowSelectionBorderColor; } + set { _RowSelectionBorderColor = value; OnPropertyChanged(nameof(RowSelectionBorderColor), value); } + } + + private Color _RowSelectionHeaderBackColor = Color.FromArgb(247, 247, 247); + + /// + /// 选中行标题背景颜色 + /// + public Color RowSelectionHeaderBackColor + { + get { return _RowSelectionHeaderBackColor; } + set { _RowSelectionHeaderBackColor = value; OnPropertyChanged(nameof(RowSelectionHeaderBackColor), value); } + } + + private Color _RowSelectionContentBackColor = Color.FromArgb(247, 247, 247); + + /// + /// 选中行内容背景颜色 + /// + public Color RowSelectionContentBackColor + { + get { return _RowSelectionContentBackColor; } + set { _RowSelectionContentBackColor = value; OnPropertyChanged(nameof(RowSelectionContentBackColor), value); } + } + + private Color _TooltipTextColor = Color.FromArgb(237, 237, 237); + + /// + /// 提示文字颜色 + /// + public Color TooltipTextColor + { + get { return _TooltipTextColor; } + set { _TooltipTextColor = value; OnPropertyChanged(nameof(TooltipTextColor), value); } + } + + private Color _TooltipBorderColor = Color.FromArgb(102, 0, 0, 0); + + /// + /// 提示边框颜色 + /// + public Color TooltipBorderColor + { + get { return _TooltipBorderColor; } + set { _TooltipBorderColor = value; OnPropertyChanged(nameof(TooltipBorderColor), value); } + } + + private Color _TooltipBackColor = Color.FromArgb(102, 0, 0, 0); + + /// + /// 提示背景颜色 + /// + public Color TooltipBackColor + { + get { return _TooltipBackColor; } + set { _TooltipBackColor = value; OnPropertyChanged(nameof(TooltipBackColor), value); } + } + + private Color _TimeRangeSelecetionBackColor = Color.FromArgb(153, 201, 255); + + /// + /// 选中时间范围背景颜色 + /// + public Color TimeRangeSelecetionBackColor + { + get { return _TimeRangeSelecetionBackColor; } + set { _TimeRangeSelecetionBackColor = value; OnPropertyChanged(nameof(TimeRangeSelecetionBackColor), value); } + } + + private Color _SelectionRectangleBackColor = Color.FromArgb(80, 210, 210, 210); + + /// + /// 选择框背景颜色 + /// + public Color SelectionRectangleBackColor + { + get { return _SelectionRectangleBackColor; } + set { _SelectionRectangleBackColor = value; OnPropertyChanged(nameof(SelectionRectangleBackColor), value); } + } + + private Color _ItemWarnningTextColor = Color.Red; + + /// + /// 项警告文字颜色 + /// + public Color ItemWarnningTextColor + { + get { return _ItemWarnningTextColor; } + set { _ItemWarnningTextColor = value; OnPropertyChanged(nameof(ItemWarnningTextColor), value); } + } + #endregion + #region Mouse + private float _MouseWheelSensitivity = 0.5f; + + /// + /// 鼠标滚轮灵敏度 + /// + [DefaultValue(0.5f)] + public float MouseWheelSensitivity + { + get { return _MouseWheelSensitivity; } + set { _MouseWheelSensitivity = value; OnPropertyChanged(nameof(MouseWheelSensitivity), value); } + } + #endregion + #region Size + private int _ItemHeight = 30; + + /// + /// 项高度 + /// + [DefaultValue(30)] + public int ItemHeight + { + get { return _ItemHeight; } + set { _ItemHeight = value; OnPropertyChanged(nameof(ItemHeight), value); } + } + + private int _RowHeaderWidth = 150; + + /// + /// 行标题宽度 + /// + [DefaultValue(150)] + public int RowHeaderWidth + { + get { return _RowHeaderWidth; } + set { _RowHeaderWidth = value; OnPropertyChanged(nameof(RowHeaderWidth), value); } + } + + private int _RowHeight = 100; + + /// + /// 行高 + /// + [DefaultValue(100)] + public int RowHeight + { + get { return _RowHeight; } + set { _RowHeight = value; OnPropertyChanged(nameof(RowHeight), value); } + } + + private int _ColumnDateHeight = 30; + + /// + /// 日期高度 + /// + [DefaultValue(30)] + public int ColumnDateHeight + { + get { return _ColumnDateHeight; } + set { _ColumnDateHeight = value; OnPropertyChanged(nameof(ColumnDateHeight), value); } + } + + private int _ColumnHourHeight = 30; + + /// + /// 小时高度 + /// + [DefaultValue(30)] + public int ColumnHourHeight + { + get { return _ColumnHourHeight; } + set { _ColumnHourHeight = value; OnPropertyChanged(nameof(ColumnHourHeight), value); } + } + + private int _DateWidth = MinDateWidth; + + /// + /// 日期宽度 + /// + [DefaultValue(MinDateWidth)] + public int DateWidth + { + get { return _DateWidth; } + set { _DateWidth = value; OnPropertyChanged(nameof(DateWidth), value); } + } + + private int _HScrollHeight = 15; + + /// + /// 横向滚动条高度 + /// + [DefaultValue(15)] + public int HScrollHeight + { + get { return _HScrollHeight; } + set { _HScrollHeight = value; OnPropertyChanged(nameof(HScrollHeight), value); } + } + + private int _VScrollWidth = 15; + + /// + /// 纵向滚动条宽度 + /// + [DefaultValue(15)] + public int VScrollWidth + { + get { return _VScrollWidth; } + set { _VScrollWidth = value; OnPropertyChanged(nameof(VScrollWidth), value); } + } + + private int _ItemSpacing = 2; + + /// + /// 项的行间距 + /// + [DefaultValue(2)] + public int ItemSpacing + { + get { return _ItemSpacing; } + set { _ItemSpacing = value; OnPropertyChanged(nameof(ItemSpacing), value); } + } + + private int _RowSpacing = 5; + + /// + /// 行间距 + /// + [DefaultValue(5)] + public int RowSpacing + { + get { return _RowSpacing; } + set { _RowSpacing = value; OnPropertyChanged(nameof(RowSpacing), value); } + } + #endregion + #region Control + private bool _IsFixedRowHeight = false; + + /// + /// 是否固定行高 + /// + [DefaultValue(false)] + public bool IsFixedRowHeight + { + get { return _IsFixedRowHeight; } + set { _IsFixedRowHeight = value; OnPropertyChanged(nameof(IsFixedRowHeight), value); } + } + + private bool _EnableEdit = false; + + /// + /// 启用编辑 + /// + [DefaultValue(false)] + public bool EnableEdit + { + get { return _EnableEdit; } + set { _EnableEdit = value; OnPropertyChanged(nameof(EnableEdit), value); } + } + + private bool _AllowItemNotWrap = true; + + /// + /// 允许项文字换行 + /// + [DefaultValue(true)] + public bool AllowItemNotWrap + { + get { return _AllowItemNotWrap; } + set { _AllowItemNotWrap = value; OnPropertyChanged(nameof(AllowItemNotWrap), value); } + } + + private bool _IsShowItemBorder = false; + + /// + /// 是否显示项边框 + /// + [DefaultValue(false)] + public bool IsShowItemBorder + { + get { return _IsShowItemBorder; } + set { _IsShowItemBorder = value; OnPropertyChanged(nameof(IsShowItemBorder), value); } + } + + private bool _AllowHeaderNotWrap = true; + + /// + /// 允许行标题文字换行 + /// + [DefaultValue(true)] + public bool AllowHeaderNotWrap + { + get { return _AllowHeaderNotWrap; } + set { _AllowHeaderNotWrap = value; OnPropertyChanged(nameof(AllowHeaderNotWrap), value); } + } + + private GanttChartViewItemBlockType _BlockType = GanttChartViewItemBlockType.TextOverBlock; + + /// + /// 项换行模式 + /// + public GanttChartViewItemBlockType BlockType + { + get { return _BlockType; } + set { _BlockType = value; OnPropertyChanged(nameof(BlockType), value); } + } + + private bool _IsShowSelectionBorder = false; + + /// + /// 显示行选中高亮边框 + /// + [DefaultValue(false)] + public bool IsShowSelectionBorder + { + get { return _IsShowSelectionBorder; } + set { _IsShowSelectionBorder = value; OnPropertyChanged(nameof(IsShowSelectionBorder), value); } + } + + private bool _AllowSelectRange = false; + + /// + /// 允许选中时间范围 + /// + [DefaultValue(false)] + public bool AllowSelectRange + { + get { return _AllowSelectRange; } + set { _AllowSelectRange = value; OnPropertyChanged(nameof(AllowSelectRange), value); } + } + + private Func _SelectionItemSameComparator = null; + + /// + /// 选择项的同类项比较器 + /// + public Func SelectionItemSameComparator + { + get { return _SelectionItemSameComparator; } + set { _SelectionItemSameComparator = value; } + } + #endregion + #region DataSource + private DateTime _StartDate = DateTime.Today; + + /// + /// 开始日期 + /// + public DateTime StartDate + { + get { return _StartDate; } + set { _StartDate = value; OnPropertyChanged(nameof(StartDate), value); } + } + + private DateTime _EndDate = DateTime.Today.AddDays(1); + + /// + /// 结束日期 + /// + public DateTime EndDate + { + get { return _EndDate; } + set { _EndDate = value; OnPropertyChanged(nameof(EndDate), value); } + } + + private GanttChartCollection _Rows = new GanttChartCollection(); + + /// + /// 行集合 + /// + public GanttChartCollection Rows + { + get { return _Rows; } + set { _Rows = value; OnPropertyChanged(nameof(Rows), value); } + } + + private GanttChartViewRow _SelectionRow = null; + + /// + /// 选中的行 + /// + public GanttChartViewRow SelectionRow + { + get { return _SelectionRow; } + set { _SelectionRow = value; OnPropertyChanged(nameof(SelectionRow), value); } + } + + private GanttChartViewItem _SelectionItem = null; + + /// + /// 选中的项 + /// + public GanttChartViewItem SelectionItem + { + get { return _SelectionItem; } + set { _SelectionItem = value; OnPropertyChanged(nameof(SelectionItem), value); } + } + + private TimeRange _SelectionTimeRange = null; + + /// + /// 选中的时间范围 + /// + public TimeRange SelectionTimeRange + { + get { return _SelectionTimeRange; } + set { _SelectionTimeRange = value; OnPropertyChanged(nameof(SelectionTimeRange), value); } + } + + private DateTime _DateSplitTime = DateTime.MinValue; + + /// + /// 日分割时间 + /// + public DateTime DateSplitTime + { + get { return _DateSplitTime; } + set { _DateSplitTime = value; OnPropertyChanged(nameof(DateSplitTime), value); } + } + + private string _DateTimeShowText = "时间"; + + /// + /// 时间显示文本 + /// + [DefaultValue("时间")] + public string DateTimeShowText + { + get { return _DateTimeShowText; } + set { _DateTimeShowText = value; OnPropertyChanged(nameof(DateTimeShowText), value); } + } + + private string _RowTitleShowText = "资源"; + + /// + /// 行头显示文本 + /// + [DefaultValue("资源")] + public string RowTitleShowText + { + get { return _RowTitleShowText; } + set { _RowTitleShowText = value; OnPropertyChanged(nameof(RowTitleShowText), value); } + } + #endregion + + #region ReadOnly + public int ColumnHeight + { + get { return ColumnDateHeight + ColumnHourHeight; } + } + + public int TotalDays + { + get + { + return (int)(EndDate.Date - StartDate.Date).TotalDays + 1; + } + } + public DateTime StartTime + { + get + { + return StartDate.Date.Add(DateSplitTime - DateSplitTime.Date); + } + } + public DateTime EndTime + { + get + { + return EndDate.Date.Add(DateSplitTime - DateSplitTime.Date); + } + } + private Size TooltipSize + { + get + { + var size = TextRenderer.MeasureText(_ShowTooltipItem.Tooltip, TooltipTextFont, Size.Empty); + size.Height = size.Height + (int)((_ShowTooltipItem.Tooltip.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).Length - 1) * 1.7); + return size; + } + } + private Size TooltipOuterSize + { + get + { + var size = TooltipSize; + size.Width += RowSpacing * 2; + size.Height += RowSpacing * 2; + return size; + } + } + #endregion + + #region Const + private const int MinDateWidth = 3 * 48; + #endregion + } +} diff --git a/GanttChart/GanttChartView.Render.cs b/GanttChart/GanttChartView.Render.cs new file mode 100644 index 0000000..82aad03 --- /dev/null +++ b/GanttChart/GanttChartView.Render.cs @@ -0,0 +1,409 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace GanttChart +{ + /// + /// 甘特图控件 + /// 该部分类用于处理甘特图的绘制渲染 + /// + public partial class GanttChartView + { + /// + /// 画背景 + /// + /// + /// + protected virtual void DrawBack(Graphics graphics, int bottom) + { + graphics.FillRectangle(this.Brushes.BackBrush, _ContentControl.DisplayRectangle); + var range = new RectangleF(new PointF(0, 0), new SizeF(TotalDays * DateWidth + RowHeaderWidth - _HScrollBar.Value, bottom - _VScrollBar.Value)); + graphics.SetClip(range); + graphics.FillRectangle(this.Brushes.HeaderBackBrush, range); + range = new RectangleF(new PointF(0, ColumnHeight), new SizeF(TotalDays * DateWidth + RowHeaderWidth - _HScrollBar.Value, bottom - ColumnHeight - _VScrollBar.Value)); + graphics.FillRectangle(this.Brushes.ContentBackBrush, range); + if (_SelectionRow != null) + { + var selectionHeader = new Rectangle(1, _SelectionRow.Top + 1 - _VScrollBar.Value, RowHeaderWidth - 2, (int)(_SelectionRow.Bottom - _SelectionRow.Top - 1)); + var selectionContent = new Rectangle(RowHeaderWidth, _SelectionRow.Top + 1 - _VScrollBar.Value, (int)(TotalDays * DateWidth - _HScrollBar.Value - 1), selectionHeader.Height); + graphics.FillRectangle(this.Brushes.RowSelectionHeaderBackBrush, selectionHeader); + graphics.FillRectangle(this.Brushes.RowSelectionContentBackBrush, selectionContent); + } + graphics.ResetClip(); + } + /// + /// 画没有数据 + /// + /// + /// + protected virtual void DrawNonData(Graphics graphics, int bottom) + { + + } + /// + /// 画提示框 + /// + /// + protected virtual void DrawItemTooltip(Graphics graphics) + { + if (!_IsShowTooltip) return; + var size = TooltipSize; + var border = new Rectangle(_ShowTooltipPoint, new Size(size.Width + RowSpacing * 2, size.Height + RowSpacing * 2)); + var content = new Point(_ShowTooltipPoint.X + RowSpacing, _ShowTooltipPoint.Y + RowSpacing); + graphics.FillRectangle(this.Brushes.TooltipBackBrush, border); + graphics.DrawRectangle(this.Brushes.TooltipBorderPen, border); + graphics.DrawString(_ShowTooltipItem.Tooltip, TooltipTextFont, this.Brushes.TooltipTextBrush, content); + //TextRenderer.DrawText(graphics, _ShowTooltipItem.Tooltip, TooltipTextFont, content, TooltipTextColor); + } + /// + /// 画选中范围 + /// + /// + protected virtual void DrawSelectionRange(Graphics graphics) + { + if (!AllowSelectRange || _SelectionRange.IsEmpty) return; + graphics.FillRectangle(this.Brushes.TimeRangeSelecetionBackBrush, _SelectionRange); + } + /// + /// 画选中的矩形 + /// + /// + protected virtual void DrawSelectingRectangle(Graphics graphics) + { + if (!AllowSelectRange || _DragStartLocation.IsEmpty || _DragEndLocation.IsEmpty || _DragStartLocation == _DragEndLocation || _DragStartLocation.X <= RowHeaderWidth || _DragStartLocation.Y <= ColumnHeight) return; + var row = GetRow(_DragStartLocation); + if (row == null) return; + var start = new Point(Math.Min(_DragStartLocation.X, Math.Max(_DragEndLocation.X, RowHeaderWidth)), Math.Min(_DragStartLocation.Y, Math.Max(_DragEndLocation.Y, row.Top))); + var height = Math.Abs(Math.Min(_DragEndLocation.Y, row.Bottom - _VScrollBar.Value) - _DragStartLocation.Y); + var width = Math.Abs(_DragEndLocation.X - _DragStartLocation.X); + if (_DragEndLocation.Y < row.Top) + { + height -= row.Top - _DragEndLocation.Y; + } + if (_DragEndLocation.X < RowHeaderWidth) + { + width -= RowHeaderWidth - _DragEndLocation.X; + } + else if(_DragEndLocation.X > TotalDays * DateWidth + RowHeaderWidth - _HScrollBar.Value) + { + width = TotalDays * DateWidth + RowHeaderWidth - _HScrollBar.Value - _DragStartLocation.X; + } + var rect = new RectangleF(start, new SizeF(width, height)); + graphics.FillRectangle(this.Brushes.SelectionRectangleBackBrush, rect); + } + /// + /// 画项的时间范围 + /// + /// + /// + /// + protected virtual void DrawItemTimeRange(Graphics graphics, GanttChartViewItem item, TimeRange sub) + { + var width = sub.Width; + var right = sub.Left + sub.Width; + if (right == item.Left + item.Width) + { + width = width - 2; + if(width < 1) + { + return; + } + } + if (item == _SelectionItem) + { + graphics.FillRectangle(this.Brushes.ItemSelectionSubRangeBackBrush, sub.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, width, ItemHeight); + graphics.DrawRectangle(this.Brushes.ItemSelectionSubRangeBackPen, sub.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, width, ItemHeight); + } + else + { + graphics.FillRectangle(this.Brushes.ItemSubRangeBackBrush, sub.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, width, ItemHeight); + graphics.DrawRectangle(this.Brushes.ItemSubRangeBackPen, sub.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, width, ItemHeight); + } + } + /// + /// 画项 + /// + /// + /// + /// + /// + protected virtual void DrawItem(Graphics graphics, StringFormat titleFormat, GanttChartViewItem item, RectangleF range) + { + var clip = new RectangleF(range.X, (range.Y < ColumnHeight ? ColumnHeight : range.Y) + 1, range.Width, (range.Y < ColumnHeight ? (range.Height - (ColumnHeight - range.Y)) : range.Height) - 2); + graphics.SetClip(clip); + if (item.Ranges != null && item.Ranges.Any()) + { + graphics.FillRectangle(this.Brushes.ItemBackBrush, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + graphics.DrawRectangle(this.Brushes.ItemBackPen, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + var subs = item.Ranges.OrderBy(r => r.StartTime); + foreach (var sub in subs) + { + DrawItemTimeRange(graphics, item, sub); + } + } + else + { + graphics.FillRectangle(this.Brushes.ItemSubRangeBackBrush, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + graphics.DrawRectangle(this.Brushes.ItemSubRangeBackPen, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + } + if (item == _SelectionItem) + { + var textBrush = item.IsWarnning ? this.Brushes.ItemWarnningTextBrush : this.Brushes.ItemSelectionTextBrush; + graphics.DrawLine(this.Brushes.ItemStartFlagPen, item.Left - _HScrollBar.Value + 1, item.Top - _VScrollBar.Value + ItemHeight / 2, item.Left - _HScrollBar.Value + 1, item.Top - _VScrollBar.Value + ItemHeight); + graphics.DrawRectangle(this.Brushes.ItemSelectionBorderPen, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + graphics.DrawString(item.Title, ItemTextFont, textBrush, new RectangleF(item.Left - _HScrollBar.Value + 2, item.Top - _VScrollBar.Value + 1, 0, ItemHeight - 2), titleFormat); + } + else if(_SelectionItem != null && SelectionItemSameComparator != null && SelectionItemSameComparator(item, _SelectionItem)) + { + var textBrush = item.IsWarnning ? this.Brushes.ItemWarnningTextBrush : this.Brushes.ItemSelectionTextBrush; + graphics.DrawLine(this.Brushes.ItemStartFlagPen, item.Left - _HScrollBar.Value + 1, item.Top - _VScrollBar.Value + ItemHeight / 2, item.Left - _HScrollBar.Value + 1, item.Top - _VScrollBar.Value + ItemHeight); + if (IsShowItemBorder) + graphics.DrawRectangle(this.Brushes.ItemBorderPen, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + graphics.DrawString(item.Title, ItemTextFont, textBrush, new RectangleF(item.Left - _HScrollBar.Value + 2, item.Top - _VScrollBar.Value + 1, 0, ItemHeight - 2), titleFormat); + } + else + { + var textBrush = item.IsWarnning ? this.Brushes.ItemWarnningTextBrush : this.Brushes.ItemTextBrush; + graphics.DrawLine(this.Brushes.ItemStartFlagPen, item.Left - _HScrollBar.Value + 1, item.Top - _VScrollBar.Value + ItemHeight / 2, item.Left - _HScrollBar.Value + 1, item.Top - _VScrollBar.Value + ItemHeight); + if (IsShowItemBorder) + graphics.DrawRectangle(this.Brushes.ItemBorderPen, item.Left - _HScrollBar.Value, item.Top - _VScrollBar.Value, Math.Max(item.Width - 2, 2), ItemHeight); + graphics.DrawString(item.Title, ItemTextFont, textBrush, new RectangleF(item.Left - _HScrollBar.Value + 2, item.Top - _VScrollBar.Value + 1, 0, ItemHeight - 2), titleFormat); + } + graphics.ResetClip(); + } + /// + /// 画行 + /// + /// + /// + /// + /// + /// + /// + protected virtual void DrawRow(Graphics graphics, StringFormat headerFormat, StringFormat titleFormat, GanttChartViewRow row, RectangleF range, RectangleF rowRect) + { + graphics.SetClip(range); + var drawY = rowRect.Y + RowSpacing; + var drawHeight = rowRect.Height - RowSpacing * 2; + if (drawHeight > (_ContentControl.Height - ColumnHeight) / 4) + { + drawY = Math.Max(rowRect.Y, ColumnHeight); + drawHeight = Math.Min(rowRect.Bottom - RowSpacing, _ContentControl.Height) - drawY; + } + graphics.DrawString(row.Title, RowHeaderTextFont, this.Brushes.RowHeaderTextBrush, new RectangleF(rowRect.X + RowSpacing, drawY, RowHeaderWidth - RowSpacing * 2, drawHeight), headerFormat); + graphics.ResetClip(); + if(row.Items != null) + { + var items = row.Items.OrderBy(r=>r.StartTime); + foreach (var item in items) + { + if (item.Left + item.Width - _HScrollBar.Value > RowHeaderWidth) + { + if (item.Left - _HScrollBar.Value < range.Right) + { + DrawItem(graphics, titleFormat, item, new RectangleF(RowHeaderWidth + 1, rowRect.Y + 1, rowRect.Width - RowHeaderWidth - 2, rowRect.Height - 2)); + } + } + } + } + graphics.SetClip(range); + graphics.DrawLine(this.Brushes.GridPen, 0, row.Bottom - _VScrollBar.Value, rowRect.Width, row.Bottom - _VScrollBar.Value); + if (_SelectionRow == row && IsShowSelectionBorder) + { + var selectionRect = new Rectangle(1, row.Top + 1, (int)(range.Width - 3), (int)(row.Bottom - row.Top - 2)); + graphics.DrawRectangle(this.Brushes.RowSelectionBorderPen, selectionRect); + } + graphics.ResetClip(); + } + /// + /// 画所有行 + /// + /// + /// + protected virtual void DrawRows(Graphics graphics, int bottom) + { + var range = new RectangleF(0, ColumnHeight, RowHeaderWidth + TotalDays * DateWidth - _HScrollBar.Value, bottom - _VScrollBar.Value + 1); + graphics.DrawLine(this.Brushes.GridPen, RowHeaderWidth, ColumnDateHeight, range.Width - 1, ColumnDateHeight); + graphics.SetClip(range); + graphics.DrawLine(this.Brushes.GridPen, 0, ColumnHeight, range.Width - 1, ColumnHeight); + if (Rows == null || !Rows.Any()) + { + DrawNonData(graphics, bottom); + graphics.DrawLine(this.Brushes.GridPen, 0, bottom, range.Width - 1, bottom); + return; + } + graphics.ResetClip(); + var headerFormat = new StringFormat(); + headerFormat.Alignment = StringAlignment.Near; + headerFormat.LineAlignment = StringAlignment.Center; + var titleFormat = new StringFormat(); + titleFormat.Alignment = StringAlignment.Near; + titleFormat.LineAlignment = StringAlignment.Center; + + titleFormat.FormatFlags = BlockType == GanttChartViewItemBlockType.Compact ? StringFormatFlags.LineLimit : StringFormatFlags.NoWrap; + foreach (var row in Rows) + { + if (row.Bottom - _VScrollBar.Value > ColumnHeight) + { + var rowRect = new RectangleF(new PointF(0, row.Top - _VScrollBar.Value), new SizeF(range.Width, row.Bottom - row.Top + 1)); + if (!range.IntersectsWith(rowRect)) break; + DrawRow(graphics, headerFormat, titleFormat, row, range, rowRect); + } + } + } + /// + /// 画列 + /// + /// + /// + /// + /// + /// + /// + /// + protected virtual void DrawColumn(Graphics graphics, StringFormat format, DateTime date, RectangleF range, int startX, int width, int cellHour) + { + var offsetMinutes = (int)(DateSplitTime - DateSplitTime.Date).TotalMinutes % (60 * cellHour); + var offset = (int)(offsetMinutes / 60f * width); + startX = startX - offset; + var dt = date.AddMinutes(-offsetMinutes); + for (DateTime i = dt; i < dt.AddDays(1); i = i.AddHours(cellHour)) + { + var rectangle = new RectangleF(startX, ColumnDateHeight + 1, 0, ColumnHourHeight - 2); + if (range.IntersectsWith(rectangle) && (offset != 0 || date.Hour != i.Hour)) + { + graphics.DrawString(i.Hour == 0 ? "0" : i.Hour.ToString("##00"), ColumnDateTextFont, this.Brushes.ColumnHourTextBrush, rectangle, format); + //graphics.DrawLine(this.Brushes.GridPen, rectangle.X, ColumnDateHeight, rectangle.X, ColumnDateHeight + ColumnHourHeight / 5); + graphics.DrawLine(this.Brushes.GridPen, rectangle.X, ColumnDateHeight + ColumnHourHeight / 5 * 4, rectangle.X, ColumnDateHeight + ColumnHourHeight - 1); + } + startX += width * cellHour; + } + } + /// + /// 画列头 + /// + /// + protected virtual void DrawColumnHeader(Graphics graphics) + { + var range = new RectangleF(new PointF(RowHeaderWidth + 1, 1), new SizeF(TotalDays * DateWidth - 1, _ContentControl.Height - 2)); + graphics.SetClip(range); + int cellHour = GetCellHours(); + var format = new StringFormat(); + format.Alignment = StringAlignment.Center; + format.LineAlignment = StringAlignment.Center; + format.FormatFlags = StringFormatFlags.LineLimit; + var x = RowHeaderWidth; + for (var i = StartTime; i <= EndTime; i = i.AddDays(1)) + { + var dx = x - _HScrollBar.Value; + var rectangle = new RectangleF(dx + 1, 1, DateWidth - 1, ColumnDateHeight - 2); + if (rectangle.Right > RowHeaderWidth) + { + if (!range.IntersectsWith(rectangle)) + { + x += DateWidth; + continue; + } + graphics.DrawString(i.ToString("yyyy-MM-dd") + " 星期" + ("日一二三四五六"[(int)i.DayOfWeek]), ColumnDateTextFont, this.Brushes.ColumnDateTextBrush, rectangle, format); + DrawColumn(graphics, format, i, range, (int)rectangle.X - 1, DateWidth / 24, cellHour); + } + x += DateWidth; + } + graphics.ResetClip(); + } + /// + /// 画行头 + /// + /// + /// + protected virtual void DrawHeader(Graphics graphics, int bottom) + { + var range = new RectangleF(new PointF(0, 0), new SizeF(RowHeaderWidth + TotalDays * DateWidth, bottom - _VScrollBar.Value)); + graphics.SetClip(range); + graphics.DrawLine(this.Brushes.GridPen, range.X, range.Y, range.X + RowHeaderWidth, range.Y + ColumnHeight); + graphics.DrawString(this.DateTimeShowText, this.ColumnDateTextFont, this.Brushes.ColumnDateTextBrush, + new Rectangle(0, 0, RowHeaderWidth - RowSpacing, ColumnDateHeight), + new StringFormat { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Center }); + graphics.DrawString(this.RowTitleShowText, this.RowHeaderTextFont, this.Brushes.RowHeaderTextBrush, + new Rectangle(RowSpacing, ColumnDateHeight, RowHeaderWidth - RowSpacing, ColumnHourHeight - RowSpacing), + new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Far }); + graphics.ResetClip(); + } + /// + /// 画时间线 + /// + /// + /// + protected virtual void DrawTimeLine(Graphics graphics, int bottom) + { + var range = new RectangleF(new PointF(0, 0), new SizeF(RowHeaderWidth + TotalDays * DateWidth, bottom - _VScrollBar.Value)); + graphics.SetClip(range); + graphics.DrawRectangle(this.Brushes.GridPen, _ContentControl.DisplayRectangle); + var x = RowHeaderWidth - 1; + if(StartTime == DateTime.Today.Add(DateSplitTime - DateSplitTime.Date)) + { + graphics.DrawLine(this.Brushes.CurrentTimeLinePen, x, 0, x, _ContentControl.Height); + } + else + { + graphics.DrawLine(this.Brushes.GridPen, x, 0, x, _ContentControl.Height); + } + for (var i = StartTime; i <= EndTime; i = i.AddDays(1)) + { + x += DateWidth; + var dx = x - _HScrollBar.Value; + if (dx + 1 > RowHeaderWidth) + { + if (dx + 1 > _ContentControl.Width) break; + if(i.AddDays(1) == DateTime.Today.Add(DateSplitTime - DateSplitTime.Date)) + { + graphics.DrawLine(this.Brushes.CurrentTimeLinePen, dx, 0, dx, _ContentControl.Height); + } + else + { + graphics.DrawLine(this.Brushes.GridPen, dx, 0, dx, _ContentControl.Height); + } + } + } + graphics.ResetClip(); + } + /// + /// 计算单元格小时 + /// + /// + private int GetCellHours() + { + var halfHourWidth = DateWidth / 48; + int cellHour; + if (halfHourWidth >= 12) + { + cellHour = 1; + } + else if (halfHourWidth >= 6) + { + cellHour = 2; + } + else if (halfHourWidth >= 4) + { + cellHour = 3; + } + else if (halfHourWidth >= 3) + { + cellHour = 4; + } + else if (halfHourWidth >= 2) + { + cellHour = 6; + } + else + { + cellHour = 12; + } + return cellHour; + } + } +} diff --git a/GanttChart/GanttChartView.cs b/GanttChart/GanttChartView.cs new file mode 100644 index 0000000..96b5231 --- /dev/null +++ b/GanttChart/GanttChartView.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace GanttChart +{ + /// + /// 甘特图控件 + /// + [ToolboxBitmap(typeof(GanttChartView), "GanttChart.GanttChartView.bmp")] + public partial class GanttChartView: Control + { + #region Private Members + private HScrollBar _HScrollBar; + private VScrollBar _VScrollBar; + + private TableLayoutPanel _Table; + private Control _ContentControl; + + private GanttChartBrushes Brushes; + #endregion + + #region ctor + public GanttChartView() + { + _Table = new TableLayoutPanel(); + _Table.ColumnCount = 2; + _Table.RowCount = 2; + _Table.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + _Table.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, VScrollWidth)); + _Table.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + _Table.RowStyles.Add(new RowStyle(SizeType.Absolute, HScrollHeight)); + + _ContentControl = new GanttChartCanvas(); + _ContentControl.Dock = DockStyle.Fill; + _ContentControl.Margin = new Padding(0); + _Table.Controls.Add(_ContentControl, 0, 0); + + _HScrollBar = new HScrollBar(); + _HScrollBar.Value = 0; + _HScrollBar.Height = HScrollHeight; + _HScrollBar.SmallChange = 1; + _HScrollBar.LargeChange = 2; + //_HScrollBar.Dock = DockStyle.Fill; + _HScrollBar.Visible = true; + _Table.Controls.Add(_HScrollBar, 0, 1); + + + _VScrollBar = new VScrollBar(); + _VScrollBar.Value = 0; + _VScrollBar.Width = VScrollWidth; + _VScrollBar.SmallChange = 1; + _VScrollBar.LargeChange = 2; + //_VScrollBar.Dock = DockStyle.Fill; + _VScrollBar.Visible = true; + _Table.Controls.Add(_VScrollBar, 1, 0); + + _Table.Dock = DockStyle.Fill; + Controls.Add(_Table); + + InitEvent(); + } + + #endregion + /// + /// 调整滚动条 + /// + private void AdjustScrollbar() + { + var bottom = ItemHeight + RowSpacing * 2; + if (Rows != null && Rows.Any()) + { + if (StartDate != Rows.StartDate || Rows.IsChanged) + { + ComputeRowsAndItemsSize(); + Rows.IsChanged = false; + Rows.StartDate = StartDate; + } + var last = Rows.Last(); + bottom = last.Bottom; + } + if (IsFixedRowHeight) + { + var h = ColumnHeight + (Rows?.Count ?? 0) * RowHeight + 200; + if(h > Height - HScrollHeight) + { + _VScrollBar.Maximum = h - Height + HScrollHeight + 1; + } + else + { + _VScrollBar.Maximum = 0; + } + } + else + { + _VScrollBar.Maximum = bottom - Height + HScrollHeight + 1 + 200; + } + _VScrollBar.Minimum = 0; + + _HScrollBar.Maximum = ((int)(EndDate.Date - StartDate.Date).TotalDays + 1) * DateWidth - Width + RowHeaderWidth + VScrollWidth + 1 + 200; + _HScrollBar.Minimum = 0; + + _VScrollBar.Enabled = _VScrollBar.Maximum > 0; + _HScrollBar.Enabled = _HScrollBar.Maximum > 0; + } + /// + /// 重绘 + /// + public new void Invalidate() + { + base.Invalidate(); + _ContentControl.Invalidate(); + } + /// + /// 销毁 + /// + /// + protected override void Dispose(bool disposing) + { + this.Brushes?.Dispose(); + base.Dispose(disposing); + } + class GanttChartCanvas : Control + { + public GanttChartCanvas() + { + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.ResizeRedraw, true); + SetStyle(ControlStyles.Selectable, true); + } + protected override void OnPaintBackground(PaintEventArgs pevent) { } + } + } +} diff --git a/GanttChart/GanttChartViewColumn.cs b/GanttChart/GanttChartViewColumn.cs new file mode 100644 index 0000000..b43a587 --- /dev/null +++ b/GanttChart/GanttChartViewColumn.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + public class GanttChartViewColumn + { + } +} diff --git a/GanttChart/GanttChartViewColumnGroup.cs b/GanttChart/GanttChartViewColumnGroup.cs new file mode 100644 index 0000000..41aba2c --- /dev/null +++ b/GanttChart/GanttChartViewColumnGroup.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + public class GanttChartViewColumnGroup + { + } +} diff --git a/GanttChart/GanttChartViewItem.cs b/GanttChart/GanttChartViewItem.cs new file mode 100644 index 0000000..809baf8 --- /dev/null +++ b/GanttChart/GanttChartViewItem.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图项 + /// + public class GanttChartViewItem : TimeRange + { + /// + /// 项的时间范围集合 + /// + public ICollection Ranges { get; set; } + /// + /// 标题 + /// + public string Title { get; set; } + /// + /// 提示内容 + /// + public string Tooltip { get; set; } + /// + /// 顶部位置 + /// + internal int Top { get; set; } + /// + /// 文本宽度 + /// + internal int TextWidth { get; set; } + /// + /// 数据 + /// + public object Data { get; set; } + /// + /// 是否警告 + /// + public bool IsWarnning { get; set; } + } +} diff --git a/GanttChart/GanttChartViewItemBlockType.cs b/GanttChart/GanttChartViewItemBlockType.cs new file mode 100644 index 0000000..5f90170 --- /dev/null +++ b/GanttChart/GanttChartViewItemBlockType.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图项的换行模式 + /// + public enum GanttChartViewItemBlockType + { + /// + /// 紧凑 + /// + Compact, + /// + /// 文字超过换行 + /// + TextOverBlock, + /// + /// 强制换行 + /// + Block, + /// + /// 回归 + /// + Return + } +} diff --git a/GanttChart/GanttChartViewRow.cs b/GanttChart/GanttChartViewRow.cs new file mode 100644 index 0000000..2e87b4b --- /dev/null +++ b/GanttChart/GanttChartViewRow.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 甘特图行 + /// + public class GanttChartViewRow + { + /// + /// 标题 + /// + public string Title { get; set; } + /// + /// 顶部 + /// + internal int Top { get; set; } + /// + /// 按钮 + /// + internal int Bottom { get; set; } + /// + /// 项集合 + /// + public List Items { get; set; } + } +} diff --git a/GanttChart/Properties/AssemblyInfo.cs b/GanttChart/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d8d03c1 --- /dev/null +++ b/GanttChart/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("GanttChart")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GanttChart")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("dea91cb9-1011-4ee7-b8e1-72428f731789")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GanttChart/Properties/Resources.Designer.cs b/GanttChart/Properties/Resources.Designer.cs new file mode 100644 index 0000000..da1a28c --- /dev/null +++ b/GanttChart/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace GanttChart.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GanttChart.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/GanttChart/Properties/Resources.resx b/GanttChart/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/GanttChart/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GanttChart/Properties/Settings.Designer.cs b/GanttChart/Properties/Settings.Designer.cs new file mode 100644 index 0000000..9879631 --- /dev/null +++ b/GanttChart/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace GanttChart.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/GanttChart/Properties/Settings.settings b/GanttChart/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/GanttChart/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/GanttChart/TimeRange.cs b/GanttChart/TimeRange.cs new file mode 100644 index 0000000..c7458f2 --- /dev/null +++ b/GanttChart/TimeRange.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GanttChart +{ + /// + /// 时间范围 + /// + public class TimeRange + { + /// + /// 开始时间 + /// + public DateTime StartTime { get; set; } + /// + /// 结束时间 + /// + public DateTime EndTime { get; set; } + /// + /// 左边 + /// + internal int Left { get; set; } + /// + /// 宽度 + /// + internal int Width { get; set; } + } +} diff --git a/SunlightCentralizedControlManagement(SCCM).sln b/SunlightCentralizedControlManagement(SCCM).sln index 708ea7a..24208a7 100644 --- a/SunlightCentralizedControlManagement(SCCM).sln +++ b/SunlightCentralizedControlManagement(SCCM).sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.11.35312.102 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SunlightCentralizedControlManagement_SCCM_", "SunlightCentralizedControlManagement_SCCM_.csproj", "{2CFE8A79-C7DC-47AC-8D0C-2E03010E2458}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GanttChart", "GanttChart\GanttChart.csproj", "{DEA91CB9-1011-4EE7-B8E1-72428F731789}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {2CFE8A79-C7DC-47AC-8D0C-2E03010E2458}.Debug|Any CPU.Build.0 = Debug|Any CPU {2CFE8A79-C7DC-47AC-8D0C-2E03010E2458}.Release|Any CPU.ActiveCfg = Release|Any CPU {2CFE8A79-C7DC-47AC-8D0C-2E03010E2458}.Release|Any CPU.Build.0 = Release|Any CPU + {DEA91CB9-1011-4EE7-B8E1-72428F731789}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEA91CB9-1011-4EE7-B8E1-72428F731789}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEA91CB9-1011-4EE7-B8E1-72428F731789}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEA91CB9-1011-4EE7-B8E1-72428F731789}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/View/ProductionPlanningView.xaml b/View/ProductionPlanningView.xaml index 602813f..44eb6ed 100644 --- a/View/ProductionPlanningView.xaml +++ b/View/ProductionPlanningView.xaml @@ -22,6 +22,7 @@ + diff --git a/View/ProductionPlanningView.xaml.cs b/View/ProductionPlanningView.xaml.cs index c8ddecd..19bbf14 100644 --- a/View/ProductionPlanningView.xaml.cs +++ b/View/ProductionPlanningView.xaml.cs @@ -30,6 +30,8 @@ using static SunlightCentralizedControlManagement_SCCM_.UserClass.SqliteHelper; using static SunlightCentralizedControlManagement_SCCM_.WindowsView.ViewStep; using static System.Windows.Forms.VisualStyles.VisualStyleElement; using SunlightCentralizedControlManagement_SCCM_.ViewModel; +using LiveChartsCore.SkiaSharpView.WPF; +using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder; namespace SunlightCentralizedControlManagement_SCCM_.View { @@ -64,8 +66,6 @@ namespace SunlightCentralizedControlManagement_SCCM_.View webBrowser.NavigateToString(html); - - } private void ListViewItem_Quit(object sender, System.Windows.Input.MouseButtonEventArgs e)//退出事件