FlexLabel——控制伸缩面板的标签

前    注:

这是自己平时根据自己需要写的一些小代码,未必对各看官有用。另外,这是根据个人想法而写,未必严谨和符合设计原则,若有任何不妥之处,还请不吝赐教。

说    明:

在豆瓣和其它一些网站陆续看到这样的UI效果:单击一篇文章的标题,在下边展开一个面板,显示文章的内容;再次单击则将文章内容面板收缩。

此控件是仿此效果的一个WinForm的实现。

此控件显示为了一个水平的标签条,左边可显示文本,右边显示一个向上或向下的箭头(描述当然相关面板处于展开还是收缩状态)。将鼠标置于控件上时,控件背景将高亮。

使用时,将要展开和收缩的面板关联到此控件并设置好其它相关属性即可。在此控件上单击鼠标时,此控件将通过设置目标控件的高度来达到展开或收缩的效果,展开或收缩的过程应用了阻尼效果。

设计要点&使用说明:

1、Target属性描述此控件关联的面板,IsSpread描述目标面板当前处于展开还是收缩状态。

2、SizeSpread和SizeShrink分别描述目标控件展开或收缩时的尺寸(高度)。

3、阻尼效果指目标控件的尺寸呈正弦曲线形变化。TickCount和TickPause用于控制阻尼效果,TickCount用于控制变化过程分几步完成,TickPause用于控制每两次变化之间的时间间隔。TickCount越大,变化过程越精细;TickCount越小、TickPause越小,收缩或展开的过程越快。

4、Flexing事件发生于IsSpread即将发生变化时,Flexed事件发生于IsSpread变化完成之后。

5、此控件的展示效果比较丑(因为是自己绘制的,在这方面,我没有天赋),如果哪位能够实现更好的效果,还请不吝赐教。

源代码  :

