《叩响C#之门》接触窗体编程 基本知识 和 自动产生的部分代码分析(11月7日更新)

一、一些概念性知识

1、基本概念

1)在Windows中,具有图形用户界面(Graphics User Interface,GUI)的程序称为窗体程序,窗体程序由窗体(Form)和组件(Component)构成,每个应用程序都有一个主窗体,主窗体中分门别类地排列着各种组件。窗体也可以看成是一个放置组件的容器(Container),组件有些是可见的,有些组件是不可见的,可见的组件称为控件(Control)。

2)关键字partial允许把同一个类分别定义在多个源文件中,窗体编程中,把需要人工编辑的代码放在诸如Form1.cs此类的文件中,而自动生成、不需要人工修改的代码定义在另一个源文件中。自动生成的有两个源文件,一个是以resx为扩展名,如Form1.resx,它包括窗体中的所有资源(resource,前三个字母正好是扩展名resx的前三个字母),包含字符串、图标、位图等;另一个文件后缀为Designer.cs,如Form1.Designer.cs,主要包含了声明控件和初始化窗体等代码,在代码中,InitializeComponent()方法,即初始化组件方法,用来初始化窗体和窗体中的控件。

3)编写窗体程序相当于编写一个派生于Form类的新窗体类(Form类是在System.Windows.Forms命名空间中),它继承了Form类的所有外观特征和行为特征。所以当生成的时候,Form1.Designer.cs中的InitializeComponent()代码很少,因为所有的属性值都是Form类初始化时默认的值,只有如下代码:

this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";

一旦在Form1中对继承的属性进行改变,则Form1.Designer.cs中的InitializeComponent()代码开始增加,当后面再添加控件后,内容将更加丰富起来。

4)在新建项目选择Windows应用程序时,输入的名称,将作为项目名称、程序集名称、默认命名空间名称,以及实际自动生成代码中所用的命名空间名称,如Form1.Designer.cs的代码都在一个命名空间中,这个命名空间就是上述的这个名称;需要人工编辑的代码Form1.cs中主程序代码所在的命名空间也是这个名称。该名称与解决方案名称没有关联。

5)当手工在Form1.Designer.cs代码中的InitializeComponent()修改Form1窗体的名称(this.Name=”修改的窗体名”),发现对程序没什么变化,设计器里窗体名称依然还是原来的名称,程序可以正常运行;如果在设计器里对窗体名进行修改,则Form1.Designer.cs中的上述对应位置自动改成新的名称,同时,Form1.Designer.cs和Form1.cs、Program.cs中的对应类名和构造函数都自动变为新的名称。

6)Program.cs中的代码也是在同一个命名空间中,并拥有程序的主入口Main()函数。在Main()函数中使用了System.Windows.Forms命名空间中的Application类,该类提供了一系列管理窗体程序的静态方法,如EnableVisualStyles()方法启用应用程序的可视样式,Exit()方法退出程序,Run()方法启动程序。其中在Run()的参数中是用new创建了一个Form1的对象,如Application.Run(new Form1()); 可见编写窗体程序实际上就是编写一个继承于Forms命名空间中的Form类的窗体类,然后通过Forms命名空间中的Application类的静态方法Run()去运行该类的一个实例。

 

2、添加控件中涉及的基本概念

1)各种控件实际上都是类,创建一个控件,如创建按钮,就相当于在窗体类中声明了一个Button类的对象(在Form1.Designer.cs中)。也就是在Form1类中添加了成员变量。

//创建按钮button1相当于在窗体类Form1中声明了一个Button类的对象button1,即生成了一个引用符,这个引用符变量是Form1类的私有成员变量。
private System.Windows.Forms.Button button1;
 
//然后在Form1类的InitialComponent()方法中,先对这个按钮控件对象引用符进行初始化,将实际的对象地址传给它们
this.button1 = new System.Windows.Forms.Button();
 

