// *********************************************************************** // Assembly : HZH_Controls // Created : 2019-09-26 // // *********************************************************************** // // Copyright by Huang Zhenghui(黄正辉) All, QQ group:568015492 QQ:623128629 Email:623128629@qq.com // // // Blog: https://www.cnblogs.com/bfyx // GitHub:https://github.com/kwwwvagaa/NetWinformControl // gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git // // If you use this code, please keep this note. // *********************************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing; using System.Drawing.Drawing2D; using System.ComponentModel; namespace HZH_Controls.Controls { /// /// Class UCFunnelChart. /// Implements the /// /// public class UCFunnelChart : UserControl { /// /// The title /// private string title; /// /// Gets or sets the title. /// /// The title. [Browsable(true)] [Category("自定义")] [Description("获取或设置标题")] public string Title { get { return title; } set { title = value; ResetTitleSize(); Invalidate(); } } /// /// The title font /// private Font titleFont = new Font("微软雅黑", 12); /// /// Gets or sets the title font. /// /// The title font. [Browsable(true)] [Category("自定义")] [Description("获取或设置标题字体")] public Font TitleFont { get { return titleFont; } set { titleFont = value; ResetTitleSize(); Invalidate(); } } /// /// The title fore color /// private Color titleForeColor = Color.Black; /// /// Gets or sets the color of the title fore. /// /// The color of the title fore. [Browsable(true)] [Category("自定义")] [Description("获取或设置标题文字颜色")] public Color TitleForeColor { get { return titleForeColor; } set { titleForeColor = value; Invalidate(); } } /// /// The items /// private FunelChartItem[] items; /// /// Gets or sets the items. /// /// The items. [Browsable(true)] [Category("自定义")] [Description("获取或设置项目")] public FunelChartItem[] Items { get { return items; } set { items = value; Invalidate(); } } /// /// The direction /// private FunelChartDirection direction = FunelChartDirection.UP; /// /// Gets or sets the direction. /// /// The direction. [Browsable(true)] [Category("自定义")] [Description("获取或设置方向")] public FunelChartDirection Direction { get { return direction; } set { direction = value; Invalidate(); } } /// /// The alignment /// private FunelChartAlignment alignment = FunelChartAlignment.Center; /// /// Gets or sets the alignment. /// /// The alignment. [Browsable(true)] [Category("自定义")] [Description("获取或设置对齐方式")] public FunelChartAlignment Alignment { get { return alignment; } set { alignment = value; Invalidate(); } } /// /// The item text align /// private FunelChartAlignment itemTextAlign = FunelChartAlignment.Center; /// /// Gets or sets the item text align. /// /// The item text align. [Browsable(true)] [Category("自定义")] [Description("获取或设置文字位置")] public FunelChartAlignment ItemTextAlign { get { return itemTextAlign; } set { itemTextAlign = value; ResetWorkingRect(); Invalidate(); } } /// /// The show value /// private bool showValue = false; /// /// Gets or sets a value indicating whether [show value]. /// /// true if [show value]; otherwise, false. [Browsable(true)] [Category("自定义")] [Description("获取或设置是否显示值")] public bool ShowValue { get { return showValue; } set { showValue = value; Invalidate(); } } /// /// The value format /// private string valueFormat = "0.##"; /// /// Gets or sets the value format. /// /// The value format. [Browsable(true)] [Category("自定义")] [Description("获取或设置值格式化")] public string ValueFormat { get { return valueFormat; } set { valueFormat = value; Invalidate(); } } /// /// The m rect working /// RectangleF m_rectWorking; /// /// The m title size /// SizeF m_titleSize = SizeF.Empty; /// /// The int split width /// int intSplitWidth = 1; /// /// Initializes a new instance of the class. /// public UCFunnelChart() { this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.DoubleBuffer, true); this.SetStyle(ControlStyles.ResizeRedraw, true); this.SetStyle(ControlStyles.Selectable, true); this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); this.SetStyle(ControlStyles.UserPaint, true); this.FontChanged += UCFunnelChart_FontChanged; Font = new Font("微软雅黑", 8); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.SizeChanged += UCFunnelChart_SizeChanged; Size = new System.Drawing.Size(150, 150); items = new FunelChartItem[0]; if (ControlHelper.IsDesignMode()) { items = new FunelChartItem[5]; for (int i = 0; i < 5; i++) { items[i] = new FunelChartItem() { Text = "item" + i, Value = 10 * (i + 1) }; } } } /// /// Handles the FontChanged event of the UCFunnelChart control. /// /// The source of the event. /// The instance containing the event data. void UCFunnelChart_FontChanged(object sender, EventArgs e) { ResetWorkingRect(); } /// /// Handles the SizeChanged event of the UCFunnelChart control. /// /// The source of the event. /// The instance containing the event data. void UCFunnelChart_SizeChanged(object sender, EventArgs e) { ResetWorkingRect(); } /// /// Resets the working rect. /// private void ResetWorkingRect() { if (itemTextAlign == FunelChartAlignment.Center) { m_rectWorking = new RectangleF(0, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); } else if (itemTextAlign == FunelChartAlignment.Left) { float fltMax = 0; if (items != null && items.Length > 0) { using (Graphics g = this.CreateGraphics()) { fltMax = items.Max(p => g.MeasureString(p.Text, Font).Width); } } m_rectWorking = new RectangleF(fltMax, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width - fltMax, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); } else { float fltMax = 0; if (items != null && items.Length > 0) { using (Graphics g = this.CreateGraphics()) { fltMax = items.Max(p => g.MeasureString(p.Text, Font).Width); } } m_rectWorking = new RectangleF(0, m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10), this.Width - fltMax, this.Height - (m_titleSize.Height == 0 ? 0 : (m_titleSize.Height + 10))); } } /// /// Resets the size of the title. /// private void ResetTitleSize() { if (string.IsNullOrEmpty(title)) { m_titleSize = SizeF.Empty; } else { using (Graphics g = this.CreateGraphics()) { m_titleSize = g.MeasureString(title, titleFont); m_titleSize.Height += 20; } } ResetWorkingRect(); } /// /// 引发 事件。 /// /// 包含事件数据的 。 protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); var g = e.Graphics; g.SetGDIHigh(); if (!string.IsNullOrEmpty(title)) { g.DrawString(title, titleFont, new SolidBrush(titleForeColor), new RectangleF(0, 0, this.Width, m_titleSize.Height), new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); } if (items == null || items.Length <= 0) { g.DrawString("没有数据", Font, new SolidBrush(Color.Black), this.m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); return; } List lstItems; if (direction == FunelChartDirection.UP) { lstItems = items.OrderBy(p => p.Value).ToList(); } else { lstItems = items.OrderByDescending(p => p.Value).ToList(); } List lstRects = new List(); List lstPaths = new List(); float maxValue = lstItems.Max(p => p.Value); float dblSplitHeight = m_rectWorking.Height / lstItems.Count; for (int i = 0; i < lstItems.Count; i++) { FunelChartItem item = lstItems[i]; if (item.ValueColor == null || item.ValueColor == Color.Empty || item.ValueColor == Color.Transparent) item.ValueColor = ControlHelper.Colors[i]; switch (alignment) { case FunelChartAlignment.Left: lstRects.Add(new RectangleF(m_rectWorking.Left, m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); break; case FunelChartAlignment.Center: lstRects.Add(new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - (item.Value / maxValue * m_rectWorking.Width)) / 2, m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); break; case FunelChartAlignment.Right: lstRects.Add(new RectangleF(m_rectWorking.Right - (item.Value / maxValue * m_rectWorking.Width), m_rectWorking.Top + dblSplitHeight * i, item.Value / maxValue * m_rectWorking.Width, dblSplitHeight)); break; } } for (int i = 0; i < lstRects.Count; i++) { var rect = lstRects[i]; GraphicsPath path = new GraphicsPath(); List lstPoints = new List(); if (direction == FunelChartDirection.UP) { switch (alignment) { case FunelChartAlignment.Left: lstPoints.Add(new PointF(rect.Left, rect.Top)); if (i != 0) { lstPoints.Add(new PointF(lstRects[i - 1].Right, rect.Top)); } break; case FunelChartAlignment.Center: if (i == 0) { lstPoints.Add(new PointF(rect.Left + rect.Width / 2, rect.Top)); } else { lstPoints.Add(new PointF(lstRects[i - 1].Left, rect.Top)); lstPoints.Add(new PointF(lstRects[i - 1].Right, rect.Top)); } break; case FunelChartAlignment.Right: if (i == 0) { lstPoints.Add(new PointF(rect.Right, rect.Top)); } else { lstPoints.Add(new PointF(rect.Right - lstRects[i - 1].Width, rect.Top)); lstPoints.Add(new PointF(rect.Right, rect.Top)); } break; } lstPoints.Add(new PointF(rect.Right, rect.Bottom - intSplitWidth)); lstPoints.Add(new PointF(rect.Left, rect.Bottom - intSplitWidth)); } else { lstPoints.Add(new PointF(rect.Left, rect.Top + intSplitWidth)); lstPoints.Add(new PointF(rect.Right, rect.Top + intSplitWidth)); switch (alignment) { case FunelChartAlignment.Left: if (i == lstRects.Count - 1) { lstPoints.Add(new PointF(rect.Left, rect.Bottom)); } else { lstPoints.Add(new PointF(lstRects[i + 1].Right, rect.Bottom)); lstPoints.Add(new PointF(rect.Left, rect.Bottom)); } break; case FunelChartAlignment.Center: if (i == lstRects.Count - 1) { lstPoints.Add(new PointF(rect.Left + rect.Width / 2, rect.Bottom)); } else { lstPoints.Add(new PointF(lstRects[i + 1].Right, rect.Bottom)); lstPoints.Add(new PointF(lstRects[i + 1].Left, rect.Bottom)); } break; case FunelChartAlignment.Right: if (i == lstRects.Count - 1) { lstPoints.Add(new PointF(rect.Right, rect.Bottom)); } else { lstPoints.Add(new PointF(rect.Right, rect.Bottom)); lstPoints.Add(new PointF(lstRects[i + 1].Left, rect.Bottom)); } break; } } path.AddLines(lstPoints.ToArray()); path.CloseAllFigures(); // g.DrawPath(new Pen(new SolidBrush(lstItems[i].ValueColor.Value)), path); g.FillPath(new SolidBrush(lstItems[i].ValueColor.Value), path); //写字 if (itemTextAlign == FunelChartAlignment.Center) { g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? Color.White : lstItems[i].TextForeColor.Value), rect, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }); } else if (itemTextAlign == FunelChartAlignment.Left) { g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value), new RectangleF(0, rect.Top, rect.Left, rect.Height), new StringFormat() { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Center }); g.DrawLine(new Pen(new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value)), rect.Left, rect.Top + rect.Height / 2, rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); } else { g.DrawString(lstItems[i].Text + (ShowValue ? lstItems[i].Value.ToString("\n" + valueFormat) : ""), Font, new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value), new RectangleF(rect.Right, rect.Top, this.Width - rect.Right, rect.Height), new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center }); g.DrawLine(new Pen(new SolidBrush((lstItems[i].TextForeColor == null || lstItems[i].TextForeColor == Color.Empty || lstItems[i].TextForeColor == Color.Transparent) ? lstItems[i].ValueColor.Value : lstItems[i].TextForeColor.Value)), rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, rect.Right, rect.Top + rect.Height / 2); } } } } }