代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace CommonLibrary.ExtendedControl
{
    
/// <summary>
    
/// 带有伸缩功能的一个标签
    
/// </summary>
    public partial class FlexLabel : Control
    {
        
#region 数据

        
#region 控件绘制相关

        
/// <summary>
        
/// 是否高亮
        
/// </summary>
        private bool _IsHighLight = false;

        
/// <summary>
        
/// 是否处于伸展状态
        
/// </summary>
        private bool _IsSpread = false;

        
/// <summary>
        
/// 状态变化:此变量用于防止使用者在Flexing事件响应中修改IsSpread属性
        
/// </summary>
        private bool StateChanging = false;

        
/// <summary>
        
/// 高亮区域的边界路径
        
/// </summary>
        System.Drawing.Drawing2D.GraphicsPath HighLightRegionPath;

        
/// <summary>
        
/// 高亮颜色
        
/// </summary>
        Color _HighLightColor;

        
/// <summary>
        
/// 隐藏时的颜色
        
/// </summary>
        Color _HideColor;

        
/// <summary>
        
/// 用于填充高亮区域的刷子
        
/// </summary>
        Brush HighLightBrush;

        
/// <summary>
        
/// 用于清空高亮区域的刷子
        
/// </summary>
        Brush HideBrush;

        
/// <summary>
        
/// 文本画刷
        
/// </summary> 
        Brush TextBrush;

        
/// <summary>
        
/// 描述信息
        
/// </summary>
        string _Description;

        
private int _LabelStyle = 0;

        
#endregion

        
#region 关联控件数据与伸缩效果

        
/// <summary>
        
/// 目标
        
/// </summary>
        Control _Target;

        
/// <summary>
        
/// 收缩时的尺寸
        
/// </summary>
        int _SizeShrink = 10;

        
/// <summary>
        
/// 伸展时的尺寸
        
/// </summary>
        int _SizeSpread = 10;

        
/// <summary>
        
/// 阻尼效果的间隔角度
        
/// </summary>
        double RadianTick = 5 * (Math.PI / 180);

        
/// <summary>
        
/// 指示伸缩过程分为多个部分完成
        
/// </summary>
        int _TickCount = 10;

        
/// <summary>
        
/// 指定两个部分伸缩动作间的时间间隔,以毫秒为单位
        
/// </summary>
        int _TickPause = 50;

        
#endregion

        CancelEventArgs FlexingArgs;

        
#endregion

        
#region 属性

        
/// <summary>
        
/// 是否处于伸展状态
        
/// </summary>
        public bool IsSpread
        {
            
get { return _IsSpread; }
            
set
            {
                
if (_IsSpread == value || StateChanging) return;

                StateChanging 
= true;

                FlexingArgs.Cancel 
= false;
                
if (_Flexing != null) _Flexing(this, FlexingArgs);

                
if (FlexingArgs.Cancel)
                {
                    StateChanging 
= false;
                    
return;
                }

                _IsSpread 
= value;

                FlexTarget();

                
this.Refresh();

                StateChanging 
= false;

                
if (_Flexed != null) _Flexed(this, System.EventArgs.Empty);
            }
        }

        
#region 控件绘制相关

        
/// <summary>
        
/// 是否高亮
        
/// </summary>
        private bool IsHighLight
        {
            
get { return _IsHighLight; }
            
set
            {
                
if (_IsHighLight == value) return;
                _IsHighLight 
= value;
                
this.Refresh();
            }
        }

        
/// <summary>
        
/// 高亮颜色
        
/// </summary>
        public Color HighLightColor
        {
            
get { return _HighLightColor; }
            
set
            {
                
if (_HighLightColor == value) return;

                _HighLightColor 
= value;
                HighLightBrush 
= new SolidBrush(_HighLightColor);
            }
        }

        
/// <summary>
        
/// 隐藏颜色
        
/// </summary>
        public Color HideColor
        {
            
get { return _HideColor; }
            
set
            {
                
if (_HideColor == value) return;

                _HideColor 
= value;
                HideBrush 
= new SolidBrush(_HideColor);
            }
        }

        
/// <summary>
        
/// 显示的提示信息
        
/// </summary>
        [Browsable(true)]
        
public override string Text
        {
            
get
            {
                
return base.Text;
            }
            
set
            {
                
base.Text = value;
            }
        }

        
/// <summary>
        
/// 标签样式
        
/// </summary>
        public int LabelStyle
        {
            
get { return _LabelStyle; }
            
set { _LabelStyle = value; }
        }

        
#endregion

        
#region 关联控件与伸缩效果

        
/// <summary>
        
/// 关联的控件
        
/// </summary>
        public Control Target
        {
            
get { return _Target; }
            
set { _Target = value; }
        }

        
/// <summary>
        
/// 朝向
        
/// </summary>
        public Orientation Orientation
        {
            
get { return Orientation.Vertical; }
            
set { }
        }

        
/// <summary>
        
/// 收缩时的尺寸
        
/// </summary>
        public int SizeShrink
        {
            
get { return _SizeShrink; }
            
set
            {
                
if (value < 0return;
                _SizeShrink 
= value;
            }
        }

        
/// <summary>
        
/// 伸展时的尺寸
        
/// </summary>
        public int SizeSpread
        {
            
get { return _SizeSpread; }
            
set
            {
                
if (value < _SizeShrink) return;
                _SizeSpread 
= value;
            }
        }

        
/// <summary>
        
/// 此值用于指定将伸缩过程分为多少次完成。此值越大,则伸缩过程的平滑效果越好,但代价也越大。推荐在8至20之间。
        
/// </summary>
        public int TickCount
        {
            
get { return _TickCount; }
            
set
            {
                
if (_TickCount < 1)
                {
                    _TickCount 
= 1;
                }
                
else if (_TickCount > 30)
                {
                    TickCount 
= 30;
                }
                
else
                {
                    _TickCount 
= value;
                }

                RadianTick 
= Math.PI / (_TickCount * 2);
            }
        }

        
/// <summary>
        
/// 指定两个部分伸缩动作间的时间间隔,以毫秒为单位
        
/// </summary>
        public int TickPause
        {
            
get { return _TickPause; }
            
set
            {
                
if (_TickPause < 0)
                {
                    _TickPause 
= 0;
                }
                
else
                {
                    _TickPause 
= value;
                }
            }
        }

        
#endregion

        
#endregion

        
#region 事件

        
/// <summary>
        
/// 伸缩事件
        
/// </summary>
        private event CancelEventHandler _Flexing;

        
/// <summary>
        
/// 伸缩事件
        
/// </summary>
        public event CancelEventHandler Flexing
        {
            add
            {
                _Flexing 
+= value;
            }
            remove
            {
                _Flexing 
-= value;
            }
        }

        
/// <summary>
        
/// 伸缩发生后事件
        
/// </summary>
        private event EventHandler _Flexed;

        
/// <summary>
        
/// 伸缩发生后事件
        
/// </summary>
        public event EventHandler Flexed
        {
            add
            {
                _Flexed 
+= value;
            }
            remove
            {
                _Flexed 
-= value;
            }
        }

        
#endregion

        
#region 方法

        
public FlexLabel()
        {
            InitializeComponent();

            
#region 画笔

            
//Graphics = this.CreateGraphics();

            HighLightRegionPath 
= GetLRRoundRegionPath(this.ClientRectangle);

            HighLightColor 
= Color.FromKnownColor(KnownColor.Control);

            HideColor 
= Color.FromKnownColor(KnownColor.Window);

            TextBrush 
= new SolidBrush(Color.FromKnownColor(KnownColor.Black));

            
#endregion

            
this.Padding = new Padding(33203);
            FlexingArgs 
= new CancelEventArgs(false);
        }

        
/// <summary>
        
/// 重置伸缩状态
        
/// </summary>
        
/// <param name="IsSpread"></param>
        public void ResetFlex(bool IsSpread)
        {
            _IsSpread 
= IsSpread;
            
this.Refresh();
        }

        
protected override void OnPaint(PaintEventArgs pe)
        {
            
// TODO: Add custom paint code here

            
// Calling the base class OnPaint
            base.OnPaint(pe);

            
float YPos = Math.Max(0, (this.Height - 10)) / 2;

            
if (HighLightRegionPath == nullreturn;

            pe.Graphics.FillRectangle(HideBrush, 
this.ClientRectangle);
            
if (IsHighLight) pe.Graphics.FillPath(HighLightBrush, HighLightRegionPath);

            pe.Graphics.DrawString(
this.Text, this.Font, this.TextBrush, this.Padding.Left, YPos);

            
string FlexTag = (IsSpread) ? "" : "";

            pe.Graphics.DrawString(FlexTag, 
this.Font, this.TextBrush, (float)(this.Width - this.Padding.Right), YPos);
        }

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

            Console.WriteLine(
"SizeChanged");

            HighLightRegionPath 
= GetLRRoundRegionPath(this.ClientRectangle);
            
//Graphics = this.CreateGraphics();
        }

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

            IsHighLight 
= true;

            
//Graphics.FillPath(HighLightBrush, HighLightRegionPath);
            
//Graphics.DrawString(Description, this.Font, this.TextBrush, 8f, 3f);
        }

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

            IsHighLight 
= false;

            
//Graphics.FillPath(HideBrush, HighLightRegionPath);
            
//Graphics.DrawString(Description, this.Font, this.TextBrush, 8f, 3f);
        }

        
protected override void OnClick(EventArgs e)
        {
            
base.OnClick(e);
            IsSpread 
= !IsSpread;
        }

        
/// <summary>
        
/// 获取一个左右两端为弧形的区域
        
/// </summary>
        
/// <param name="Rect">包含区域的矩形</param>
        
/// <returns></returns>
        public System.Drawing.Drawing2D.GraphicsPath GetLRRoundRegionPath(Rectangle Rect)
        {
            System.Drawing.Drawing2D.GraphicsPath Path 
= null;
            Rectangle ArcRect;

            
try
            {
                Path 
= new System.Drawing.Drawing2D.GraphicsPath();

                
switch (LabelStyle)
                {
                    
case 0:
                        
#region 两端圆滑

                        ArcRect 
= new Rectangle(Rect.Location, new Size(Rect.Height, Rect.Height));

                        
//左边
                        Path.AddArc(ArcRect, 90180);

                        
//右边
                        ArcRect.X = Rect.X + Rect.Width - Rect.Height;
                        Path.AddArc(ArcRect, 
270180);

                        
break;
                        
#endregion

                    
case 1:
                        
#region 四角圆滑

                        ArcRect 
= new Rectangle(Rect.Location, new Size(88));

                        
//左上角
                        Path.AddArc(ArcRect, 18090);


                        ArcRect.X 
= Rect.Width - 9;
                        
//右上角
                        Path.AddArc(ArcRect, 27090);

                        ArcRect.Y 
= Rect.Height - 9;
                        
//右下角
                        Path.AddArc(ArcRect, 090);

                        ArcRect.X 
= 0;
                        
//左下角
                        Path.AddArc(ArcRect, 9090);

                        
break;

                        
#endregion

                    
case 2:
                        
#region 四角平滑

                        Path.AddLine(
0220);
                        Path.AddLine(Rect.Width 
- 20, Rect.Width, 2);
                        Path.AddLine(Rect.Width, Rect.Height 
- 2, Rect.Width - 2, Rect.Height);
                        Path.AddLine(
2, Rect.Height, 0, Rect.Height - 2);

                        
break;
                        
#endregion

                    
default:
                        
break;
                }

                
//闭合
                Path.CloseFigure();
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            
return Path;
        }

        
/// <summary>
        
/// 伸缩目标控件
        
/// </summary>
        public void FlexTarget()
        {
            
if (Target == null || SizeShrink == SizeSpread) return;

            
int Distance = SizeSpread - SizeShrink;
            
double Radian = 0;
            
double RadianTick = this.IsSpread ? this.RadianTick : -this.RadianTick;
            
int SizeInit = this.IsSpread ? this.SizeShrink : this.SizeSpread;

            
for (int i = 0; i < _TickCount; i++)
            {
                Radian 
+= RadianTick;

                Target.Height 
= SizeInit + (int)(Distance * Math.Sin(Radian));

                Target.Update();

                System.Threading.Thread.Sleep(_TickPause);
            }
        }

        
#endregion
    }
}


原文地址:https://www.cnblogs.com/yedaoq/p/1712336.html