2)添加Button类的对象后,Form1.Designer.cs的InitialComponent()方法中需要熟悉的代码如下:

 

            // Form1
           // 
           this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);//获取或设置控件的设计尺寸
           this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;//获取或设置控件的自动缩放模式
           this.ClientSize = new System.Drawing.Size(292, 273);//获取或设置窗体工作区大小
           this.Controls.Add(this.button1);
           /*
            * Controls是属于System.Windows.Forms命名空间中的Control类中的一个只读属性,这个属性是以Control类中的嵌套类ControlCollection为类型的属性,用于获取包含在控件内的控件的集合。
            * Add是属于System.Windows.Forms命名空间中的Control类中的嵌套类ControlCollection中的一个虚方法,将指定的控件添加到控件集合中
           
           */
           this.Name = "Form1";//获取或设置控件的名称
           this.Text = "Form1";
           this.ResumeLayout(false);

3)无意间,对Controls和ControlCollection下的Add()方法研究中,发现C#中的一些有趣知识:

a、类是可以嵌套类的;

b、类可以定义为静态类,只不过这种类是拒绝被实现为对象的,只能放静态方法和字段及属性,不能有实例成员,但在静态类下继续嵌套的类不用定义为静态类,也不是必须拥有静态方法及属性、字段,静态类下嵌套的非静态类可以被实例化;

c、输出各对象的类型可以直接用对象,或者对象.GetType()或者对象.ToString(); 如果是静态类,则只能通过typeof(静态类名) 来得到对应的CTS类型名,嵌套的类输出的类型名很神奇。

d、嵌套类的作用:譬如A类嵌套了B类,一般用法:A类会定义一个具有B类型的属性C或方法D(),通过属性C或方法D()可以在程序中很好的操控B类中的方法,看似调用B类的方法时,没有针对B类实现实例,实际上不管C还是D(),因其类型是B,所以必须有返回值,该返回值就是B类的一个实例,因此再通过C或D()调用B类的方法也就顺理成章了。

随手试验的代码如下(随手的试验,代码比较乱,仔细看能明白在试验什么):

using System;
using System.Collections.Generic;
using System.Text;

namespace Test
{
    public class Test
    {
        public string str;
        public Test2 testTest = new Test2();
        public Test2 Test2Pro
        {
            get
            {
                return testTest;
 
            }
        }
        public Test2 Test2Fun()
        {
            return testTest;
        }
        public void Function()
        { 
        }
        public static class Test1
        {
            static int i;
            public static void ppp()
            {
                Console.WriteLine("hello Test1");
            }
            public class c            
            {
                int i;
                public static void ppp()
                {
                    Console.WriteLine("hello Test2");

                }

            }
        }
        public class Test2
        {   
            public class Test22
            {
                
            }
            public static void p1()
            {
                Console.WriteLine("hello Test2");
 
            }
            public  void p2()
            {
                Console.WriteLine("由Test类属性或方法调出来");
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test test = new Test();
            Test.Test2 test2 = new Test.Test2();
            test.Test2Pro.p2();
            test.Test2Fun().p2();
            test.testTest = new Test.Test2();
            Test.Test1.c cVar = new Test.Test1.c();
            Console.WriteLine(test);
            Console.WriteLine(test2);
            Console.WriteLine(test.testTest);
            Console.WriteLine(cVar);
            Console.WriteLine(typeof(Test.Test1));

           
        }
    }
}

 image

 

4)在写注释时,发现注释之所以要放在要注释的代码前面,除了清楚以外,另外,如果放在后面,当程序自动在这段代码后面生成代码时,新生成的代码是插在原代码和注释之间的,也就是说如果注释放在代码后面,一旦程序自动在代码后生成新代码,注释就变成了新代码的注释了,容易出问题。

5)自动生成代码的小结

Form1.Designer.cs文件内

在窗体上添加控件,实际上就是在Form1类中添加成员变量,这个成员变量声明后,是在InitializeComponent()方法中进行了初始化,并进行空间变量所有属性的赋值,窗体的属性赋值也在该方法中进行。

