• C#仿QQ皮肤-总体层次说明(一)


         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(10), 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(
    thisnew EventArgs());

                            
    if (Scroll != null)
                                Scroll(
    thisnew 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(
    thisnew EventArgs());

                            
    if (Scroll != null)
                                Scroll(
    thisnew 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(
    thisnew EventArgs());

                
    if (Scroll != null)
                    Scroll(
    thisnew 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窗体就更好说了,就是我前面所有用到 的窗体如下

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

      完吧!

                             

                                  如有转载请注明出处谢谢合作!!

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    TiFlash:并非另一个 T + 1 列存数据库
    单图说TDSQL;OceanBase 2.2 事务引擎核心功能;穿云箭2.0版发布;RMAN DUPLICATE配置19C DG;外键上有无索引的影响;MySQL8.0 索引新功能;GaussDB C
    tcpdump 看到 报文长度超过MSS
    TCP 最大段大小(Max Segment Size,MSS) 最大段大小
    Hack The Box——Traceback
    如何设置don't fragment (DF) flag 在socket上? (实际模拟路径 MTU 发现)
    MTU=1500 1460数据为什么要发2次?
    MTU=1500,单个报文段最大值为1448
    MTU 1500 发送1448数据
  • 原文地址:https://www.cnblogs.com/jcomet/p/1693567.html
Copyright © 2020-2023  润新知