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  <   0 return ;
                _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( 3 3 20 3 );
            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  ==   null return ;

            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,  90 180 );

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

                        
break ;
                        
#endregion

                    
case   1 :
                        
#region  四角圆滑

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

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


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

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

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

                        
break ;

                        
#endregion

                    
case   2 :
                        
#region  四角平滑

                        Path.AddLine(
0 2 2 0 );
                        Path.AddLine(Rect.Width 
-   2 0 , 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
    }
}


 

 

你可能感兴趣的:(label)