Form1.cs文件内

定义了Form1类的构造函数,其实一般也就是调用InitializeComponent()函数,以实现控件的初始化,以及窗体、控件的属性赋值。

窗体、各控件的事件处理程序也定义在该文件中,需手工编写代码。

Program.cs文件内

主要是主程序入口,通过Forms命名空间中的Application类的静态方法Run()去运行窗体类Form1的一个实例。

 

3、System.Windows.Forms命名空间中有大量的类需要学习和掌握

譬如,MessageBox类 显示可包含文本、按钮和符号(通知并指示用户)的消息框。

最主要的方法就是 Show() 该方法有大量重载,最简单的参数就是一个字符串。

 

4、System.Diagnostics (诊断)命名空间提供特定的类,使您能够与系统进程、事件日志和性能计数器进行交互。

EventLog 组件提供在网络上写入事件日志、读取事件日志项以及创建和删除事件日志与事件源的功能。

Process 类提供下列功能:监视整个网络的系统进程以及启动和停止本地系统进程。

PerformanceCounter 类使您能够监视系统性能,而 PerformanceCounterCategory 类则提供新建自定义计数器和类别的方式。

 

重点针对Process类  提供对本地和远程进程的访问并使您能够启动和停止本地系统进程。

Process.Start(参数列表) 被重载

参数只有一个字符串,通过指定文档或应用程序文件的名称来启动进程资源,并将资源与新的 Process 组件关联。

public static Process Start (
    string fileName
)

 

参数是两个字符串,通过指定应用程序的名称和一组命令行参数来启动一个进程资源,并将该资源与新的 Process 组件相关联。

public static Process Start (
    string fileName,
    string arguments
)

参数
fileName
要在该进程中运行的应用程序文件的名称。

arguments
启动该进程时传递的命令行参数。

返回值
与该进程关联的新的 Process 组件,或者如果没有启动进程资源(例如,如果重用了现有进程),则为 空引用

 

5、刚学的窗体代码都是在一个命名空间完成

现在做的都是单线程程序,因此想通过Button的Click事件再调用一个新的Application(窗体实例)时,会出现如下bug:

“在单个线程上开始另一个消息循环是无效操作,请改用 Form.ShowDialog。”

 

1)首先对Program.cs中的代码进一步尝试,特别是对Application类中的方法做简单的研究

a、当我再Program.cs文件内写下如下代码时

static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            Form1 myForm = new Form1();
            myForm.Text = "第二个窗体";
            Application.Run(myForm);
        }

发现两个窗体不是同时打开,而是先打开第一个,当地一个关闭以后,第二个窗体才打开,说明Application.Run()方法在窗体为退出之前是不会流转到下一句代码的。

b、Application.Restart()    该语句一用(放在上述程序的最后一句),发现窗口不管怎么关,都会再次重新打开,两个窗体出现的顺序和上面的一样。

Restart()函数  关闭应用程序并立即启动新的实例。

c、Application.Run() 有三个重载,现在只能大概看明白参数为窗体实例的用法。

Application.Run(窗体实例)  在当前线程上开始运行标准应用程序消息循环,并使指定窗体可见。

d、Application.Exit()方法,通知所有消息泵必须终止,并且在处理了消息以后关闭所有应用程序窗口。

可以放在按钮的Click事件内,点击该button,就会退出程序。但如果有了Restart()语句后,及时Exit,也不能真正推出程序。

另外,ExitThread() 退出当前线程上的消息循环,并关闭该线程上的所有窗口。

e、Application.UserAppDataPath属性是字符串值,为 获取用户的应用程序数据的路径。

f、StartupPath属性  获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称。

 

2)窗体的方法和属性

属性

a、AcceptButton  获取或设置当用户按 Enter 键时所单击的窗体上的按钮。

试了,但是发现不好使,不知哪里出了错误。

b、Visible   获取或设置一个值,该值指示是否显示该控件。其实不论窗体还是控件,该属性都通用。

