c# winform编程转例

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace openfiledialog
{
    class keytest:Form
    {
        public static void Main()
        {
            Application.Run(new keytest());
        }

        //枚举事件类型 不事的事件,如无,按下键,放开键,
        enum eventtype
        {
            none,
            keydown,
            keyup,
            keypress
        }

        //struct结构 轻类型的类,它是值类型 存储事件类型名称比如keypress及事件的参数,如:keyup(object sender,keyeventargs args)
        struct keyevent
        {
            public eventtype evttype;
            public EventArgs evtargs;
        }
        //const常整型常量
        const int inumlines = 25;
        int inumvalid = 0;
        int iinsertindex = 0;

        //STRUCT结构类型的数组,而非类,如下数组存储?内容
        keyevent[] akeyevt=new keyevent[inumlines];//数组声明,引用上述的CONST常整常量

        //文本的位置,具体起何功能,再分析 如下的xevent为首行各个列头显示的坐标
        int xevent, xchar, xcode, xmods, xdata, xshift, xctrl, xalt, xright;

        //构造函数 初始化窗体的基本元素信息
        public keytest()
        {
            Text = "key test";
            BackColor = SystemColors.Window;
            ForeColor = SystemColors.WindowText;
            
            //初始化上述以X打头的相关字段,让多个东东有关联性,即是多个东东以其中某个东东为基础,这样就OK了
            xevent = 0;
            xchar = xevent + 5 * Font.Height;//在xevent及font字体行高的基础,得到xchar
            xcode = xchar + 5 * Font.Height;
            xmods = xcode + 8 * Font.Height;
            xdata = xmods + 8 * Font.Height;
            xshift = xdata + 8 * Font.Height;
            xctrl = xshift + 5 * Font.Height;
            xalt = xctrl + 5 * Font.Height;
            xright = xalt + 5 * Font.Height;

            //clientsize为窗体工作区的大小 窗体工作区的大小取决于xright及字体行高和常量inumlines的关系
            ClientSize = new Size(xright,Font.Height*(inumlines+1));
            FormBorderStyle. = FormBorderStyle.Fixed3D;//formborderstyle窗体边框样式
            MaximizeBox = false;//maximizebox标题栏是否显示最大化按钮
        }

        //onkeydown为control事件 输入protected override void后面的事件会自动提示弹出
        protected override void OnKeyDown(KeyEventArgs e)
        {
            akeyevt[iinsertindex].evttype = eventtype.keydown;//引用iinsertindex字段
            akeyevt[iinsertindex].evtargs = e;
            onkey();//onkeydown按键事件会调用此自定义方法
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            akeyevt[iinsertindex].evttype = eventtype.keyup;
            akeyevt[iinsertindex].evtargs = e;
            onkey();
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            akeyevt[iinsertindex].evttype = eventtype.keypress;
            akeyevt[iinsertindex].evtargs = e;
            onkey();
        }

        void onkey()
        {
            if(inumvalid
            {
                //调用creategraphics与dispose必须成对出现
                Graphics gp = CreateGraphics();
                displaykeyinfo(gp,iinsertindex,iinsertindex);
                gp.Dispose();
            }
            else
            {
                scrolllines();
            }
            iinsertindex = (iinsertindex + 1) % inumlines;
            inumvalid = Math.Min(inumvalid+1,inumlines);//math.min返回两个decimal较小的一个
        }

        //如下方法为滚屏方法,其实就是采取重绘,如何重绘呢,调用invalidate,然后会自动调用onpaint事件(即重绘方法),封装
        //重绘区域大小及位置定位,
        protected virtual void scrolllines()
        {
            //rect为窗体工作区大小,不含首行,即首行列头不重绘,第二行左上角坐标(x,y)=(0,font.height)到右下角坐标(x,y)=(clientsize.width,client.height-font.height),为何要-减,要少绘一个行高,故减 
            Rectangle rect = new Rectangle(0,Font.Height,ClientSize.Width,ClientSize.Height-Font.Height);
            Invalidate(rect);//invalidate方法会调用onpaint重绘控件
        }

        //scrolllines会调用此方法事件
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics gp = e.Graphics;

            //打印首列不同列头文字,每个列头文字的坐标由(xevent,0)控制
            boldunderline(gp,"event",xevent,0);//注意如下行皆是同行不同列,这样就把xevent,xchar等打印出来,以不同列同行方式
            boldunderline(gp, "keychar", xchar, 0);
            boldunderline(gp, "keycode", xcode, 0);
            boldunderline(gp, "modifiers", xmods, 0);
            boldunderline(gp, "keydata", xdata, 0);
            boldunderline(gp, "shift", xshift, 0);

            //总结:1,如下2行代码为何不显示,方法1:注解掉  方法2:变换字符串内容  方法3:
            boldunderline(gp, "control", xshift+50, 0);////////////////////////为何这个没打印出来呢,??????? 原代码经过分析,是此control文字的输出坐标在初始化(构造函数中)超出窗体工作区大小,故看不到了,修正即可
            boldunderline(gp, "alt", xalt, 0);

            //
            if (inumvalid < inumlines)
            {
                for (int i = 0; i < inumvalid; i++) //for循环如果内含一条语句,此语句不用{}括起来,类似于if后跟单语句一样
                    displaykeyinfo(gp, i, i);//注意此displaykeyinfo方法属于上述语句if及for,此方法会在内部更新inumvalid
            }
            else
            {
                for (int i = 0; i < inumlines; i++)
                    displaykeyinfo(gp,i,(iinsertindex+i)%inumlines);
            }
        }

        void boldunderline(Graphics gpis, string str, int x, int y)
        {
            //画粗体文字
            Brush brush = new SolidBrush(ForeColor); //solidbrush单色画笔,填充图形形状
            gpis.DrawString(str,Font,brush,x,y);//用何字体及画笔(工具)在何坐标画出字符串str
            gpis.DrawString(str,Font,brush,x+1,y);//再次绘制字符串STR,但不同原来的坐标,而是偏移一个像素位置,这样不加加粗文字了吗
            
            //下划线文字

            // 返回结果:
           // 此方法返回 System.Drawing.SizeF 结构,该结构表示 text 参数指定的、使用 font 参数绘制的字符串的大小,单位由 System.Drawing.Graphics.PageUnit
           //     属性指定。
            SizeF sizef=gpis.MeasureString(str,Font); //知形宽高度,在此对应字符串的矩形大小,sizef

            //如何在每列文字下面加下划线 ,注意每列文字x,y,那么下划线的坐标 位置要考虑到字符串的大小,字符串的大小对应sizef
            gpis.DrawLine(new Pen(ForeColor),x,y+sizef.Height,x+sizef.Width,y+sizef.Height); //drawline画一个连接两个点的线,用工具pen

        }

        //显示按键相关信息的方法
        void displaykeyinfo(Graphics gp, int y, int i)
        {
            Brush br = new SolidBrush(ForeColor);//在此forecolor对应窗体的前景色
            y = (1 + y) * Font.Height;//font为控件显示文字的字体
            gp.DrawString(akeyevt[i].evttype.ToString(), Font, br, xevent, y);//xevent,y为字符串绘制位置坐标,字符串为akyeevt[i].evttype,引用枚举及结构类型STRRUCT
            if (akeyevt[i].evttype == eventtype.keypress)
            {
                //如果事件类型为keypress,事件类型为属于什么事件
                KeyPressEventArgs kpea = (KeyPressEventArgs)akeyevt[i].evtargs;//大转化为小,也叫拆箱操作
                string str = string.Format("\x202D{0} (0X{1:X4}", kpea.KeyChar, (int)kpea.KeyChar);//用到了string.format对指定字符进行格式然后输出
                gp.DrawString(str, Font, br, xchar, y);//基于上行str
            }
            else
            {
                KeyEventArgs kea = (KeyEventArgs)akeyevt[i].evtargs;
                string str = string.Format("",kea.KeyCode,(int)kea.KeyCode);//keyeventargs事件参数类有许多属性如keycode,keydata等
                gp.DrawString(str, Font, br, xcode, y);//如下皆为同行不同列显示对应的信息
                gp.DrawString(kea.Modifiers.ToString(), Font, br, xmods, y);//modifiers为keydown,keyup事件按下ctrl,shift,alt按下键的组合,返回为keys
                gp.DrawString(kea.KeyData.ToString(), Font, br, xdata, y);//keydata为keydown,keyup事件的键数据
                gp.DrawString(kea.Shift.ToString(), Font, br, xcode, y);//keyeventargs.shift指示是否按下shift键,bool型
                gp.DrawString(kea.Control.ToString(), Font, br, xctrl, y);
                gp.DrawString(kea.Alt.ToString(),Font,br,xalt,y);
            }
        }
    }
}