GDI不规则圆弧菜单控件----------WinForm控件开发系列

Posted tlmbem

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GDI不规则圆弧菜单控件----------WinForm控件开发系列相关的知识,希望对你有一定的参考价值。

技术图片

  /// <summary>
  /// 圆弧菜单控件
  /// </summary>
  [ToolboxItem(true)]
  [DefaultProperty("Items")]
  [DefaultEvent("RadianMenuItemClick")]
  [Description("圆弧菜单控件")]
  public partial class RadianMenuExt : Control
  
    public delegate void EventHandler(object sender, RadianMenuItemEventArgs e);

    private event EventHandler radianMenuItemClick;
    /// <summary>
    /// 鱼眼菜单单击事件
    /// </summary>
    [Description("鱼眼菜单单击事件")]
    public event EventHandler RadianMenuItemClick
    
      add  this.radianMenuItemClick += value; 
      remove  this.radianMenuItemClick -= value; 
    

    #region

    private int circleRadius = 50;
    /// <summary>
    /// 圆半径
    /// </summary>
    [DefaultValue(50)]
    [Description("圆半径(默认50)")]
    public int CircleRadius
    
      get  return this.circleRadius; 
      set
      
        if (this.circleRadius == value || value < 0)
          return;
        this.circleRadius = value;
        this.InitializeRadian();
        this.Invalidate();
      
    

    private int radianOpacity = 150;
    /// <summary>
    /// 圆弧透明度
    /// </summary>
    [DefaultValue(150)]
    [Description("圆弧透明度(默认150)")]
    public int RadianOpacity
    
      get  return this.radianOpacity; 
      set
      
        if (this.radianOpacity == value || this.radianOpacity < 0 || this.radianOpacity > 255)
          return;
        this.radianOpacity = value;
        this.Invalidate();
      
    

    private int radianTextOpacity = 220;
    /// <summary>
    /// 圆弧文字透明度
    /// </summary>
    [DefaultValue(220)]
    [Description("圆弧文字透明度(默认220)")]
    public int RadianTextOpacity
    
      get  return this.radianTextOpacity; 
      set
      
        if (this.radianTextOpacity == value || this.radianTextOpacity < 0 || this.radianTextOpacity > 255)
          return;
        this.radianTextOpacity = value;
        this.Invalidate();
      
    

    private int radianWidth = 70;
    /// <summary>
    /// 圆弧宽度
    /// </summary>
    [DefaultValue(70)]
    [Description("圆弧宽度(默认70)")]
    public int RadianWidth
    
      get  return this.radianWidth; 
      set
      
        if (this.radianWidth == value || value < 0)
          return;
        this.radianWidth = value;
        this.InitializeRadian();
        this.Invalidate();
      
    

    private int radianWidthLargen = 20;
    /// <summary>
    /// 圆弧宽度放大值
    /// </summary>
    [DefaultValue(20)]
    [Description("圆弧宽度放大值(默认20)")]
    public int RadianWidthLargen
    
      get  return this.radianWidthLargen; 
      set
      
        if (this.radianWidthLargen == value || value < 0)
          return;
        this.radianWidthLargen = value;
        this.InitializeRadian();
        this.Invalidate();
      
    

    private int radianLargenTime = 350;
    /// <summary>
    /// 圆弧放大缩小动画播放的总时间
    /// </summary>
    [DefaultValue(350)]
    [Description("圆弧放大缩小动画播放的总时间(默认350毫秒)")]
    public int RadianLargenTime
    
      get
      
        return this.radianLargenTime;
      
      set
      
        if (this.radianLargenTime == value || value < 0)
          return;
        this.radianLargenTime = value;
        for (int i = 0; i < this.Items.Count; i++)
        
          this.Items[i].Animation.Options.AllTime = value;
        
      
    

    private bool radianRotate = false;
    /// <summary>
    /// 圆弧是否旋转动画
    /// </summary>
    [DefaultValue(false)]
    [Description("圆弧旋转动画(默认false)")]
    public bool RadianRotate
    
      get  return this.radianRotate; 
      set
      
        if (this.radianRotate == value)
          return;
        this.radianRotate = value;
        if (value)
          this.InitializeRadianAnimation();
        else
          this.UnInitializeRadianAnimation();
      
    

    private int radianRotateTime = 1000;
    /// <summary>
    /// 圆弧旋转动画播放的总时间
    /// </summary>
    [DefaultValue(1000)]
    [Description("圆弧动画播放的总时间(默认1000毫秒)")]
    public int RadianRotateTime
    
      get  return this.radianRotateTime; 
      set
      
        if (this.radianRotateTime == value)
          return;
        this.radianRotateTime = value;
        for (int i = 0; i < this.Items.Count; i++)
        
          this.Items[i].RadianAnimation.Options.AllTime = value;
        
      
    

    private RadianMenuItemCollection radianMenuItemCollection;
    /// <summary>
    /// 圆弧选项配置列表
    /// </summary>
    [Description("圆弧选项配置列表")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public RadianMenuItemCollection Items
    
      get
      
        if (this.radianMenuItemCollection == null)
          this.radianMenuItemCollection = new RadianMenuItemCollection(this);
        return this.radianMenuItemCollection;
      
    

    protected override Size DefaultSize
    
      get
      
        return new Size(450, 450);
      
    

    /// <summary>
    /// 选中圆弧选项
    /// </summary>
    private RadianMenuItem selectedItem = null;

    #endregion

    public RadianMenuExt()
    
      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.TextRenderingHint = TextRenderingHint.AntiAlias;

      for (int i = 0; i < this.Items.Count; i++)
      
        SolidBrush radian_sb = new SolidBrush(Color.FromArgb(this.RadianOpacity, this.Items[i].RadianColor));
        g.FillRegion(radian_sb, this.Items[i].RadianRegion);
        radian_sb.Dispose();

        if (!String.IsNullOrWhiteSpace(Items[i].Text))//文本
        
          SolidBrush text_sb = new SolidBrush(Color.FromArgb(this.RadianTextOpacity, this.Items[i].TextColor));
          SizeF str_size = g.MeasureString(this.Items[i].Text, this.Items[i].TextFont);
          if (i == 0)//圆心
          
            str_size = new SizeF(str_size.Width + 2f, str_size.Height + 2f);
            g.DrawString(this.Items[i].Text, this.Items[i].TextFont, text_sb, new RectangleF(this.Items[i].RadianNowRectF.X + (this.Items[i].RadianNowRectF.Width - str_size.Width) / 2, this.Items[i].RadianNowRectF.Y + (this.Items[i].RadianNowRectF.Height - str_size.Height) / 2, str_size.Width, str_size.Height));

          
          else//圆弧
          
            SizeF ds = new System.Drawing.SizeF(this.Items[i].RadianNowRectF.Width - (this.Items[i].RadianMaxRectF.Width - this.Items[i].RadianNormalRectF.Width) / 2, this.Items[i].RadianNowRectF.Height - (this.Items[i].RadianMaxRectF.Height - this.Items[i].RadianNormalRectF.Height) / 2);

            double degrees = 1 / ((2 * Math.PI * this.Items[i].RadianNowRectF.Width / 2) / 360);//一度弧长
            double angle = degrees * ((str_size.Width / this.Items[i].Text.Length) + 4);//一弧长角度

            g.TranslateTransform(this.Width / 2, this.Height / 2);//更改起始坐标
            g.RotateTransform(90 + this.Items[i].RadianNowStartAngle);//更改起始角度

            for (int j = 0; j < this.Items[i].Text.Length; j++)
            
              g.RotateTransform((float)angle);
              g.DrawString(this.Items[i].Text[j].ToString(), this.Items[i].TextFont, text_sb, 0, -this.Items[i].RadianNowRectF.Height / 2 + str_size.Height / 2);
            

            g.RotateTransform((float)((this.Items[i].Text.Length * -angle)));
            g.RotateTransform(-(90 + this.Items[i].RadianNowStartAngle));
            g.TranslateTransform(-this.Width / 2, -this.Height / 2);//更改起始坐标
          
          text_sb.Dispose();
        
      
    

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

      this.InitializeRadian();
    

    protected override void OnClick(EventArgs e)
    
      base.OnClick(e);
      if (this.selectedItem != null && this.radianMenuItemClick != null)
      
        this.radianMenuItemClick(this, new RadianMenuItemEventArgs()  Item = this.selectedItem );
      
    

    /// <summary>
    /// 鼠标进入离开
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected override void OnMouseMove(MouseEventArgs e)
    
      base.OnMouseMove(e);

      for (int i = 0; i < this.Items.Count; i++)
      
        if (this.Items[i].RadianRegion.IsVisible(e.Location))//鼠标进入
        
          if (this.Items[i].RadianMoveState == MoveState.Leave)
          
            this.selectedItem = this.Items[i];
            this.Items[i].RadianMoveState = MoveState.EnterAnimation;

            this.Items[i].Animation.AT = AnimationType.ElasticOut;
            this.Items[i].Animation.Options.AllTime = this.RadianLargenTime;
            this.Items[i].Animation.Start(true, this.Items[i].Animation.UsedTime);
          
        
        else//鼠标离开
        
          if (this.Items[i].RadianMoveState == MoveState.Enter || this.Items[i].RadianMoveState == MoveState.EnterAnimation)
          
            if (this.selectedItem != null && this.selectedItem.Equals(this.Items[i]))
              this.selectedItem = null;
            this.Items[i].RadianMoveState = MoveState.LeaveAnimation;

            this.Items[i].Animation.AT = AnimationType.BackIn;
            this.Items[i].Animation.Options.AllTime = this.RadianLargenTime;
            this.Items[i].Animation.Start(false, this.Items[i].Animation.UsedTime);
          
        
      
    
    /// <summary>
    /// 鼠标离开可视区
    /// </summary>
    /// <param name="e"></param>
    protected override void OnMouseLeave(EventArgs e)
    
      base.OnMouseLeave(e);
      this.selectedItem = null;
    

    /// <summary>
    /// 缩放动画进行中
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Animation_AnimationIng(object sender, AnimationEventArgs e)
    
      RadianMenuItem rmi = (RadianMenuItem)e.Data;
      float w = (float)(rmi.RadianNormalRectF.Width + (rmi.RadianMaxRectF.Width - rmi.RadianNormalRectF.Width) * e.progressTime);
      float h = (float)(rmi.RadianNormalRectF.Height + (rmi.RadianMaxRectF.Height - rmi.RadianNormalRectF.Height) * e.progressTime);
      float x = (this.ClientRectangle.Width - w) / 2;
      float y = (this.ClientRectangle.Height - h) / 2;
      rmi.RadianNowRectF = new RectangleF(x, y, w, h);
      float v = (float)((rmi.RadianMaxWidth - rmi.RadianNormalWidth) * e.progressTime);
      rmi.RadianNowWidth = (int)(rmi.RadianNormalWidth + v);

      int index = this.Items.IndexOf(rmi);
      if (index == 0)
      
        GraphicsPath gp1 = new GraphicsPath();
        gp1.AddEllipse(rmi.RadianNowRectF);
        Region r = new Region(gp1);
        rmi.RadianRegion = r;
        gp1.Dispose();
      
      else
      
        GraphicsPath gp1 = new GraphicsPath();
        GraphicsPath gp2 = new GraphicsPath();
        gp1.AddPie(rmi.RadianNowRectF.X, rmi.RadianNowRectF.Y, rmi.RadianNowRectF.Width, rmi.RadianNowRectF.Height, rmi.RadianNowStartAngle, rmi.RadianSweepAngle);
        gp2.AddPie(rmi.RadianNowRectF.X + rmi.RadianNowWidth / 2, rmi.RadianNowRectF.Y + rmi.RadianNowWidth / 2, rmi.RadianNowRectF.Width - rmi.RadianNowWidth, rmi.RadianNowRectF.Height - rmi.RadianNowWidth, 0f, 360f);
        Region r = new Region(gp1);
        r.Exclude(gp2);
        rmi.RadianRegion = r;
        gp1.Dispose();
        gp2.Dispose();
      

      this.Invalidate();
    
    /// <summary>
    /// 缩放动画结束
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Animation_AnimationEnding(object sender, AnimationEventArgs e)
    
      RadianMenuItem rmi = (RadianMenuItem)e.Data;
      if (rmi.RadianMoveState == MoveState.LeaveAnimation)
      
        rmi.RadianMoveState = MoveState.Leave;
      
      else if (rmi.RadianMoveState == MoveState.EnterAnimation)
      
        rmi.RadianMoveState = MoveState.Enter;
      
    

    /// <summary>
    /// 旋转动画进行中
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void RadianAnimation_AnimationIng(object sender, AnimationEventArgs e)
    
      RadianMenuItem rmi = (RadianMenuItem)e.Data;
      if (rmi.RadianMoveState == MoveState.Leave)
      
        float original = 0f;
        if (rmi.RadianRotateValue < 0)//旋转方向
        
          original = rmi.RadianAnimation.Options.Transform < 0 ? rmi.RadianStartAngle : (rmi.RadianStartAngle + rmi.RadianRotateValue);
        
        else
        
          original = rmi.RadianAnimation.Options.Transform > 0 ? rmi.RadianStartAngle : (rmi.RadianStartAngle + rmi.RadianRotateValue);
        
        rmi.RadianNowStartAngle = (int)(original + e.Transform * e.progressTime);


        GraphicsPath gp1 = new GraphicsPath();
        GraphicsPath gp2 = new GraphicsPath();
        gp1.AddPie(rmi.RadianNormalRectF.X, rmi.RadianNormalRectF.Y, rmi.RadianNormalRectF.Width, rmi.RadianNormalRectF.Height, rmi.RadianNowStartAngle, rmi.RadianSweepAngle);
        gp2.AddPie(rmi.RadianNormalRectF.X + rmi.RadianNormalWidth / 2, rmi.RadianNormalRectF.Y + rmi.RadianNormalWidth / 2, rmi.RadianNormalRectF.Width - rmi.RadianNormalWidth, rmi.RadianNormalRectF.Height - rmi.RadianNormalWidth, 0f, 360f);
        Region r = new Region(gp1);
        r.Exclude(gp2);
        rmi.RadianRegion = r;
        gp1.Dispose();
        gp2.Dispose();

        this.Invalidate();
      
    
    /// <summary>
    /// 旋转动画间隔重复事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void RadianAnimation_AnimationRepetitioning(object sender, AnimationEventArgs e)
    
      RadianMenuItem rmi = (RadianMenuItem)e.Data;
      rmi.RadianAnimation.Options.Transform = -1 * rmi.RadianAnimation.Options.Transform;
    

    /// <summary>
    /// 初始化圆弧控件大小位置
    /// </summary>
    private void InitializeRadian()
    
      int diameter = this.CircleRadius * 2;//直径

      for (int i = 0; i < this.Items.Count; i++)
      
        if (i == 0)
        
          float w = diameter;
          float h = diameter;
          float x = (this.ClientRectangle.Width - w) / 2;
          float y = (this.ClientRectangle.Height - h) / 2;

          float max_w = w + this.RadianWidthLargen;
          float max_h = h + this.RadianWidthLargen;
          float max_x = (this.ClientRectangle.Width - max_w) / 2;
          float max_y = (this.ClientRectangle.Height - max_h) / 2;

          this.Items[i].RadianNormalRectF = new RectangleF(x, y, w, h);
          this.Items[i].RadianMaxRectF = new RectangleF(max_x, max_y, max_w, max_h);
          this.Items[i].RadianNowRectF = this.Items[i].RadianNormalRectF;

          GraphicsPath gp1 = new GraphicsPath();
          gp1.AddEllipse(this.Items[i].RadianNormalRectF);
          Region r = new Region(gp1);
          this.Items[i].RadianRegion = r;
          gp1.Dispose();
        
        else
        
          float w = diameter + this.RadianWidth * i;
          float h = diameter + this.RadianWidth * i;
          float x = (this.ClientRectangle.Width - w) / 2;
          float y = (this.ClientRectangle.Height - h) / 2;

          float max_w = w + this.RadianWidthLargen;
          float max_h = h + this.RadianWidthLargen;
          float max_x = (this.ClientRectangle.Width - max_w) / 2;
          float max_y = (this.ClientRectangle.Height - max_h) / 2;

          this.Items[i].RadianNormalRectF = new RectangleF(x, y, w, h);
          this.Items[i].RadianMaxRectF = new RectangleF(max_x, max_y, max_w, max_h);
          this.Items[i].RadianNowRectF = this.Items[i].RadianNormalRectF;

          this.Items[i].RadianNormalWidth = this.RadianWidth;
          this.Items[i].RadianMaxWidth = this.RadianWidth + this.RadianWidthLargen;
          this.Items[i].RadianNowWidth = this.RadianWidth;
          this.Items[i].RadianNowStartAngle = this.Items[i].RadianStartAngle;

          GraphicsPath gp1 = new GraphicsPath();
          GraphicsPath gp2 = new GraphicsPath();
          gp1.AddPie(this.Items[i].RadianNormalRectF.X, this.Items[i].RadianNormalRectF.Y, this.Items[i].RadianNormalRectF.Width, this.Items[i].RadianNormalRectF.Height, this.Items[i].RadianNowStartAngle, this.Items[i].RadianSweepAngle);
          gp2.AddPie(this.Items[i].RadianNormalRectF.X + this.Items[i].RadianNormalWidth / 2, this.Items[i].RadianNormalRectF.Y + this.Items[i].RadianNormalWidth / 2, this.Items[i].RadianNormalRectF.Width - this.Items[i].RadianNormalWidth, this.Items[i].RadianNormalRectF.Height - this.Items[i].RadianNormalWidth, 0f, 360f);
          Region r = new Region(gp1);
          r.Exclude(gp2);
          this.Items[i].RadianRegion = r;
          gp1.Dispose();
          gp2.Dispose();
        
        if (this.Items[i].Animation == null)
        
          this.Items[i].Animation = new AnimationTimer(this, new AnimationOptions());
          this.Items[i].Animation.Options.Data = this.Items[i];
          this.Items[i].Animation.AnimationIng += new AnimationTimer.AnimationHandel(this.Animation_AnimationIng);
          this.Items[i].Animation.AnimationEnding += new AnimationTimer.AnimationHandel(this.Animation_AnimationEnding);
        
      
    

    /// <summary>
    /// 加载旋转动画
    /// </summary>
    private void InitializeRadianAnimation()
    
      for (int i = 1; i < this.Items.Count; i++)
      
        if (this.Items[i].RadianRotateValue != 0)
        
          this.Items[i].RadianAnimation = new AnimationTimer(this, new AnimationOptions());
          this.Items[i].RadianAnimation.AnimationIng += new AnimationTimer.AnimationHandel(this.RadianAnimation_AnimationIng);
          this.Items[i].RadianAnimation.AnimationRepetitioning += new AnimationTimer.AnimationHandel(this.RadianAnimation_AnimationRepetitioning);

          this.Items[i].RadianAnimation.Options.AlwaysRepetitionExercise = true;
          this.Items[i].RadianAnimation.Options.Data = this.Items[i];
          this.Items[i].RadianAnimation.Options.Transform = this.Items[i].RadianRotateValue;
          this.Items[i].RadianAnimation.Options.AllTime = this.RadianRotateTime;
          this.Items[i].RadianAnimation.AT = AnimationType.UniformMotion;
          this.Items[i].RadianAnimation.Start(true, 0);
        
      
    

    /// <summary>
    /// 卸载旋转动画
    /// </summary>
    private void UnInitializeRadianAnimation()
    
      for (int i = 1; i < this.Items.Count; i++)
      
        if (this.Items[i].RadianRotateValue != 0)
        
          this.Items[i].RadianAnimation.Dispose();
          this.Items[i].RadianAnimation = null;
        
      
    

    protected override void Dispose(bool disposing)
    
      if (disposing && (components != null))
      
        components.Dispose();
        for (int i = 0; i < this.Items.Count; i++)
        
          if (this.Items[i].Animation != null)
          
            this.Items[i].Animation.Dispose();
          
          if (this.Items[i].RadianAnimation != null)
          
            this.Items[i].RadianAnimation.Dispose();
          
        
      
      base.Dispose(disposing);
    

    /// <summary>
    /// 圆弧选项集合
    /// </summary>
    [Description("圆弧选项集合")]
    [Editor(typeof(CollectionEditorExt), typeof(UITypeEditor))]
    public sealed class RadianMenuItemCollection : IList, ICollection, IEnumerable
    
      private ArrayList radianMenuItemList = new ArrayList();
      private RadianMenuExt owner;

      public RadianMenuItemCollection(RadianMenuExt owner)
      
        this.owner = owner;
      

      #region IEnumerable

      public IEnumerator GetEnumerator()
      
        RadianMenuItem[] listArray = new RadianMenuItem[this.radianMenuItemList.Count];
        for (int index = 0; index < listArray.Length; ++index)
          listArray[index] = (RadianMenuItem)this.radianMenuItemList[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.radianMenuItemList[i], i + index);
      

      public int Count
      
        get
        
          return this.radianMenuItemList.Count;
        
      

      public bool IsSynchronized
      
        get
        
          return false;
        
      

      public object SyncRoot
      
        get
        
          return (object)this;
        
      

      #endregion

      #region IList

      public int Add(object value)
      
        RadianMenuItem radianMenuItem = (RadianMenuItem)value;
        this.radianMenuItemList.Add(radianMenuItem);
        this.owner.InitializeRadian();
        this.owner.Invalidate();
        return this.Count - 1;
      

      public void Clear()
      
        for (int i = 0; i < this.Count; i++)
        
          RadianMenuItem item = (RadianMenuItem)this.radianMenuItemList[i];
          item.Animation.Dispose();
          item.Animation = null;
          item.RadianAnimation.Dispose();
          item.RadianAnimation = null;
          item.RadianRegion.Dispose();
          item.TextFont.Dispose();
        
        this.radianMenuItemList.Clear();
        this.owner.InitializeRadian();
        this.owner.Invalidate();
      

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

      public int IndexOf(object value)
      
        return this.radianMenuItemList.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 RadianMenuItem))
          return;

        RadianMenuItem item = (RadianMenuItem)value;
        item.Animation.Dispose();
        item.Animation = null;
        item.RadianAnimation.Dispose();
        item.RadianAnimation = null;
        item.RadianRegion.Dispose();
        item.TextFont.Dispose();
        this.radianMenuItemList.Remove((RadianMenuItem)value);
        this.owner.InitializeRadian();
        this.owner.Invalidate();
      

      public void RemoveAt(int index)
      
        RadianMenuItem item = (RadianMenuItem)this.radianMenuItemList[index];
        item.Animation.Dispose();
        item.Animation = null;
        item.RadianAnimation.Dispose();
        item.RadianAnimation = null;
        item.RadianRegion.Dispose();
        item.TextFont.Dispose();
        this.radianMenuItemList.RemoveAt(index);
        this.owner.InitializeRadian();
        this.owner.Invalidate();
      

      public RadianMenuItem this[int index]
      
        get
        
          return (RadianMenuItem)this.radianMenuItemList[index];
        
        set
        
          this.radianMenuItemList[index] = (RadianMenuItem)value;
          this.owner.InitializeRadian();
          this.owner.Invalidate();
        
      

      object IList.this[int index]
      
        get
        
          return (object)this.radianMenuItemList[index];
        
        set
        
          this.radianMenuItemList[index] = (RadianMenuItem)value;
          this.owner.InitializeRadian();
          this.owner.Invalidate();
        
      

      #endregion

    

    /// <summary>
    /// 圆弧选项
    /// </summary>
    [Description("圆弧选项")]
    public class RadianMenuItem
    
      private AnimationTimer animation = null;
      /// <summary>
      /// 缩放动画对象
      /// </summary>
      [Browsable(false)]
      [DefaultValue(null)]
      [Description("缩放动画对象")]
      public AnimationTimer Animation
      
        get  return this.animation; 
        set
        
          if (this.animation == value)
            return;
          this.animation = value;
        
      

      private AnimationTimer radianAnimation = null;
      /// <summary>
      /// 旋转动画对象
      /// </summary>
      [Browsable(false)]
      [DefaultValue(null)]
      [Description("旋转动画对象")]
      public AnimationTimer RadianAnimation
      
        get  return this.radianAnimation; 
        set
        
          if (this.radianAnimation == value)
            return;
          this.radianAnimation = value;
        
      

      private RectangleF radianNormalRectF;
      /// <summary>
      /// 圆弧选项默认Rect
      /// </summary>
      [Browsable(false)]
      [Description("圆弧选项默认Rect")]
      public RectangleF RadianNormalRectF
      
        get  return this.radianNormalRectF; 
        set
        
          if (this.radianNormalRectF == value)
            return;
          this.radianNormalRectF = value;
        
      

      private RectangleF radianMaxRectF;
      /// <summary>
      /// 圆弧选项最大Rect
      /// </summary>
      [Browsable(false)]
      [Description("圆弧选项最大Rect")]
      public RectangleF RadianMaxRectF
      
        get  return this.radianMaxRectF; 
        set
        
          if (this.radianMaxRectF == value)
            return;
          this.radianMaxRectF = value;
        
      

      private RectangleF radianNowRectF;
      /// <summary>
      /// 圆弧选项当前Rect
      /// </summary>
      [Browsable(false)]
      [Description("圆弧选项当前Rect")]
      public RectangleF RadianNowRectF
      
        get  return this.radianNowRectF; 
        set
        
          if (this.radianNowRectF == value)
            return;
          this.radianNowRectF = value;
        
      

      private Region radianRegion = new Region();
      /// <summary>
      /// 圆弧选项Region
      /// </summary>
      [Browsable(false)]
      [Description("圆弧选项Region")]
      public Region RadianRegion
      
        get  return this.radianRegion; 
        set
        
          if (this.radianRegion == value)
            return;
          this.radianRegion = value;
        
      

      private MoveState radianMoveState = MoveState.Leave;
      /// <summary>
      /// 圆弧选项鼠标状态
      /// </summary>
      [Browsable(false)]
      [Description("圆弧选项鼠标状态")]
      public MoveState RadianMoveState
      
        get  return this.radianMoveState; 
        set
        
          if (this.radianMoveState == value)
            return;
          this.radianMoveState = value;
        
      

      private string text;
      /// <summary>
      /// 圆弧选项文本
      /// </summary>
      [Browsable(true)]
      [Description("圆弧选项文本")]
      public string Text
      
        get  return this.text; 
        set
        
          if (this.text == value)
            return;
          this.text = value;
        
      

      private Font textFont = new Font("幼圆", 12, FontStyle.Bold);
      /// <summary>
      /// 文本字体
      /// </summary>
      [Browsable(true)]
      [DefaultValue(typeof(Font), "幼圆, 12pt, style=Bold")]
      [Description("文本字体")]
      public Font TextFont
      
        get  return this.textFont; 
        set
        
          if (this.textFont == value)
            return;
          this.textFont = value;
        
      

      private Color textColor = Color.White;
      /// <summary>
      /// 文本字体颜色
      /// </summary>
      [Browsable(true)]
      [DefaultValue(typeof(Color), "White")]
      [Description("文本字体颜色")]
      public Color TextColor
      
        get  return this.textColor; 
        set
        
          if (this.textColor == value)
            return;
          this.textColor = value;
        
      

      private Color radianColor = Color.Empty;
      /// <summary>
      /// 圆弧颜色
      /// </summary>
      [Browsable(true)]
      [DefaultValue(typeof(Color), "Empty")]
      [Description("圆弧颜色")]
      public Color RadianColor
      
        get  return this.radianColor; 
        set
        
          if (this.radianColor == value)
            return;
          this.radianColor = value;
        
      

      private float radianStartAngle = 0f;
      /// <summary>
      ///  圆弧开始角度
      /// </summary>
      [Browsable(true)]
      [DefaultValue(0f)]
      [Description("圆弧开始角度")]
      public float RadianStartAngle
      
        get  return this.radianStartAngle; 
        set
        
          if (this.radianStartAngle == value || value < -360 || value > 360)
            return;
          this.radianStartAngle = value;
        
      

      private float radianSweepAngle = 90f;
      /// <summary>
      /// 圆弧从 RadianStartAngle 参数到圆弧的结束点沿顺时针方向度量的角(以度为单位)。
      /// </summary>
      [Browsable(true)]
      [DefaultValue(90f)]
      [Description("圆弧从 RadianStartAngle 参数到圆弧的结束点沿顺时针方向度量的角(以度为单位)。")]
      public float RadianSweepAngle
      
        get  return this.radianSweepAngle; 
        set
        
          if (this.radianSweepAngle == value || value < -360 || value > 360)
            return;
          this.radianSweepAngle = value;
        
      

      private int radianRotateValue = 0;
      /// <summary>
      /// 圆弧要旋转的角度(-180至180)
      /// </summary>
      [Browsable(true)]
      [DefaultValue(0)]
      [Description("弧度要旋转的角度(-180至180)(默认0)")]
      public int RadianRotateValue
      
        get  return this.radianRotateValue; 
        set
        
          if (this.radianRotateValue == value || value < -180 || value > 180)
            return;
          this.radianRotateValue = value;
        
      

      private float radianNowStartAngle;
      /// <summary>
      /// 当前圆弧开始角度
      /// </summary>
      [Browsable(false)]
      [Description("当前圆弧开始角度")]
      public float RadianNowStartAngle
      
        get  return this.radianNowStartAngle; 
        set
        
          if (this.radianNowStartAngle == value || value < -360 || value > 360)
            return;
          this.radianNowStartAngle = value;
        
      

      private int radianNormalWidth;
      /// <summary>
      /// 圆弧默认宽度
      /// </summary>
      [Browsable(false)]
      [Description("圆弧默认宽度")]
      public int RadianNormalWidth
      
        get  return this.radianNormalWidth; 
        set
        
          if (this.radianNormalWidth == value)
            return;
          this.radianNormalWidth = value;
        
      

      private int radianMaxWidth;
      /// <summary>
      /// 圆弧最大宽度
      /// </summary>
      [Browsable(false)]
      [Description("圆弧最大宽度")]
      public int RadianMaxWidth
      
        get  return this.radianMaxWidth; 
        set
        
          if (this.radianMaxWidth == value)
            return;
          this.radianMaxWidth = value;
        
      

      private int radianNowWidth;
      /// <summary>
      /// 当前圆弧宽度
      /// </summary>
      [Browsable(false)]
      [Description("当前圆弧宽度")]
      public int RadianNowWidth
      
        get  return this.radianNowWidth; 
        set
        
          if (this.radianNowWidth == value)
            return;
          this.radianNowWidth = value;
        
      
    

    /// <summary>
    /// 圆弧选项鼠标状态
    /// </summary>
    [Description("圆弧选项鼠标状态")]
    public enum MoveState
    
      /// <summary>
      /// 光标已离开可视区
      /// </summary>
      Leave,
      /// <summary>
      /// 光标已离开可视区(动画还在进行中)
      /// </summary>
      LeaveAnimation,
      /// <summary>
      /// 光标已进入可视区
      /// </summary>
      Enter,
      /// <summary>
      /// 光标已进入可视区(动画还在进行中)
      /// </summary>
      EnterAnimation
    

  

  /// <summary>
  /// 圆弧选项单击事件参数
  /// </summary>
  [Description("鱼眼菜单单击事件参数")]
  public class RadianMenuItemEventArgs : EventArgs
  
    /// <summary>
    /// 鱼眼菜单选项
    /// </summary>
    [Description("鱼眼菜单选项")]
    public RadianMenuExt.RadianMenuItem Item  get; set; 
  

源码下载:GDI不规则圆弧菜单控件.zip

以上是关于GDI不规则圆弧菜单控件----------WinForm控件开发系列的主要内容,如果未能解决你的问题,请参考以下文章

C# winform 制作圆形窗体

求教:C#中winform 并未使用任何图片,但报错”GDI+ 中发生一般性错误“

FPS游戏:D3D内部游戏菜单实现

Qt编写自定义控件21-圆弧仪表盘

如何使用 Win32/GDI 加载 PNG 图像(如果可能,不要使用 GDI+)?

Python中tkinter中控件的使用(7,Menu顶层菜单栏)