c、BackColor 用于获取或设置窗体的背景色 

d、TransparencyKey  获取或设置将表示窗体透明区域的颜色。 需和BackColor配合使用

this.BackColor = Color.Red;
this.TransparencyKey = Color.Red;

将上述语句放到一个按钮的Click事件中,可以看到窗体变透明后的变化。

e、Form.ActiveForm 属性 只读属性

获取此应用程序的当前活动窗体。 可以使用此方法获得对当前活动窗体的引用,以在该窗体或其控件上执行操作。 如果应用程序是多文档界面 (MDI) 应用程序,请使用 ActiveMdiChild 属性获得当前活动的 MDI 子窗体。

public static Form ActiveForm { get; }

如: Form currentForm = Form.ActiveForm;

f、AutoScaleMode属性还没找到在哪里使用合适,但MSDN上很有用。

AutoSizeMode属性(获取或设置窗体自动调整自身大小的模式。)还是很有用的,两个值,分别为GrowAndShrink 控件根据它的内容增大或缩小,GrowOnly 控件可以根据其内容任意增大,但不会缩小至小于它的 Size 属性值。 但在使用前一定要将 AutoSize置为true。程序中已试验。

g、ModifierKeys   获取一个值,该值指示哪一个修改键(Shift、Ctrl 和 Alt)处于按下的状态。

public static Keys ModifierKeys { get; }

Keys枚举值非常丰富,请看MSDN

h、Opacity  获取或设置窗体的不透明度级别。

i、Locked属性在设计Form1中窗体的属性中,但MSDN没有对它做解释,只是在派生类中有该属性。应该怎么用?

j、Enabled   获取或设置一个值,该值指示控件是否可以对用户交互作出响应。

 

方法

a、对ShowDialog()进行了初探,如A.ShowDialog(B)  方法是 将窗体A窗体显示为具有指定所有者(所有者是窗体B)的模式对话框,即B拥有A,A是B的对话框。A和B都是 不同窗体类的对象的引用符。

如果直接用ShowDialog(),没有参数,则将窗体显示为模式对话框,并将当前活动窗口设置为它的所有者。

可以使用此方法在应用程序中显示模式对话框。调用此方法时,直到关闭对话框后,才执行此方法后面的代码。可以将 DialogResult 枚举值之一分配给对话框,方法是将该值分配给窗体上 Button 的 DialogResult 属性或通过使用代码设置窗体的 DialogResult 属性。此方法随后返回该值。可以使用此返回值确定如何处理对话框中发生的操作。例如,如果关闭了对话框,并通过此方法返回了 DialogResult.Cancel 值,则可防止执行在调用 ShowDialog 之后的代码。

当窗体显示为模式对话框时,单击“关闭”按钮(窗体右上角带 X 的按钮)会隐藏窗体并将 DialogResult 属性设置为 DialogResult.Cancel。与无模式窗体不同,当用户单击对话框的关闭窗体按钮或设置 DialogResult 属性的值时,.NET Framework 不调用 Close 方法。窗体转而可以隐藏并可重新显示,而不用创建该对话框的新实例。因为未关闭显示为对话框的窗体,所以在您的应用程序不再需要该窗体时,必须调用该窗体的 Dispose 方法。

        public void ShowMyDialogBox()
        {
            Form2 testDialog = new Form2();
            Form1 form1 = new Form1();
            // Show testDialog as a modal dialog and determine if DialogResult = OK.
            DialogResult dia=testDialog.ShowDialog(form1);
            form1.usernameTextBox.Text = dia.ToString();
            form1.ShowDialog();
            testDialog.Show();//关掉的窗体又出来了!
            //if (testDialog.ShowDialog() == DialogResult.OK)
            //{
            //    // Read the contents of testDialog's TextBox.
            //    this.usernameTextBox.Text =testDialog.textBox1.Text;
            //}
            //else
            //{
            //    this.usernameTextBox.Text = "Cancelled";
            //}
            //testDialog.Dispose();//要想真正关掉模式窗体,必须用Dispose()
        }

