阅读全文:http://www.cckan.net/forum.php?mod=viewthread&tid=258

C#仿QQ皮肤-实现原理系列文章导航
                                                              http://www.cnblogs.com/sufei/archive/2010/03/10/1682847.html

前面的文章说了不少的费话,从这一节开始说点实际点的东西,皮肤的实现原理,我通过下面几步跟大家一点一点的抛吧.

第一步   皮肤的分类,一共有两个大类,我是根据实现的对象不同来分的,感觉这样可能会容易理解一下,因为窗体皮肤和控件皮肤还是有一些不同的,窗体的属性和控件的属性不一至,所 以在实现上有少许的差别,在这里我把他们分成两类;

1.窗体皮肤,

2.控件皮肤

1.窗体皮肤

窗体皮肤不用多说大家都 知道就是窗体的皮肤,当然这里面我还细分为窗体,和用户控件两种,也就是From和UserControl

在这里我们实现以下几个窗体和用户控件

1.         基窗体FormBase与基用户控件FormBase1的实现

2.         基窗体FunctionFormBase的实现

3.         主窗体MainForm和Main的实现

4.         用户控件EnterUserControl的实现

5.         皮肤控件窗体SkinForm的实现

6.        Windows消息提示框窗体MessageBoxForm的实现

7.        常用用户控件EnterFrom1和窗体EntryForm的实现

一个一个的来说明吧,大家先下载一下最新的皮肤 这样方便理解

第一个,基窗体FormBase与基用户控件FormBase1

这 是两个基窗体吧,应该可以这样讲吧,FormBase是所有以上窗体的基窗体,也就是说上面的窗体创建都要继承这个窗体,而FormBase1 是做什么的呢,呵呵 ,其实他跟FormBase的功能 是完全一样的,只有一点区别就是FormBase是窗体,而FormBase1 是用户控件,实现的方法也是一样的

基窗体的实现是很简单的

第二个,基窗体FunctionFormBase的实现

FunctionFormBase这是一个窗体,跟FormBase的功能是一样的,是其它窗体的基窗体,像皮肤控制窗体SkinForm这样的窗体是要继承它的。

任何一个基窗体里都是用来重绘和拦截Windows消息的,做过自定义滚动条的朋友应该都 知道 什么是Windows消息,.net没有提共控件的直接Scroll,我们只能通过拦截Windows消息来处理了,和处理APIHook来解决问题了,Windows消息一般要处理这样几个VSCROLL,WM_HITTEST,WM_NCMOUSEMOVE等

说到滚动条多说几句,在处理滚动条的时候一般都 要处理这样几个方法

1.第一个是Value值

这里有一段参考代码可以分享一下

代码

  public   int  Value
        {
             get  {  return  moValue; }
             set
            {
                moValue  =  value;

int  nTrackHeight  =  ( this .Height  -  (UpArrowImage.Height  +  DownArrowImage.Height));
                 float  fThumbHeight  =  (( float )LargeChange  /  ( float )Maximum)  *  nTrackHeight;
                 int  nThumbHeight  =  ( int )fThumbHeight;

if  (nThumbHeight  >  nTrackHeight)
                {
                    nThumbHeight  =  nTrackHeight;
                    fThumbHeight  =  nTrackHeight;
                }
                 if  (nThumbHeight  <   56 )
                {
                    nThumbHeight  =   56 ;
                    fThumbHeight  =   56 ;
                }

// figure out value
                 int  nPixelRange  =  nTrackHeight  -  nThumbHeight;
                 int  nRealRange  =  (Maximum  -  Minimum)  -  LargeChange;
                 float  fPerc  =   0.0f ;
                 if  (nRealRange  !=   0 )
                {
                    fPerc  =  ( float )moValue  /  ( float )nRealRange;

}

float  fTop  =  fPerc  *  nPixelRange;
                moThumbTop  =  ( int )fTop;

Invalidate();
            }
        }

其它的跟这个基本上差不多,都 是先画个自己的滚动条,然后通过Api把系统的隐藏,把自己的加上,并且让他们产生连动;

