多点滑块进度控件----------WinForm控件开发系列

2019-08-26

多点滑块进度控件----------WinForm控件开发系列

技术图片

  /// <summary>
  /// 多点滑块进度控件
  /// </summary>
  [ToolboxItem(true)]
  [DefaultProperty("Items")]
  [DefaultEvent("SlideValueChanged")]
  [Description("多点滑块进度控件")]
  public partial class SlideBarExt : Control
  {
    public delegate void EventHandler(object sender, SlideValueEventArgs e);

    #region

    private event EventHandler slideValueChanged;
    /// <summary>
    /// 滑块选项值更改事件
    /// </summary>
    [Description("滑块选项值更改事件")]
    public event EventHandler SlideValueChanged
    {
      add { this.slideValueChanged += value; }
      remove { this.slideValueChanged -= value; }
    }

    private SlideOrientation orientation = SlideOrientation.HorizontalBottom;
    /// <summary>
    /// 控件滑块方向位置
    /// </summary>
    [DefaultValue(SlideOrientation.HorizontalBottom)]
    [Description("控件滑块方向位置")]
    public SlideOrientation Orientation
    {
      get { return this.orientation; }
      set
      {
        if (this.orientation == value)
          return;
        this.orientation = value;
        this.InitializeSlideValueRectangleFs();
        this.Invalidate();
      }
    }

    private int slidePadding = 6;
    /// <summary>
    /// 控件边距
    /// </summary>
    [DefaultValue(6)]
    [Description("控件边距")]
    public int SlidePadding
    {
      get { return this.slidePadding; }
      set
      {
        if (this.slidePadding == value || value < 0)
          return;
        this.slidePadding = value;
        this.InitializeSlideValueRectangleFs();
        this.Invalidate();
      }
    }

    private float maxValue = 100f;
    /// <summary>
    /// 最大值
    /// </summary>
    [DefaultValue(100f)]
    [Description("最大值")]
    public float MaxValue
    {
      get { return this.maxValue; }
      set
      {
        if (this.maxValue == value || value < this.minValue)
          return;
        this.maxValue = value;
        this.Invalidate();
      }
    }

    private float minValue = 0f;
    /// <summary>
    /// 最小值
    /// </summary>
    [DefaultValue(0f)]
    [Description("最小值")]
    public float MinValue
    {
      get { return this.minValue; }
      set
      {
        if (this.minValue == value || value > this.maxValue)
          return;
        this.minValue = value;
        this.Invalidate();
      }
    }

    #region 背景

    private Color backdropColor = Color.FromArgb(76, 240, 128, 128);
    /// <summary>
    /// 背景颜色
    /// </summary>
    [DefaultValue(typeof(Color), "76, 240, 128, 128")]
    [Description("背景颜色")]
    [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
    public Color BackdropColor
    {
      get { return this.backdropColor; }
      set
      {
        if (this.backdropColor == value)
          return;
        this.backdropColor = value;
        this.Invalidate();
      }
    }

    private int backdropThickness = 8;
    /// <summary>
    /// 背景大小
    /// </summary>
    [DefaultValue(8)]
    [Description("背景大小")]
    public int BackdropThickness
    {
      get { return this.backdropThickness; }
      set
      {
        if (this.backdropThickness == value || value < 0)
          return;
        this.backdropThickness = value;
        this.Invalidate();
      }
    }

    private bool backdropRadius = true;
    /// <summary>
    /// 背景是否为圆角
    /// </summary>
    [DefaultValue(true)]
    [Description("背景是否为圆角")]
    public bool BackdropRadius
    {
      get { return this.backdropRadius; }
      set
      {
        if (this.backdropRadius == value)
          return;
        this.backdropRadius = value;
        this.Invalidate();
      }
    }

    #endregion

    #region 滑块

    private int slideWidth = 20;
    /// <summary>
    /// 滑块宽度
    /// </summary>
    [DefaultValue(20)]
    [Description("滑块宽度")]
    public int SlideWidth
    {
      get { return this.slideWidth; }
      set
      {
        if (this.slideWidth == value || value < 0)
          return;
        this.slideWidth = value;
        this.InitializeSlideValueRectangleFs();
        this.Invalidate();
      }
    }

    private int slideHeight = 20;
    /// <summary>
    /// 滑块高度
    /// </summary>
    [DefaultValue(20)]
    [Description("滑块高度")]
    public int SlideHeight
    {
      get { return this.slideHeight; }
      set
      {
        if (this.slideHeight == value || value < 0)
          return;
        this.slideHeight = value;
        this.InitializeSlideValueRectangleFs();
        this.Invalidate();
      }
    }

    private int slideRadius = 10;
    /// <summary>
    /// 滑块圆角大小(限于Flat)
    /// </summary>
    [DefaultValue(10)]
    [Description("滑块圆角大小(限于Flat)")]
    public int SlideRadius
    {
      get { return this.slideRadius; }
      set
      {
        if (this.slideRadius == value || value < 0)
          return;
        this.slideRadius = value;
        this.InitializeSlideValueRectangleFs();
        this.Invalidate();
      }
    }

    private Color slideColor = Color.FromArgb(255, 128, 128);
    /// <summary>
    /// 滑块颜色(总体设置)
    /// </summary>
    [DefaultValue(typeof(Color), "255, 128, 128")]
    [Description("滑块颜色(总体设置)")]
    [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
    public Color SlideColor
    {
      get { return this.slideColor; }
      set
      {
        if (this.slideColor == value)
          return;
        this.slideColor = value;
        this.Invalidate();
      }
    }

    private Color slideProgressColor = Color.FromArgb(175, 240, 128, 128);
    /// <summary>
    /// 滑块进度颜色(仅限于只有一个滑块)
    /// </summary>
    [DefaultValue(typeof(Color), "175, 240, 128, 128")]
    [Description("滑块进度颜色(仅限于只有一个滑块)")]
    [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
    public Color SlideProgressColor
    {
      get { return this.slideProgressColor; }
      set
      {
        if (this.slideProgressColor == value)
          return;
        this.slideProgressColor = value;
        this.Invalidate();
      }
    }

    private int selectIndex = -1;
    /// <summary>
    /// 当前选中滑块的索引
    /// </summary>
    [Browsable(false)]
    [DefaultValue(-1)]
    [Description("当前选中滑块的索引")]
    public int SelectIndex
    {
      get { return this.selectIndex; }
    }

    private SlideValueCollection slideValueCollection;
    /// <summary>
    /// 滑块选项集合
    /// </summary>
    [DefaultValue(null)]
    [Description("滑块选项集合")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public SlideValueCollection Items
    {
      get
      {
        if (this.slideValueCollection == null)
          this.slideValueCollection = new SlideValueCollection(this);
        return this.slideValueCollection;
      }
    }

    #endregion

    #region 提示信息

    private bool tipShow = true;
    /// <summary>
    /// 是否显示提示信息
    /// </summary>
    [DefaultValue(true)]
    [Description("是否显示提示信息")]
    public bool TipShow
    {
      get { return this.tipShow; }
      set
      {
        if (this.tipShow == value)
          return;
        this.tipShow = value;
        this.Invalidate();
      }
    }

    private Color tipBackdropColor = Color.FromArgb(126, 255, 128, 128);
    /// <summary>
    /// 提示信息背景颜色
    /// </summary>
    [DefaultValue(typeof(Color), "126, 255, 128, 128")]
    [Description("提示信息背景颜色")]
    [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
    public Color TipBackdropColor
    {
      get { return this.tipBackdropColor; }
      set
      {
        if (this.tipBackdropColor == value)
          return;
        this.tipBackdropColor = value;
        this.Invalidate();
      }
    }

    private Font tipFont = new Font("宋体", 11, FontStyle.Regular);
    /// <summary>
    /// 提示信息字体
    /// </summary>
    [DefaultValue(typeof(Font), "11pt style=Regular")]
    [Description("提示信息字体")]
    public Font TipFont
    {
      get { return this.tipFont; }
      set
      {
        if (this.tipFont == value)
          return;
        this.tipFont = value;
        this.Invalidate();
      }
    }

    private Color tipColor = Color.FromArgb(150, 255, 255, 255);
    /// <summary>
    /// 提示信息颜色
    /// </summary>
    [DefaultValue(typeof(Color), "150,255, 255, 255")]
    [Description("提示信息颜色")]
    [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
    public Color TipColor
    {
      get { return this.tipColor; }
      set
      {
        if (this.tipColor == value)
          return;
        this.tipColor = value;
        this.Invalidate();
      }
    }

    #endregion

    protected override Size DefaultSize
    {
      get
      {
        return new Size(300, 60);
      }
    }

    /// <summary>
    /// 鼠标状态
    /// </summary>
    private MoveStatus move_status = MoveStatus.Leave;
    /// <summary>
    /// 鼠标坐标
    /// </summary>
    private Point move_point;

    #endregion

    public SlideBarExt()
    {
      SetStyle(ControlStyles.UserPaint, true);
      SetStyle(ControlStyles.AllPaintingInWmPaint, true);
      SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
      SetStyle(ControlStyles.ResizeRedraw, true);
      SetStyle(ControlStyles.SupportsTransparentBackColor, true);

      InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      base.OnPaint(e);

      Graphics g = e.Graphics;
      g.SmoothingMode = SmoothingMode.AntiAlias;
      Rectangle rectf = new Rectangle(this.SlidePadding, this.SlidePadding, e.ClipRectangle.Width - this.SlidePadding * 2, e.ClipRectangle.Height - this.SlidePadding * 2);

      #region 背景

      Pen backdrop_pen = new Pen(this.BackdropColor, this.BackdropThickness);
      int backdrop_x_start = 0;
      int backdrop_x_end = 0;
      int backdrop_y = 0;
      if (this.Orientation == SlideOrientation.HorizontalTop || this.Orientation == SlideOrientation.HorizontalBottom)
      {
        backdrop_x_start = rectf.X;
        backdrop_x_end = rectf.Right;
        backdrop_y = (this.Orientation == SlideOrientation.HorizontalTop) ? this.SlidePadding + this.SlideHeight / 2 : rectf.Bottom - this.SlideHeight / 2 - this.SlidePadding;
        if (this.BackdropRadius)
        {
          backdrop_pen.StartCap = LineCap.Round;
          backdrop_pen.EndCap = LineCap.Round;
          backdrop_x_start += this.BackdropThickness / 2;
          backdrop_x_end -= this.BackdropThickness / 2;
        }
        g.DrawLine(backdrop_pen, backdrop_x_start, backdrop_y, backdrop_x_end, backdrop_y);
      }
      else
      {
        backdrop_x_start = rectf.Y;
        backdrop_x_end = rectf.Bottom;
        backdrop_y = (this.Orientation == SlideOrientation.VerticalLeft) ? this.SlidePadding + this.SlideWidth / 2 : rectf.Right - this.SlideWidth / 2 - this.SlidePadding;
        if (this.BackdropRadius)
        {
          backdrop_pen.StartCap = LineCap.Round;
          backdrop_pen.EndCap = LineCap.Round;
          backdrop_x_start += this.BackdropThickness / 2;
          backdrop_x_end -= this.BackdropThickness / 2;
        }
        g.DrawLine(backdrop_pen, backdrop_y, backdrop_x_start, backdrop_y, backdrop_x_end);
      }
      backdrop_pen.Dispose();

      #endregion

      #region 进度值
      if (this.Orientation == SlideOrientation.HorizontalTop || this.Orientation == SlideOrientation.HorizontalBottom)
      {
        if (this.Items.Count == 1)
        {
          int slideProgress_x_start = rectf.X;
          int slideProgress_x_end = (int)(this.Items[0].RectF.Right - this.Items[0].RectF.Width) + this.SlideWidth / 2;
          int slideProgress_y = (this.Orientation == SlideOrientation.HorizontalTop) ? this.SlidePadding + this.SlideHeight / 2 : rectf.Bottom - this.SlideHeight / 2 - this.SlidePadding;
          Pen slideProgress_pen = new Pen(this.SlideProgressColor, this.BackdropThickness);
          if (this.BackdropRadius)
          {
            slideProgress_pen.StartCap = LineCap.Round;
            slideProgress_x_start += this.BackdropThickness / 2;
          }
          g.DrawLine(slideProgress_pen, slideProgress_x_start, slideProgress_y, slideProgress_x_end, slideProgress_y);
          slideProgress_pen.Dispose();
        }
      }
      else
      {
        if (this.Items.Count == 1)
        {
          int slideProgress_x_start = rectf.Bottom;
          int slideProgress_x_end = (int)(this.Items[0].RectF.Bottom - this.Items[0].RectF.Height);
          int slideProgress_y = (this.Orientation == SlideOrientation.VerticalLeft) ? this.SlidePadding + this.SlideWidth / 2 : rectf.Right - this.SlideWidth / 2 - this.SlidePadding;
          Pen slideProgress_pen = new Pen(this.SlideProgressColor, this.BackdropThickness);
          if (this.BackdropRadius)
          {
            slideProgress_pen.StartCap = LineCap.Round;
            slideProgress_x_start -= this.BackdropThickness / 2;
          }
          g.DrawLine(slideProgress_pen, slideProgress_y, slideProgress_x_start, slideProgress_y, slideProgress_x_end);
          slideProgress_pen.Dispose();
        }
      }

      #endregion

      #region  滑块

      int line_area = 6;//连线区域大小
      Pen tooltipline_pen = new Pen(this.TipBackdropColor, 2);
      SolidBrush tooltipback_sb = new SolidBrush(this.TipBackdropColor);
      for (int i = 0; i < this.Items.Count; i++)
      {
        #region 滑块
        SolidBrush slide_sb = new SolidBrush(this.Items[i].SlideColor == Color.Empty ? this.SlideColor : this.Items[i].SlideColor);
        GraphicsPath slide_gp = TransformCircular(new RectangleF(this.Items[i].RectF.X, this.Items[i].RectF.Y, this.Items[i].RectF.Width, this.Items[i].RectF.Height), this.SlideRadius);
        g.FillPath(slide_sb, slide_gp);
        slide_gp.Dispose();
        slide_sb.Dispose();
        #endregion

        #region 计算提示信息的位置
        if (this.TipShow)
        {
          string tooltiptext_str = this.Items[i].Value.ToString("F2");
          Size tooltiptext_size = TextRenderer.MeasureText(tooltiptext_str, this.TipFont, new Size(), TextFormatFlags.NoPadding | TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);

          Rectangle tooltipback_rect = new Rectangle(0, 0, tooltiptext_size.Width, tooltiptext_size.Height);
          if (this.Orientation == SlideOrientation.HorizontalTop)
          {
            tooltipback_rect.X = (int)this.Items[i].RectF.X + ((int)this.Items[i].RectF.Width - tooltipback_rect.Width) / 2;
            if (tooltipback_rect.X < 0)
              tooltipback_rect.X = 0;
            tooltipback_rect.Y = (int)this.Items[i].RectF.Bottom + line_area;
            if (i > 0 && (tooltipback_rect.X < this.Items[i - 1].TipRect.Right + line_area))
            {
              tooltipback_rect.X = (int)this.Items[i - 1].TipRect.Right + line_area;
            }
          }
          else if (this.Orientation == SlideOrientation.HorizontalBottom)
          {
            tooltipback_rect.X = (int)this.Items[i].RectF.X + ((int)this.Items[i].RectF.Width - tooltipback_rect.Width) / 2;
            if (tooltipback_rect.X < 0)
              tooltipback_rect.X = 0;
            tooltipback_rect.Y = (int)this.Items[i].RectF.Y - line_area - tooltipback_rect.Height;
            if (i > 0 && (tooltipback_rect.X < this.Items[i - 1].TipRect.Right + line_area))
            {
              tooltipback_rect.X = (int)this.Items[i - 1].TipRect.Right + line_area;
            }
          }
          else if (this.Orientation == SlideOrientation.VerticalLeft)
          {
            tooltipback_rect.X = (int)this.Items[i].RectF.Right + line_area;
            tooltipback_rect.Y = (int)this.Items[i].RectF.Y + ((int)this.Items[i].RectF.Height - tooltipback_rect.Height) / 2;
            if (tooltipback_rect.Y > rectf.Bottom - tooltipback_rect.Height)
              tooltipback_rect.Y = rectf.Bottom - tooltipback_rect.Height;
            if (i > 0 && (tooltipback_rect.Y > (int)this.Items[i - 1].TipRect.Y - line_area - tooltipback_rect.Height))
            {
              tooltipback_rect.Y = (int)this.Items[i - 1].TipRect.Y - line_area - tooltipback_rect.Height;
            }
          }
          else if (this.Orientation == SlideOrientation.VerticalRight)
          {
            tooltipback_rect.X = (int)this.Items[i].RectF.X - line_area - tooltipback_rect.Width;
            tooltipback_rect.Y = (int)this.Items[i].RectF.Y + ((int)this.Items[i].RectF.Height - tooltipback_rect.Height) / 2;
            if (tooltipback_rect.Y > rectf.Bottom - tooltipback_rect.Height)
              tooltipback_rect.Y = rectf.Bottom - tooltipback_rect.Height;
            if (i > 0 && (tooltipback_rect.Y > (int)this.Items[i - 1].TipRect.Y - line_area - tooltipback_rect.Height))
            {
              tooltipback_rect.Y = (int)this.Items[i - 1].TipRect.Y - line_area - tooltipback_rect.Height;
            }
          }

          this.Items[i].TipRect = tooltipback_rect;
        }
        #endregion
      }

      #region
      if (this.TipShow)
      {
        for (int i = this.Items.Count - 1; i >= 0; i--)
        {
          if (this.Orientation == SlideOrientation.HorizontalTop || this.Orientation == SlideOrientation.HorizontalBottom)
          {
            if ((i == this.Items.Count - 1))
            {
              if (this.Items[i].TipRect.Right > rectf.Right)
                this.Items[i].TipRect = new Rectangle((int)(rectf.Right - this.Items[i].TipRect.Width), this.Items[i].TipRect.Y, this.Items[i].TipRect.Width, this.Items[i].TipRect.Height);
            }
            else
            {
              if (this.Items[i].TipRect.Right + line_area > this.Items[i + 1].TipRect.X)
                this.Items[i].TipRect = new Rectangle((this.Items[i + 1].TipRect.X - this.Items[i].TipRect.Width - line_area), this.Items[i].TipRect.Y, this.Items[i].TipRect.Width, this.Items[i].TipRect.Height);
            }

            this.Items[i].Slidepoint = new PointF(this.Items[i].RectF.X + this.Items[i].RectF.Width / 2, this.Items[i].RectF.Bottom);
            this.Items[i].Tippoint = new PointF(this.Items[i].TipRect.X + this.Items[i].TipRect.Width / 2, this.Items[i].TipRect.Y);
            if (this.Orientation == SlideOrientation.HorizontalBottom)
            {
              this.Items[i].Slidepoint = new PointF(this.Items[i].RectF.X + this.Items[i].RectF.Width / 2, this.Items[i].RectF.Y);
              this.Items[i].Tippoint = new PointF(this.Items[i].TipRect.X + this.Items[i].TipRect.Width / 2, this.Items[i].TipRect.Bottom);
            }
          }
          else if (this.Orientation == SlideOrientation.VerticalLeft || this.Orientation == SlideOrientation.VerticalRight)
          {
            if (i == this.Items.Count - 1)
            {
              if (this.Items[i].TipRect.Y < rectf.Y)
                this.Items[i].TipRect = new Rectangle(this.Items[i].TipRect.X, rectf.Y, this.Items[i].TipRect.Width, this.Items[i].TipRect.Height);
            }
            else
            {
              if (this.Items[i].TipRect.Y < this.Items[i + 1].TipRect.Bottom + line_area)
                this.Items[i].TipRect = new Rectangle(this.Items[i].TipRect.X, this.Items[i + 1].TipRect.Bottom + line_area, this.Items[i].TipRect.Width, this.Items[i].TipRect.Height);
            }

            this.Items[i].Slidepoint = new PointF(this.Items[i].RectF.Right, this.Items[i].RectF.Bottom - this.Items[i].RectF.Height / 2);
            this.Items[i].Tippoint = new PointF(this.Items[i].TipRect.Left, this.Items[i].TipRect.Bottom - this.Items[i].TipRect.Height / 2);
            if (this.Orientation == SlideOrientation.VerticalRight)
            {
              this.Items[i].Slidepoint = new PointF(this.Items[i].RectF.X, this.Items[i].RectF.Bottom - this.Items[i].RectF.Height / 2);
              this.Items[i].Tippoint = new PointF(this.Items[i].TipRect.Right, this.Items[i].TipRect.Bottom - this.Items[i].TipRect.Height / 2);
            }
          }

          g.DrawLine(tooltipline_pen, this.Items[i].Slidepoint, this.Items[i].Tippoint);
          g.FillRectangle(tooltipback_sb, this.Items[i].TipRect);
          string tooltiptext_str = this.Items[i].Value.ToString("F2");
          TextRenderer.DrawText(g, tooltiptext_str, this.TipFont, this.Items[i].TipRect, this.TipColor, TextFormatFlags.NoPadding | TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
        }
      }
      #endregion

      tooltipback_sb.Dispose();
      tooltipline_pen.Dispose();

      #endregion
    }

    protected override void OnResize(EventArgs e)
    {
      base.OnResize(e);

      this.InitializeSlideValueRectangleFs();
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
      base.OnMouseDown(e);

      this.move_status = MoveStatus.Down;
      Point point = this.PointToClient(Control.MousePosition);
      this.move_point = point;
      this.selectIndex = this.GetSelectSlideIndex(point);
    }
    protected override void OnMouseUp(MouseEventArgs e)
    {
      base.OnMouseUp(e);

      this.move_status = MoveStatus.Up;
      this.selectIndex = -1;
    }
    protected override void OnMouseEnter(EventArgs e)
    {
      base.OnMouseEnter(e);

      this.move_status = MoveStatus.Enter;
    }
    protected override void OnMouseLeave(EventArgs e)
    {
      base.OnMouseLeave(e);

      this.move_status = MoveStatus.Leave;
      this.selectIndex = -1;
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
      base.OnMouseMove(e);

      if (this.move_status == MoveStatus.Down)
      {
        if (this.SelectIndex > -1)
        {
          this.InitializeSlideValue(this.Items[this.SelectIndex]);
        }
      }
      else
      {
        Point point = this.PointToClient(Control.MousePosition);
        this.Cursor = (this.GetSelectSlideIndex(point) > -1) ? Cursors.Hand : Cursors.Default;
      }

    }

    #region

    /// <summary>
    /// 根据当前鼠标坐标计算滑块值
    /// </summary>
    /// <param name="item">当前滑块</param>
    private void InitializeSlideValue(SlideValue item)
    {
      Rectangle rect = new Rectangle(this.SlidePadding, this.SlidePadding, this.ClientRectangle.Width - this.SlidePadding * 2, this.ClientRectangle.Height - this.SlidePadding * 2);
      Point point = this.PointToClient(Control.MousePosition);

      if (this.Orientation == SlideOrientation.HorizontalTop || this.Orientation == SlideOrientation.HorizontalBottom)
      {
        if (point.X == this.move_point.X)
          return;

        int index = this.Items.IndexOf(item);
        float value_l = Math.Abs(this.MinValue) + Math.Abs(this.MaxValue);//值总长度
        float back_l = rect.Width - this.SlideWidth - (this.Items.Count - 1) * this.SlideWidth;//背景总长度
        float increment = value_l / back_l;//一个像素代表曾值量

        float value = item.Value;
        value += increment * (point.X - this.move_point.X);
        this.move_point = point;

        item.Value = value;
      }
      else
      {
        if (point.Y == this.move_point.Y)
          return;

        int index = this.Items.IndexOf(item);
        float value_l = Math.Abs(this.MinValue) + Math.Abs(this.MaxValue);//值总长度
        float back_l = rect.Height - this.SlideHeight - (this.Items.Count - 1) * this.SlideHeight;//背景总长度
        float increment = value_l / back_l;//一个像素代表曾值量

        float value = item.Value;
        value -= increment * (point.Y - this.move_point.Y);
        this.move_point = point;

        item.Value = value;
      }
    }

    /// <summary>
    /// 计算所有滑块rectf
    /// </summary>
    /// <param name="item"></param>
    private void InitializeSlideValueRectangleFs()
    {
      for (int i = 0; i < this.Items.Count; i++)
      {
        this.InitializeSlideValueRectangleF(this.Items[i]);
      }
    }

    /// <summary>
    /// 计算指定滑块rectf
    /// </summary>
    /// <param name="item"></param>
    private void InitializeSlideValueRectangleF(SlideValue item)
    {
      Rectangle rect = new Rectangle(this.SlidePadding, this.SlidePadding, this.ClientRectangle.Width - this.SlidePadding * 2, this.ClientRectangle.Height - this.SlidePadding * 2);

      int index = this.Items.IndexOf(item);
      if (this.Orientation == SlideOrientation.HorizontalBottom || this.Orientation == SlideOrientation.HorizontalTop)
      {
        float back_l = rect.Width - this.SlideWidth - (this.Items.Count - 1) * this.SlideWidth;
        int slide_x = this.SlidePadding + (this.SlideWidth / 2) + (int)(item.Value / (Math.Abs(this.MinValue) + Math.Abs(this.MaxValue)) * back_l);
        if (slide_x < 0)
          slide_x = 0;
        if (slide_x > rect.Right - this.SlideWidth / 2)
          slide_x = rect.Right - this.SlideWidth / 2;

        int slide_y = (this.Orientation == SlideOrientation.HorizontalTop) ? rect.Y : rect.Bottom - this.SlideHeight - this.SlidePadding;
        item.RectF = new RectangleF(slide_x - (this.SlideWidth / 2) + index * this.SlideWidth, slide_y, this.SlideWidth, this.SlideHeight);
      }
      else
      {
        float back_l = rect.Height - this.SlideHeight - (this.Items.Count - 1) * this.SlideHeight;
        int slide_y = rect.Bottom - (this.SlideHeight / 2) - (int)(item.Value / (Math.Abs(this.MinValue) + Math.Abs(this.MaxValue)) * back_l);
        if (slide_y < 0)
          slide_y = 0;
        if (slide_y > rect.Bottom - this.SlideHeight / 2)
          slide_y = rect.Bottom - this.SlideHeight / 2;
        int slide_x = (this.Orientation == SlideOrientation.VerticalLeft) ? rect.X : rect.Right - this.SlideWidth - this.SlidePadding;
        item.RectF = new RectangleF(slide_x, slide_y - (this.SlideHeight / 2) - index * this.SlideHeight, this.SlideWidth, this.SlideHeight);
      }
    }

    /// <summary>
    /// 获取选中滑块索引
    /// </summary>
    /// <param name="point">当前鼠标坐标</param>
    /// <returns></returns>
    private int GetSelectSlideIndex(Point point)
    {
      for (int i = 0; i < this.Items.Count; i++)
      {
        if (this.Items[i].RectF.Contains(point))
          return i;
      }
      return -1;
    }

    /// <summary>
    /// 根据画笔大小转换rectf
    /// </summary>
    /// <param name="rectf">要转换的rectf</param>
    /// <param name="pen">画笔大小大小</param>
    /// <returns></returns>
    private static RectangleF TransformRectangleF(RectangleF rectf, int pen)
    {
      RectangleF result = new RectangleF();
      result.Width = rectf.Width - (pen < 1 ? 0 : pen);
      result.Height = rectf.Height - (pen < 1 ? 0 : pen);
      result.X = rectf.X + (float)(pen / 2);
      result.Y = rectf.Y + (float)(pen / 2);
      return result;
    }

    /// <summary>
    /// 转换成圆角
    /// </summary>
    /// <param name="rectf">要转换的rectf</param>
    /// <param name="radius">圆角半径的大小</param>
    /// <returns></returns>
    private static GraphicsPath TransformCircular(RectangleF rectf, int radius = 0)
    {
      return TransformCircular(rectf, radius, radius, radius, radius);
    }

    /// <summary>
    /// 转换成圆角
    /// </summary>
    /// <param name="rectf">要转换的rectf</param>
    /// <param name="leftTopRadius">左上角</param>
    /// <param name="rightTopRadius">右上角</param>
    /// <param name="rightBottomRadius">右下角</param>
    /// <param name="leftBottomRadius">左下角</param>
    /// <returns></returns>
    private static GraphicsPath TransformCircular(RectangleF rectf, int leftTopRadius = 0, int rightTopRadius = 0, int rightBottomRadius = 0, int leftBottomRadius = 0)
    {
      GraphicsPath gp = new GraphicsPath();
      if (leftTopRadius > 0)
      {
        RectangleF lefttop_rect = new RectangleF(rectf.X, rectf.Y, leftTopRadius * 2, leftTopRadius * 2);
        gp.AddArc(lefttop_rect, 180, 90);
      }
      else
      {
        gp.AddLine(new PointF(rectf.X, rectf.Y), new PointF(rightTopRadius > 0 ? rectf.Right - rightTopRadius * 2 : rectf.Right, rectf.Y));
      }
      if (rightTopRadius > 0)
      {
        RectangleF righttop_rect = new RectangleF(rectf.Right - rightTopRadius * 2, rectf.Y, rightTopRadius * 2, rightTopRadius * 2);
        gp.AddArc(righttop_rect, 270, 90);
      }
      else
      {
        gp.AddLine(new PointF(rectf.Right, rectf.Y), new PointF(rectf.Right, rightBottomRadius > 0 ? rectf.Bottom - rightTopRadius * 2 : rectf.Bottom));
      }
      if (rightBottomRadius > 0)
      {
        RectangleF rightbottom_rect = new RectangleF(rectf.Right - rightTopRadius * 2, rectf.Bottom - rightTopRadius * 2, rightBottomRadius * 2, rightBottomRadius * 2);
        gp.AddArc(rightbottom_rect, 0, 90);
      }
      else
      {
        gp.AddLine(new PointF(rectf.Right, rectf.Bottom), new PointF(leftBottomRadius > 0 ? leftBottomRadius * 2 : rectf.X, rectf.Bottom));
      }
      if (leftBottomRadius > 0)
      {
        RectangleF rightbottom_rect = new RectangleF(rectf.X, rectf.Bottom - leftBottomRadius * 2, leftBottomRadius * 2, leftBottomRadius * 2);
        gp.AddArc(rightbottom_rect, 90, 90);
      }
      else
      {
        gp.AddLine(new PointF(rectf.X, rectf.Bottom), new PointF(rectf.X, leftTopRadius > 0 ? rectf.X + leftTopRadius * 2 : rectf.X));
      }
      gp.CloseAllFigures();
      return gp;
    }

    #endregion

    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    /// <summary>
    /// 滑块选项集合
    /// </summary>
    [Description("滑块选项集合")]
    [Editor(typeof(CollectionEditorExt), typeof(UITypeEditor))]
    public sealed class SlideValueCollection : IList, ICollection, IEnumerable
    {
      private ArrayList slideValueList = new ArrayList();
      private SlideBarExt owner;

      public SlideValueCollection(SlideBarExt owner)
      {
        this.owner = owner;
      }

      #region IEnumerable

      public IEnumerator GetEnumerator()
      {
        SlideValue[] listArray = new SlideValue[this.slideValueList.Count];
        for (int index = 0; index < listArray.Length; ++index)
          listArray[index] = (SlideValue)this.slideValueList[index];
        return listArray.GetEnumerator();
      }

      #endregion

      #region ICollection

      public void CopyTo(Array array, int index)
      {
        for (int i = 0; i < this.Count; i++)
          array.SetValue(this.slideValueList[i], i + index);
      }

      public int Count
      {
        get
        {
          return this.slideValueList.Count;
        }
      }

      public bool IsSynchronized
      {
        get
        {
          return false;
        }
      }

      public object SyncRoot
      {
        get
        {
          return (object)this;
        }
      }

      #endregion

      #region IList

      public int Add(object value)
      {
        SlideValue slideValue = (SlideValue)value;
        slideValue.owner = this.owner;
        slideValue.self = slideValue;
        this.slideValueList.Add(slideValue);
        this.owner.InitializeSlideValueRectangleF(slideValue);
        this.owner.Invalidate();
        return this.Count - 1;
      }

      public void Clear()
      {
        this.slideValueList.Clear();
        this.owner.Invalidate();
      }

      public bool Contains(object value)
      {
        return this.IndexOf(value) != -1;
      }

      public int IndexOf(object value)
      {
        return this.slideValueList.IndexOf(value);
      }

      public void Insert(int index, object value)
      {
        throw new NotImplementedException();
      }

      public bool IsFixedSize
      {
        get { return false; }
      }

      public bool IsReadOnly
      {
        get { return false; }
      }

      public void Remove(object value)
      {
        if (!(value is SlideValue))
          return;
        this.slideValueList.Remove((SlideValue)value);
        this.owner.Invalidate();
      }

      public void RemoveAt(int index)
      {
        this.slideValueList.RemoveAt(index);
        this.owner.Invalidate();
      }

      public SlideValue this[int index]
      {
        get
        {
          return (SlideValue)this.slideValueList[index];
        }
        set
        {
          this.slideValueList[index] = (SlideValue)value;
          this.owner.InitializeSlideValueRectangleF((SlideValue)value);
          this.owner.Invalidate();
        }
      }

      object IList.this[int index]
      {
        get
        {
          return (object)this.slideValueList[index];
        }
        set
        {
          this.slideValueList[index] = (SlideValue)value;
          this.owner.InitializeSlideValueRectangleF((SlideValue)value);
          this.owner.Invalidate();
        }
      }

      #endregion

    }

    /// <summary>
    /// 滑块选项
    /// </summary>
    [Description("滑块选项")]
    public class SlideValue
    {
      public SlideValue()
      {

      }

      public SlideValue(SlideBarExt owner)
      {
        this.owner = owner;
      }

      /// <summary>
      /// 控件主体
      /// </summary>
      public SlideBarExt owner;
      /// <summary>
      /// 当前滑块选项
      /// </summary>
      public SlideValue self;

      private RectangleF rectf = new RectangleF();
      /// <summary>
      /// 滑块rectf
      /// </summary>
      [Browsable(false)]
      [Description("滑块rectf")]
      public RectangleF RectF
      {
        get { return this.rectf; }
        set
        {
          if (this.rectf == value)
            return;
          this.rectf = value;
        }
      }

      private Rectangle tiprect = new Rectangle();
      /// <summary>
      /// 滑块提示信息rect
      /// </summary>
      [Browsable(false)]
      [Description("滑块提示信息rect")]
      public Rectangle TipRect
      {
        get { return this.tiprect; }
        set
        {
          if (this.tiprect == value)
            return;
          this.tiprect = value;
        }
      }


      private PointF slidepoint = new Point();
      /// <summary>
      /// 连线滑块的坐标
      /// </summary>
      [Browsable(false)]
      [Description("连线滑块的坐标")]
      public PointF Slidepoint
      {
        get { return this.slidepoint; }
        set
        {
          if (this.slidepoint == value)
            return;
          this.slidepoint = value;
        }
      }

      private PointF tippoint = new Point();
      /// <summary>
      /// 连线提示信息的坐标
      /// </summary>
      [Browsable(false)]
      [Description("连线提示信息的坐标")]
      public PointF Tippoint
      {
        get { return this.tippoint; }
        set
        {
          if (this.tippoint == value)
            return;
          this.tippoint = value;
        }
      }


      private float value { get; set; }
      /// <summary>
      /// 滑块值
      /// </summary>
      [Browsable(true)]
      [DefaultValue("0f")]
      [Description("滑块值")]
      public float Value
      {
        get { return this.value; }
        set
        {
          if (this.value == value)
            return;

          if (this.owner != null)
          {
            if (value < this.owner.MinValue)
              value = this.owner.MinValue;
            if (value > this.owner.MaxValue)
              value = this.owner.MaxValue;

            int index = this.owner.Items.IndexOf(self);
            if (this.owner.Items.Count > 1)
            {
              if (value > this.value)//往右移动
              {
                if (index < this.owner.Items.Count - 1)
                {
                  if (value > this.owner.Items[index + 1].Value)
                  {
                    value = this.owner.Items[index + 1].Value;
                  }
                }
              }
              else if (value < this.value)
              {
                if (index > 0)
                {
                  if (value < this.owner.Items[index - 1].Value)
                  {
                    value = this.owner.Items[index - 1].Value;
                  }
                }
              }
            }

            this.value = value;

            this.owner.InitializeSlideValueRectangleF(self);
            this.owner.Invalidate();

            if (this.owner.slideValueChanged != null)
            {
              this.owner.slideValueChanged(this.owner, new SlideValueEventArgs() { Item = self });
            }
          }
        }
      }

      private Color slideColor = Color.Empty;
      /// <summary>
      /// 滑块颜色
      /// </summary>
      [Browsable(true)]
      [DefaultValue(typeof(Color), "Empty")]
      [Description("滑块颜色")]
      [Editor(typeof(ColorEditorExt), typeof(System.Drawing.Design.UITypeEditor))]
      public Color SlideColor
      {
        get { return this.slideColor; }
        set
        {
          if (this.slideColor == value)
            return;
          this.slideColor = value;
          if (this.owner != null)
          {
            this.owner.Invalidate();
          }
        }
      }

    }

    /// <summary>
    /// 滑块选项值更改事件参数
    /// </summary>
    [Description("滑块选项值更改事件参数")]
    public class SlideValueEventArgs : EventArgs
    {
      /// <summary>
      /// 滑块选项
      /// </summary>
      [Description("滑块选项")]
      public SlideValue Item { get; set; }
    }

    /// <summary>
    /// 鼠标状态
    /// </summary>
    [Description("鼠标状态")]
    private enum MoveStatus
    {
      /// <summary>
      /// 鼠标进入
      /// </summary>
      Enter,
      /// <summary>
      /// 鼠标离开
      /// </summary>
      Leave,
      /// <summary>
      /// 鼠标按下
      /// </summary>
      Down,
      /// <summary>
      /// 鼠标按下释放
      /// </summary>
      Up
    }

    /// <summary>
    /// 控件滑块方向位置
    /// </summary>
    [Description("控件滑块方向位置")]
    public enum SlideOrientation
    {
      /// <summary>
      ///水平放置靠近上边
      /// </summary>
      HorizontalTop,
      /// <summary>
      /// 水平放置靠近下边
      /// </summary>
      HorizontalBottom,
      /// <summary>
      /// 垂直放置靠近左边
      /// </summary>
      VerticalLeft,
      /// <summary>
      /// 垂直放置靠近右边
      /// </summary>
      VerticalRight,
    }
  }

源码下载:多点滑块进度控件.zip

多点滑块进度控件----------WinForm控件开发系列

多点滑块进度控件----------WinForm控件开发系列

原文地址:https://www.cnblogs.com/tlmbem/p/11410163.html