具体试验程序在虚拟机中的“D:\DOT NET\叩响C#之门\第13章 Windows窗体编程\HelloWorld\HelloWorld”项目中。

 

b、Hide()方法 对用户隐藏控件。MSDN上的这段代码不光说米高Hide()的用法,也引入一个新知识(ModifierKeys ),下面的代码示例在单击某按钮的同时按 Ctrl 键时隐藏该按钮。

        private void button2_Click(object sender, System.EventArgs e)
        {
            /* If the CTRL key is pressed when the 
               * control is clicked, hide the control. */
            if (Control.ModifierKeys == Keys.Control)
            {
                ((Control)sender).Hide();
            }
        }

c、Close()方法  关闭窗体。某些时候效果上类似于Hide()方法,主要是在关闭模式窗体时。

窗体关闭后,关闭在该对象内创建的所有资源并且释放该窗体。通过处理 Closing 事件,并设置作为参数传递给事件处理程序的 CancelEventArgs 的 Cancel 属性,可以防止在运行时关闭窗体。如果要关闭的窗体是应用程序的启动窗体,则该应用程序结束。

Close 时不释放窗体的一种情况是,窗体属于多文档界面 (MDI) 应用程序的一部分且是不可见的(在关闭模式窗体时,应该也是如此)。在这种情况下,您需要手动调用 Dispose,将窗体的所有控件都标记为进行垃圾回收。

在显示为无模式窗口的 Form 上调用 Close 方法时,不能调用 Show 方法使窗体可见,因为窗体的资源已被释放。若要隐藏窗体然后又使其可见,请使用 Control.Hide 方法。

d、Show()方法,普通的Show()方法 向用户显示控件。 当Show()内有了参数 窗体实例时,则向用户显示具有指定所有者的窗体,如窗体A.Show(窗体B),表示将A显示出来,同时指定B为A的所有者。这一用法很有趣。

 

 

 

二、最简单的一些控件

1、Button

Button 构造函数

public Button ()

首先,创建按钮button1相当于在窗体类中声明了一个Button类的对象button1,即生成了一个引用符,这个引用符变量是Form1类的私有成员变量。

private System.Windows.Forms.Button button1;

第二步,Form1.Designer.cs中的InitializeComponent()中需增加如下代码:

this.button1 = new System.Windows.Forms.Button();//初始化实例,并把对象的地址赋给引用符button1

this.Controls.Add(this.button1);//在对Form1窗体的初始化程序中加入该句,将button1添

 

引入Controls属性的解释:

System.Windows.Forms.Control类 定义控件的基类,控件是带有可视化表示形式的组件。

Control.Controls属性是个集合类的属性  获取包含在控件内的控件的集合。

public ControlCollection Controls { get; }

属性值是一个 Control.ControlCollection,它表示控件内包含的控件的集合。

这里涉及到集合类的知识,可以先学后章节,再回来分析

Control 可以充当控件集合的父级。例如,将多个控件添加到 Form 时,每一个控件都是分配给该窗体的 Controls 属性的 Control.ControlCollection 的成员,Controls 属性派生于 Control 类。

可以使用 Control.ControlCollection 类中的可用方法,在分配给 Controls 属性的 Control.ControlCollection 中操作控件。

将多个控件添加到父控件时,建议在初始化要添加的控件之前调用 SuspendLayout 方法。将控件添加到父控件之后,调用 ResumeLayout 方法。这样就可以提高带有许多控件的应用程序的性能。

上面Add()方法前的Controls属性是继承自Control类来的,Controls属性是个集合类ControlCollection,包含在控件内的控件的集合,因此通过Add()来添加集合类内的元素,这些元素又都是Control类的对象。

 

常见属性

AutoSizeMode、AutoSize、BackColor、Enabled、Visible用法与窗体一致

常见方法

Hide()、Show()方法与窗体一致

常用事件

Click   在单击控件时发生。

 