用到的Api介绍一下

代码

[DllImport( " user32.dll " )]

[ return : MarshalAs(UnmanagedType.Bool)]

public   static   extern   bool  GetScrollInfo(IntPtr hwnd,  int  fnBar,  ref  SCROLLINFO lpsi);

[DllImport( " user32.dll " )]

public   static   extern   int  SetScrollInfo(IntPtr hwnd,  int  fnBar, [In]  ref  SCROLLINFO

lpsi,  bool  fRedraw);

关于这两个Api的介绍如下

GetScrollInfo

该函数找到滚动条的参数,包括滚动条位置的最小值、最大值,页面大小,滚动按钮的 位置,

函数原型:BOOL GetScrolllnfo(HWND hWnd,int fnBar,LPSCROLLINFO lpsi);

参数:

hWnd:滚动条控制或有标准滚动条的窗体句柄,由fnBar参数确定。

fnBar:指定待找回滚动条参数的类型,此参数可以为如下值,其值含义:

SB_CTL:找回滚动条控制参数。其中参数hwnd一定是处理滚动条控制的句柄。

SB_HORZ:找回所指定窗体的标准水平滚动条参数。

SB_VERT:找回所指定窗体的标准垂直滚动条参数。

lpsi:指向SCROLLINFO结构。在调用Getscrolllofo函数之前,设置SCROLLINFO结构中cbSize成员以标识结构 大小,设置成员fMask以说明待找回的滚动条参数。在运行之前,函数复制结构中适当的成员所指定的参数。

成员fMask可以是如下值:

SIF_PAGE:复制滚动页码到由lpsi指向的SCROLLINFO结构的nPage成员中。

SIF_POS:复制滚动位置到由lpsi指向的SCROLLINFO结构的nPos成员中。

GetScrollInfo-相关资料

SIF_RANGE:复制滚动范围到由lpsi指向的SCROLLINFO结构的nMin和nMax成员中。

SIF_TRACKPOS:复制当前滚动盒跟踪位置到由nTrackPos指向的SCROLLINFO结构的 nPage成员中。

返回值:如果函数找到任何一个值,那么返回值为非零;如果函数没有找到任何值,那么返回值为零;

注意:Getscrolllnfo函数尽管WM_HSCROLL和WM_VSCROLL指出了滚动条位置消息,却仅提供了16位数据,而函数 SetScrollnfo和GetScrollnfo则提供了32位的滚动条数据。因而,当应用程序在处理WM_HSCROLL或 WM_VSCROLL时,要获得32位滚动条位置的数据时, 则要调用Getscrolllnfo函数。     在WM_HSCROLL或WM_VSCROLL消息中SB_THUMBTRACK通告过程中,为了获得32位的滚动盒位置,需要调用 GetScrolllnfo函数以得到结构SCROLLINFO成员fMask中的SCROLLINFO值。函数返回在结构SCROLLINFO成员nTrackPos中指出的滚动盒跟踪位置的值。这将允许当用户移动滚动盒时能得到其位置。

SetScrollInfo

函数功能:该函数设置滚动条参数,包括滚动位置的最大值和最小值,页面大小,滚动按钮的位置。如被请求,函数也可以重画滚动条。

函数原型:int SetScrollInfo(HWND hWnd;int fnBar,LPSCROLLINFO lpsi,BOOL fRedraw);

参数:

hWnd:滚动条控制或带标准滚动条的窗体句柄,由fnBar参数决定。

fnBar:指定被设定参数的滚动条的类型。这个参数可以是下面值,含义如下:

SB_CTL:设置滚动条控制。而参数hwnd必须是滚动条控制的句柄。

SB_HORZ:设置所给定的窗体上标准水平滚动条参数。

SB_VERT:设置所给定的窗体上标准垂直滚动条参数。

IPBI:指向SCROLLINFO结构。在调用SetScrognfo之前,设置 SCROLLINFO结构中cbSize成员以标识结构大小,设置成员fMask以说明待设置的滚动条参数,并且在适当的成员中制定新的参数值。成员 fMask可以为下面所列复合值,含义如下:

