zoukankan      html  css  js  c++  java
  • 委托杂谈

    最近在想C#中的控件是如何绘制上去的,当然我想问的就是绘制是在什么时候触发的?网上找了找,似乎也有人在讨论,众说纷纭。于是将C#那个Forms结尾的dll给反编译了,似乎看出些猫腻,里面有几个和绘制相关的方法,OnPaint和OnItemdraw,如下图:

    这是我们直接从源码中看到的,也就是说在这两个方法中又是通过事件去处理的。

    Windows是基于消息的,所谓消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了某种变化,例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消。

    在MFC中我们会看到WM_PAINT 这个消息,而这个消息对应的操作函数就是OnPaint,了解MFC中视图的话,就应该知道我们经常在Ondraw 方法中进行绘制,其实这个OnDraw方法是在OnPaint中调用的。而在C#中OnPaint会引发Paint,如果我们要对一些进行操作,重新Paint就行了。

    在C#中是通过消息触发-然后调用OnPaint,OnDrawItem方法,然后调用一个派发事件的调用,对于事件C#开发人员应该不会陌生,什么单击,双击等。

    事件我在合理简单的称之为一个委托的特殊变量,看来在C#中存在大量的委托或者大量的事件,今天就静下心来想了想,既然治理派发事件的其实是一个虚函数,虚函数是子类可以重写的,并且可以在运行时识别的,那为什么不在子类中直接重写虚函数,而还要派发事件呢?这里我通过两种方式都实现了我要的结果(ListBox隔行换色):

     重写事件
    
    
    this.DrawItem += new DrawItemEventHandler(ListBoxEx_DrawItem); void ListBoxEx_DrawItem(object sender, DrawItemEventArgs e) { OnDrawItem1(e); } protected void OnDrawItem1(DrawItemEventArgs e) { if (e.Index != -1) { if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { RenderHelper.RenderBackgroundInternal(e.Graphics, e.Bounds, Color.FromArgb(255, 255, 255), Color.FromArgb(0, 0, 0), Color.Purple, RoundStyle.Left, true , true, LinearGradientMode.Vertical); } else { Color backColor; if (e.Index % 2 == 0) { backColor = Color.FromArgb(255, 255, 255); } else { backColor = Color.FromArgb(0, 255, 255); } using (SolidBrush brush = new SolidBrush(backColor)) { e.Graphics.FillRectangle(brush, e.Bounds); } } string text = Items[e.Index].ToString(); TextFormatFlags formatFlags = TextFormatFlags.VerticalCenter; if (RightToLeft == RightToLeft.Yes) { formatFlags |= TextFormatFlags.RightToLeft; } else { formatFlags |= TextFormatFlags.Left; } TextRenderer.DrawText( e.Graphics, text, Font, e.Bounds, ForeColor, formatFlags); if ((e.State & DrawItemState.Focus) == DrawItemState.Focus) { e.DrawFocusRectangle(); } } }
    重写虚函数
    
    

    protected void OnDrawItem(DrawItemEventArgs e) { base.OnDrawItem(e); if (e.Index != -1) { if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { RenderHelper.RenderBackgroundInternal(e.Graphics, e.Bounds, Color.FromArgb(255, 255, 255), Color.FromArgb(0, 0, 0), Color.Purple, RoundStyle.Left, true , true, LinearGradientMode.Vertical); } else { Color backColor; if (e.Index % 2 == 0) { backColor = Color.FromArgb(255, 255, 255); } else { backColor = Color.FromArgb(0, 255, 255); } using (SolidBrush brush = new SolidBrush(backColor)) { e.Graphics.FillRectangle(brush, e.Bounds); } } string text = Items[e.Index].ToString(); TextFormatFlags formatFlags = TextFormatFlags.VerticalCenter; if (RightToLeft == RightToLeft.Yes) { formatFlags |= TextFormatFlags.RightToLeft; } else { formatFlags |= TextFormatFlags.Left; } TextRenderer.DrawText( e.Graphics, text, Font, e.Bounds, ForeColor, formatFlags); if ((e.State & DrawItemState.Focus) == DrawItemState.Focus) { e.DrawFocusRectangle(); } } }

    肯定是一样的,刚才已经说的很清楚了OnDrawItem中调用了DrawItem,这个只是顺序的不同而已,这种带来的好处,我个人认为,这里将主动权交给了我们,通过委托可以实现迟后的绑定,将要执行的操作暴露给了客户端。DrawItem是事件,是微软定义好的,那么事件的处理函数,将由我们实现。通过和MFC的对比,看来这两者在事件处理上本质上还是一样的,可谓换汤不换药。

    委托到底有什么好处呢?这个问题我还没说吧。但是我不想说了,因为从上面可以看出就是委托才将消息跟我们的事件对应起来,这是上面的例子直接可以看出的,起到桥梁作用,起到了延后调用的作用,还有处理逻辑分离(MVC中就是在视图中定义事件或者委托变量,然后在Controller中绑定的)。此外,在给委托变量赋值的时候可以用“=”,也可以用“+=”,前者是赋值,而后者是相当于追加或者绑定,(如果是事件的话,直接用+=),但是在追加(绑定)之前先赋值,否则会直接报错,大家也可以在去尝试。

            int Pow2(int x)
            {
                 return (int)Math.Pow(x, 2);
            }
        
                    
                Pow PowTest2 = new Pow(this.Pow2);
               //使用+=之前先用等号赋值,不然出错       
                  PowTest2 +=new Pow(this.Pow2);
                 PowTest2.Invoke(4);
      
    委托变量和在C++或者C语言中学的函数指针类似,因为在这两个语言中,函数名代表了函数所在位置的指针,所以可以定义一个有相同参数的变量,C#中的委托变量跟这个如出一辙:

      protected int add(int a, int b)
            {

                return a + b;
            }

            protected int sub(int a, int b)
            {

                return a - b;
            }

     public delegate int Calculate(int a, int b);
            Calculate pCal;
            public Form1()
            {
                
                        pCal = add;
            }

            private void Form1_Load(object sender, EventArgs e)
            {
            

                if (pCal != null)
                {
                    int c=pCal(5, 3);

                    pCal = sub;

                    c = pCal(5, 3);

                }
            }

    今天的这些就是自己的饿突发奇想,冥冥之中,希望自己有所长进,有所收获,也希望自己能将这种剖根问底的想法坚持到底!

    推荐博文:http://www.cnblogs.com/BLoodMaster/archive/2010/07/06/1771926.html

  • 相关阅读:
    软工试水日报-纯js原生简易动态表格 3/15
    软工试水日报-Echarts动态柱形图 3/14
    大二下学期第一次结对作业(第一阶段)
    大二下学期之第一次结对作业(第一阶段)
    大二下每周总结
    大二下学期之第一次结对作业(第一阶段)
    大二下学期第一次结对作业(第一阶段)
    大二下学期第一次结对作业(第一阶段)
    大二下学期每日总结之第一次个人作业(第三阶段)
    大二下学期每日总结之第一次个人作业(第三阶段)
  • 原文地址:https://www.cnblogs.com/zuiyirenjian/p/3124322.html
Copyright © 2011-2022 走看看