2、标签(Label)主要用属性Text

3、超链接标签(LinkLabel)

常用属性: LinkColor 超链接访问前的颜色,默认为蓝色;ActiveLinkColor 超链接单击时的颜色;VisitedLinkColor超链接访问后的颜色

LinkVisited 记录超链接是否被访问过,ture表示已访问;LinkBehavior 设置超链接中下画线的样式,共有四种

LinkArea 指定那部分文本为超链接

常用事件: LinkClicked单击超链接时产生事件

在使用时会用到System.Diagnostics命名空间的Process类,该类的Start()方法用于打开文件夹或应用程序。该方法有一个或两个参数,在本随笔的上文已有阐述。

4、文本框(TextBox)

常见属性:

Text 文本框内显示的文本(即默认文本)

SelectedText 文本框内被选中的文本

SelectedTextStart 被选中的第一个字符

Multiline 为true时文本可多行,为false时只能单行,默认为false

AcceptsRetrun 为true时Enter建的功能为换行,否则Enter建的功能是确定

ReadOnly 为true时文本不能修改,文本框显示灰色背景

ScrollBars滚动条样式

TabIndex用Tab键激活控件的顺序   怎么用?

PasswordChar使文本框变为密码框,用指定字符屏蔽用户输入的密码

事件

TextChanged文本改变时产生

方法

Clear()清除文本框中的所有文本

Copy()将选定的文本复制到“剪贴板”中

Cut()将选定的文本剪切到“剪贴板”中

Paste()用剪切板中的文本替换选定的文本

ResetText()将Text属性重置为默认值

Redo()重复上一个编辑操作

Undo()撤销上一个编辑操作

5、单选按钮

属性:Checked单选按钮是否被选中

事件:CheckedChanged当单选按钮被选中时发生

6、复选框

属性:Text复选框旁显示的文本

Checked 复选框是否被勾选

CheckState复选框的状态,分Checked、Unchecked和Indeteminate(不确定)

事件:

CheckedChanged当复选框被勾选时发生(取消勾选时也发生)

CheckStateChanged当CheckState属性改变时发生

7、数字输入框(NumericUpDown)

数字输入框是用来帮助我们输入特定范围内的数字。既可以在文本框内直接输入数字,也可以用上下按钮进行微调。

属性:

Value 数字输入框内显示的数值(类型为decimal)

Increment (名词:增加,增量)用上下按钮进行微调时的变化量

Maximum 可输入的最大值

Minimum 可输入的最小值

UpDownAlign 设定上下按钮的位置,有左右两种选项

事件:

ValueChanged数值改变时发生

 

涉及其他类的知识:

Font类 属于System.Drawing空间

Font (String, Single)   是其中的一个构造函数,参数为字体和字大小

public Font (
    string familyName,
    float emSize
)

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            int fontSize = Convert.ToInt32(numericUpDown1.Value);
            Font myFont = new Font("黑体",fontSize);
            label1.Font = myFont;
        }

 

8、群组框(GroupBox)

如果窗体上有很多空间,可以用群组框进行分组,功能相关的控件分为一组可以使界面更加清晰。

属性:Controls 由群组框包含的所有控件组成的集合

在Form1.Designer.cs的代码中对 GroupBox属性的初始化的代码中会有类似下述的语句,将包含在GroupBox内的控件添加到群组框内的控件集合中。

this.groupBox1.Controls.Add(this.MaleRadio);
this.groupBox1.Controls.Add(this.FemaleRadio);

 

9、面板(Panel)

面板(Panel)和群组框一样,也用来把控件进行分组,它和群组框的区别是群组框可以有标题,而面板可以有滚动条。

属性:

AutoScroll 是否显示滚动条

Controls 有群组框中包含的控件组成的集合

BackgroundImage 设置背景图片

涉及其他类的知识:

String 类中 Substring()方法 从字符串中截取子串。

posted on 2009-10-31 17:31  友闻语上  阅读(1398)  评论(0编辑  收藏  举报