SIF_DfSABLENOSCROLL:如果滚动条的新参数使其为没必要,则使滚动条无效而不再移动它。

SIF_PAGE:设置滚动页码值到由Ipsi指向的SCROLLINFO结构的nPage成员中。

SIF_POS:设置滚动位置值到由lpsi指向的SCROLLINFO结构的nPos成员中。

SIF_RANGE:设置滚动范围值到由lpsl指向的SCROLLINFO结构的nMin和nMax成员中。

fRedraw:指定滚动条是否重画以反映滚动条的变化。如果这个参数为TRUE,滚动条将被重画,否则不被重画。

返回值:返回值是滚动盒的当前位置。

注意:SetScrolllnfo函数执行任务是检查SCROLLINFO结构中由成员 nPage和nPos值的范围。成员uPage值必须从0到nMax- nMin+1,成员nPos必须是在nMin和nMax-nMax-max(nPage C1,0)之间的指定值。如果任何一个值超过了这个范围,函数将在指定范围内为它设置一个值。

第二个,是鼠标事件我在这里处理了三个,参考 一下吧

代码

  // 鼠标按下
         private   void  CustomScrollbar_MouseDown( object  sender, MouseEventArgs e)
        {
            Point ptPoint  =   this .PointToClient(Cursor.Position);
             int  nTrackHeight  =  ( this .Height  -  (UpArrowImage.Height  +  DownArrowImage.Height));
             float  fThumbHeight  =  (( float )LargeChange  /  ( float )Maximum)  *  nTrackHeight;
             int  nThumbHeight  =  ( int )fThumbHeight;

if  (nThumbHeight  >  nTrackHeight)
            {
                nThumbHeight  =  nTrackHeight;
                fThumbHeight  =  nTrackHeight;
            }
             if  (nThumbHeight  <   56 )
            {
                nThumbHeight  =   56 ;
                fThumbHeight  =   56 ;
            }

int  nTop  =  moThumbTop;
            nTop  +=  UpArrowImage.Height;

Rectangle thumbrect  =   new  Rectangle( new  Point( 1 , nTop),  new  Size(ThumbMiddleImage.Width, nThumbHeight));
             if  (thumbrect.Contains(ptPoint))
            {

// hit the thumb
                nClickPoint  =  (ptPoint.Y  -  nTop);
                 // MessageBox.Show(Convert.ToString((ptPoint.Y - nTop)));
                 this .moThumbDown  =   true ;
            }

Rectangle uparrowrect  =   new  Rectangle( new  Point( 1 ,  0 ),  new  Size(UpArrowImage.Width, UpArrowImage.Height));
             if  (uparrowrect.Contains(ptPoint))
            {

int  nRealRange  =  (Maximum  -  Minimum)  -  LargeChange;
                 int  nPixelRange  =  (nTrackHeight  -  nThumbHeight);
                 if  (nRealRange  >   0 )
                {
                     if  (nPixelRange  >   0 )
                    {
                         if  ((moThumbTop  -  SmallChange)  <   0 )
                            moThumbTop  =   0 ;
                         else
                            moThumbTop  -=  SmallChange;

// figure out value
                         float  fPerc  =  ( float )moThumbTop  /  ( float )nPixelRange;
                         float  fValue  =  fPerc  *  (Maximum  -  LargeChange);

moValue  =  ( int )fValue;
                        Debug.WriteLine(moValue.ToString());

if  (ValueChanged  !=   null )
                            ValueChanged( this ,  new  EventArgs());

if  (Scroll  !=   null )
                            Scroll( this ,  new  EventArgs());

Invalidate();
                    }
                }
            }

Rectangle downarrowrect  =   new  Rectangle( new  Point( 1 , UpArrowImage.Height  +  nTrackHeight),  new  Size(UpArrowImage.Width, UpArrowImage.Height));
             if  (downarrowrect.Contains(ptPoint))
            {
                 int  nRealRange  =  (Maximum  -  Minimum)  -  LargeChange;
                 int  nPixelRange  =  (nTrackHeight  -  nThumbHeight);
                 if  (nRealRange  >   0 )
                {
                     if  (nPixelRange  >   0 )
                    {
                         if  ((moThumbTop  +  SmallChange)  >  nPixelRange)
                            moThumbTop  =  nPixelRange;
                         else
                            moThumbTop  +=  SmallChange;

// figure out value
                         float  fPerc  =  ( float )moThumbTop  /  ( float )nPixelRange;
                         float  fValue  =  fPerc  *  (Maximum  -  LargeChange);

moValue  =  ( int )fValue;
                        Debug.WriteLine(moValue.ToString());

if  (ValueChanged  !=   null )
                            ValueChanged( this ,  new  EventArgs());

if  (Scroll  !=   null )
                            Scroll( this ,  new  EventArgs());

Invalidate();
                    }
                }
            }
        }

// 鼠标松开
         private   void  CustomScrollbar_MouseUp( object  sender, MouseEventArgs e)
        {
             this .moThumbDown  =   false ;
             this .moThumbDragging  =   false ;
        }

// 鼠标经过
         private   void  CustomScrollbar_MouseMove( object  sender, MouseEventArgs e)
        {
             if  (moThumbDown  ==   true )
            {
                 this .moThumbDragging  =   true ;
            }

if  ( this .moThumbDragging)
            {

MoveThumb(e.Y);
            }

if  (ValueChanged  !=   null )
                ValueChanged( this ,  new  EventArgs());

if  (Scroll  !=   null )
                Scroll( this ,  new  EventArgs());
        }

第三个  就是MoveThumb方法了,看方法体

代码

    private   void  MoveThumb( int  y)
        {
             int  nRealRange  =  Maximum  -  Minimum;
             int  nTrackHeight  =  ( this .Height  -  (UpArrowImage.Height  +  DownArrowImage.Height));
             float  fThumbHeight  =  (( float )LargeChange  /  ( float )Maximum)  *  nTrackHeight;
             int  nThumbHeight  =  ( int )fThumbHeight;

if  (nThumbHeight  >  nTrackHeight)
            {
                nThumbHeight  =  nTrackHeight;
                fThumbHeight  =  nTrackHeight;
            }
             if  (nThumbHeight  <   56 )
            {
                nThumbHeight  =   56 ;
                fThumbHeight  =   56 ;
            }

int  nSpot  =  nClickPoint;

int  nPixelRange  =  (nTrackHeight  -  nThumbHeight);
             if  (moThumbDown  &&  nRealRange  >   0 )
            {
                 if  (nPixelRange  >   0 )
                {
                     int  nNewThumbTop  =  y  -  (UpArrowImage.Height  +  nSpot);

if  (nNewThumbTop  <   0 )
                    {
                        moThumbTop  =  nNewThumbTop  =   0 ;
                    }
                     else   if  (nNewThumbTop  >  nPixelRange)
                    {
                        moThumbTop  =  nNewThumbTop  =  nPixelRange;
                    }
                     else
                    {
                        moThumbTop  =  y  -  (UpArrowImage.Height  +  nSpot);
                    }

// figure out value
                     float  fPerc  =  ( float )moThumbTop  /  ( float )nPixelRange;
                     float  fValue  =  fPerc  *  (Maximum  -  LargeChange);
                    moValue  =  ( int )fValue;
                    Debug.WriteLine(moValue.ToString());

Application.DoEvents();

Invalidate();
                }
            }
        }

滚动条先到这里吧,具体的到这个控件制作时再具体的说,

我们接着看

第三个        主窗体MainForm和Main窗体

见名思意MainForm,主窗体,这是一个主窗体,是一般化的窗体,只有菜单和皮肤两项菜单,不带有换底纹的功能界面如下

Main窗体就更好说了,就是我前面所有用到 的窗体如下

有点长了,本来打算今天 把框架说完了,看来是不行了,下次接着来吧,下面的四个下次接着来吧,这次多少写的滚动条的代码 有些多了,哎。

完吧!

C#仿QQ皮肤-总体层次说明(一)相关推荐

  1. C#仿QQ皮肤-实现原理系列文章导航

    请稳步我的博客查阅并下载所有资源以及源代码 http://www.cckan.net                                          写作说明 有不少朋友建议我写一下 ...

  2. C#仿QQ皮肤_写作说明

    写作说明 有不少朋友建议我写一下原理,其实我也很想写一下不过一直没有时间,正好今天有时间来个开题吧!!!  有句名言说的好,用心做事但不埋头,积跬步而至千里.性格决定命运,专注成就人生,细节决定成败. ...

  3. C#仿QQ皮肤-常用用户控件EnterFrom1和窗体EntryForm的实现

    导读部分 ----------------------------------------------------------------------------------------------- ...

  4. C#仿QQ皮肤系列之-引言

    阅读全文:http://www.cckan.net/forum.php?mod=viewthread&tid=200 C#仿QQ皮肤-实现原理系列文章导航                    ...

  5. C#仿QQ皮肤-ContextMenuStrip 控件实现

    原文:http://www.sufeinet.com/thread-2083-1-1.html 导读部分 ----------------------------------------------- ...

  6. C#仿QQ皮肤—更新DataGridView的鼠标跟随效果

    C#仿QQ皮肤-实现原理系列文章导航                                                               http://www.cnblogs. ...

  7. MySkin仿QQ皮肤,零基础拥有漂亮的软件界面

    MySkin仿QQ皮肤,零基础拥有漂亮的软件界面.支持透明效果,阴影边框,任意颜色更换,自定义背景,完美支持原生winform全套控件. QQ2013/2014 皮肤同时也是MySkin默认皮肤,无需 ...

  8. C#仿QQ皮肤----新版皮肤Dll发布啦

    下载:http://www.cckan.net/forum.php?mod=viewthread&tid=77 C#仿QQ皮肤-实现原理系列文章导航                       ...

  9. C#仿QQ皮肤-TextBox 控件实现

    C#仿QQ皮肤-实现原理系列文章导航                                                               http://www.cnblogs. ...

最新文章

  1. Java Socket多线程异步通信
  2. driver: Linux设备模型之input子系统详解
  3. 黑盒测试——自动饮料售货机
  4. 如何通过Graph+AI的方法打造高精度风控模型?
  5. Linux网络子系统
  6. Nginx+Tpmcat 负载均衡
  7. 用html5做一个介绍自己家乡的页面_厚溥资讯 | HTML5的小知识点小集合(上)
  8. powershell局域网内同步文件夹,文件脚本
  9. mac无法正确打开html,苹果MacBook电脑打不开网页不能访问怎么办
  10. 开发监控云组态软件的组成
  11. 线性代数-MIT 18.06-汇总
  12. 【万字讲解C语言入门小游戏】——三子棋
  13. Eclipse使用教程(入门级)
  14. GIS基础知识 - 坐标系、投影、EPSG:4326、EPSG:3857
  15. Android开发之科大讯飞语音合成与播报
  16. 关于ksps(A/D转换速率单位)
  17. 线性离散系统的分析与校正
  18. html5 input搜索框样式修改,修改input搜索框默认叉号的样式为自定义图片
  19. HTTP协议是什么?
  20. sqlyog企业版 v8 32注册码

热门文章

  1. KMM(Kotlin Multiplatform Mobile)Welcome to Kotlin/Native World
  2. python 函数定义的样例
  3. 基于SSM实现的评教系统
  4. win VS15 c++添加 opencv
  5. 随笔 javascript-抽象工厂模式
  6. COCI 2016/2017 Round #3 题解
  7. Java多线程开发详解
  8. python11-函数合集
  9. Oracle WDP引爆了什么
  10. 为什么从虚拟机打开linux时黑屏,linux虚拟机黑屏解决