精华内容
下载资源
问答
  • XP控件command

    2009-06-09 18:14:04
    让你的窗体拥有像XP一样的风格。TGcommand ,TGBOX,TGlist
  • VB讲课笔记06:窗体与常用控件

    千次阅读 2018-03-10 17:24:29
    VB讲课笔记06:窗体与常用控件 一、窗体设计 窗体是VB中最基本的对象,是应用程序的基本组成部分,是菜单栏、工具栏和控件等对象的容器,是应用程序与用户交互的窗口或对话框。窗体设计是整个VB应用程序设计的基础...

    目录

    一、窗体设计

    1、窗体设计分类

    2、窗体常用属性

    设置窗体背景图片的两种方法

    3、窗体常用事件

    任务1、每单击窗体一次,让窗体缩小1/3。

    任务2、设置窗体背景图片(静态方式、动态方式)

    4、窗体常用方法

    案例:测试窗体常用方法

    5、多重窗体程序如何设置启动对象(窗体或Sub_Main过程)

    6、加载窗体与卸载窗体

    二、控件概述

    1、控件概念

    2、VB控件分类

    三、标签控件

    1、标签概述

    2、标签常用属性

    标签控件示例

    四、文本框

    1、文本框概述

    2、文本框常用属性

    3、文本框常用事件

    4、焦点

    5、获得焦点的方法

    案例1、计算圆周长、圆面积与球体积

    五、按钮

    1、按钮概述

    2、按钮常用属性

    3、按钮常用事件

    案例:用户登录(标签、文本框和按钮)

    六、图片框与图像框

    1、图片框属性

    2、图像框属性

    3、图像框与图片框的区别

    4、图片框和图像框的事件和方法

    案例:交换图片

    案例:图片框与图像框比较

    七、单选按钮与复选框

    1、单选按钮与复选框常用属性

    单选按钮案例演示:

    复选框案例演示:

    八、列表框与组合框

    (一)列表框

    1、列表框概述

    2、列表框常用属性

    (二)组合框

    1、组合框概述

    2、组合框三种样式

    (三)列表框与组合框常用事件

    列表框案例:选择水果

    九、滚动条

    1、滚动条常用属性

    2、滚动条常用事件

    ​十、计时器

    1、计时器属性

    2、计时器事件

    案例:滚动字幕

    十一、框架

    1、框架概述

    2、框架常用属性

    十二、形状控件

    1、Shape概述

    2、常用属性

    案例:展示六种基本形状以及交替显示两种图形

    十三、焦点与Tab顺序

    1、焦点

    2、Tab顺序

    十四、综合案例:个人信息注册


    一、窗体设计

    窗体是VB中最基本的对象,是应用程序的基本组成部分,是菜单栏、工具栏和控件等对象的容器,是应用程序与用户交互的窗口或对话框。窗体设计是整个VB应用程序设计的基础。一个窗体对应一个代码模块。

    1、窗体设计分类

    (1)单窗体设计

    应用程序中只包含一个窗体,即单文档窗体。

    (2)多重窗体设计

    应用程序中包含多个窗体,即多个单文档窗体。

    (3)多文档窗体(MDI)设计

    一种特殊的多窗体界面,应用程序中包含一个父窗体和多个子窗体。

    2、窗体常用属性

    (1)Name(名称)

    窗体名称,默认值为Form1、Form2、……。只能在设计阶段通过属性窗口改变,运行时不能更改。

    (2)Caption(标题)

    在窗体的标题栏中显示的标题,默认值为Form1、Form2、……。


    • 窗体的属性决定窗体的外观和操作。可以通过属性窗口、程序代码、鼠标拖动来设置窗体属性。
    • 名称(Name)属性与Caption属性有本质区别。Caption属性的值显示在标题栏中,而名称(Name)属性是一个标识,用于区别其他对象,在代码中标识对象。
    • 在设计状态下,窗体和控件的Name属性值可以由编程人员设置,这就是对窗体或控件的命名,命名必需使用合法的用户标识符。

    (3)MinButton

    最小化按钮,取值为True/False, 默认值为True,表示显示最小化按钮。

    (4)MaxButton

    最大化按钮,取值为True/False,默认值为True,表示显示最大化按钮。

    (5)ControlButton

    确定是否显示三个控制按钮(最大化、最小化、关闭按钮),默认值为True。如果设置为False,不显示三个控制按钮。

    如果将MinButton属性设置为False,则窗体的最小化按钮显示为灰色。

    如果将MinButton属性和MaxButton属性均设置为False,则最小化按钮和最大化按钮均不会显示,只显示关闭按钮。

    (7)ForeColor

    设置窗体上显示文本的颜色。

    (8)Font

    设置窗体上显示文本的字体。

    (9)Enabled

    设置窗体运行时能否被用户操作。默认值为True,能被用户操作;如果设置为False,则不能被用户操作。在运行时才能看到效果。

    (10)Visible

    设置窗体运行时是否可见。默认值为True,窗体可见;如果设置为False,窗体不显示,此时不能被用户操作。在运行时才能看到效果。

    (11)BorderStyle

    确定窗体边框的类型,取值范围为0到5。

    (12)Picture

    设置窗体背景的图像,默认值为空,表示没有背景图像。

    (13)Left、Top

    设置或返回窗体的左边框距屏幕左边的距离、顶部距屏幕顶部的距离。

    (14)Height、Width

    设置或返回窗体的高度和宽度。

    (15)WindowState

    窗体开始运行时的初始显示状态,0表示正常状态(默认值),1表示最小化,2表示最大化。

    (16)StartUpPosition

    用于设置窗体启动位置

    (17)Count属性

    窗体里控件个数

        


    设置窗体背景图片的两种方法


    (1)直接在属性窗口中,选择Picture属性,通过选择文件或“粘贴”剪贴板中的图形,进行设置。

    (2)在程序中用函数LoadPicture将图像调入作为背景,其格式为:

     [Object.]Picture = LoadPicture("图像文件的完整路径名和文件名")

    如果Object(对象)为当前窗体,则可省略为:

    Picture = LoadPicture("图像文件的完整路径名和文件名")

    3、窗体常用事件

    (1)Initialize

    初始化所有的数据。

    (2)Load

    发生在Initialize之后。装载窗体,但此时窗体不是活动的。

    (3)Activate

    发生在Load之后。激活窗体,等待其他事件发生,此时才能响应用户在界面上的交互操作。

    (4)Click

    在Activate之后,单击窗体,触发Click事件。

    (5)DbClick

    在Activate之后,双击窗体,触发DbClick事件。

    (6)Resize

    在Activate之后,如果进行了改变窗体大小的操作,才会触发Resize事件。

    (7)Unload

    关闭窗口时才发生。把窗体从内存中删除(即卸载窗体)。

    任务1、每单击窗体一次,让窗体缩小1/3。

    单击一次窗体:


    任务2、设置窗体背景图片(静态方式、动态方式)

    (1)静态方式

    (2)动态方式

    问题:如何通过代码取消设置的背景图片呢?

    运行程序,单击窗体加载背景图片:

    单击【清除窗体背景图片】按钮:


    4、窗体常用方法

    (1)Cls

    清除运行时输出的文本和图形。

    格式:[Object.]Cls

    (2)Show

    显示窗体。

    格式: <Object.> Show

    (3)Hide

    隐藏窗体。

    格式: <Object. > Hide

    (4)Move

    移动窗体或控件。

    格式: [Object.] Move Left,Top,Width,Height


    案例:测试窗体常用方法


    通过Move方法固然可以改变窗体在屏幕上的位置,其实通过修改窗体的Top和Left属性也可以移动窗体。

    以后我们结合定时器Timer,定时修改控件的位置,可以产生动画效果。

    Left属性——相当于横坐标;Top属性——相当于纵坐标

    此时,可以通过四个按钮将窗体移到屏幕的任何位置。

    5、多重窗体程序如何设置启动对象(窗体或Sub_Main过程)

    将启动对象改为Form2:

    启动程序,看看是不是Form2出现在屏幕上。

    下面我们把Sub Main作为启动对象,要求用户输入姓名,如果姓名是“admin”,则显示窗体Form1,否则显示窗体Form2。

    启动程序,首先弹出输入框,要求用户输入姓名:

    输入“admin”,看看那个窗体出现在屏幕上:

    单击【确定】按钮,你会发现窗体Form1出现在屏幕上:

    关闭程序,重新启动,这次输入其它姓名来试一试。

    6、加载窗体与卸载窗体

    运行程序:

    单击【加载窗体2】按钮,屏幕上没有动静。

    单击【显示窗体2】按钮,显示出第二个窗体:

    单击【卸载窗体2】按钮,第二个窗体消失:

    二、控件概述

    1、控件概念

    控件是在图形用户界面(GUI)上输入信息、输出信息、启动事件过程等交互操作的图形对象,是进行可视化程序设计的基础和重要工具。

    2、VB控件分类

    (1)标准控件

    (2)ActiveX控件

    (3)可插入对象控件 

    三、标签控件

    1、标签概述

    标签控件(Label)主要用于显示文本信息及程序运行的某些结果等。
    默认控件名为 Label1、Label2、…。

    2、标签常用属性

    (1)Caption

    用于在标签中显示文本。其值为字符型,是默认属性

    (2)Alignment

    文本的对齐方式。

    •     0-左对齐(默认值)
    •     1-右对齐
    •     2-居中

    (3)BackStyle

    设置标签背景是否透明。

    •     0-透明(默认值)
    •     1-表不透明

    (4)BorderStyle

    设置有无边框。

    •     0-无边框(默认值)
    •     1-表有边框

    (5)Autosize

    决定是否能自动改变控件大小以显示全部内容。

    • True-自动调节
    • False-不自动调节(默认值)。

    (6)Wordwrap

    决定是否能扩大控件以显示标题文字。
     当Autosize和Wordwrap均为True时,显示在要标签中的文本就能实现自动换行。

    默认属性是在程序代码中,可以省略的属性,即可用控件名称(Name)代替该属性。

    运行程序:

    单击窗体:

    标签控件示例


    先设置了字折行,再设置自动调整大小:

    四、文本框

    1、文本框概述

    文本框(TextBox)主要用于在窗体中显示和接收文本信息。
    在程序运行期间,用户可用鼠标、键盘在文本框中进行文字编辑。
    文本框的默认控件名为Text1、Text2…。

    2、文本框常用属性

    (1)Text

    文本框中显示的文本内容。其值为字符型,是默认属性。

    (2)PassWordChar

    口令字符,设置文本框内容显示形式(仅对单行文本有效) 。 
         设置为空,表示正常显示形式(默认值)
         设置一个字符,比如“*”,表示显示内容均为该字符

    (3)MultiLine

    决定是否能接收和显示多行文本。
         设置为True,表示可接收和显示多行文本

    (4)MaxLength

    文本框中可接收和显示字符的最大长度。
         设置为0,表示字符数无限制

    (5)ScrollBars

    确定文本框是否具有滚动条,只有当MultiLine True时,该属性才有效。
          设置为0,表示无滚动条
          设置为1,表示有水平滚动条
          设置为2,表示有垂直滚动条
          设置为3,表示有水平和垂直两种滚动条

    (6)Locked

    设置是否锁定文本框中的内容。
          设置为True,表示不能改编辑文本框内容

    设置为False,表示可以编辑文本框内容

    (7)SelText、SelLength

    设置选定文本的内容和长度。
         若未选定任何文本,则SelLength的值为0
    (8)SelStart

    设置在文本框中插入点的位置
         设置为0,表示插入点在最左边(默认值)

    3、文本框常用事件

    文本框除了能响应事件Click、DblClick外,还可响应其他事件。
    (1)Change:当Text属性发生变化时,触发该事件。
    (2)GetFocus:当对象获得焦点时,触发该事件。
    (3)LostFocus:当对象失去焦点时,触发该事件。
    (4)KeyPress:在拥有焦点时,按下键盘键并释放则触发该事件。

    4、焦点

    一个窗体上可以添加多个控件,但最多只允许一个控件能够接受键盘输入。这个能处理键盘事件的控件称为“拥有焦点”。原先不拥有焦点的对象,现在能够接受键盘输入了,称为“获得焦点”,同时触发GotFocus事件;反之,则称为“失去焦点”,同时触发LostFocus事件。例如:在用户界面上,若某个文本框中有表示插入点的竖线在闪动,则表示该文本框拥有焦点。

    5、获得焦点的方法

    (1)用鼠标单击窗体上的某个控件,可以使它获得焦点。
    (2)利用键盘上的Tab键,可以使焦点在不同对象之间按TabIndex 属性指定的顺序依次转移。但是,如果某个对象的TabStop属性设置为False,利用Tab键转移焦点时将跳过该对象。

    注意:许多控件是可以拥有焦点的,但Label、Frame、Timer、Image等控件不能拥有焦点。

    (3)要将焦点移到指定的对象上,可使用SetFocus方法。
      SetFocus方法适用于大部分可见控件,其代码格式:<Object.> SetFocus
    例如:将焦点移到文本框Text1上,使用代码为:Text1.SetFocus

    案例1、计算圆周长、圆面积与球体积

    要求计算结果保留二位小数,第三位四舍五入。

    案例2、演示文本框中选定文本

    五、按钮

    1、按钮概述

    命令按钮(CommandButton)是以按钮的形式出现在窗体上。命令按钮是用户以交互方式执行选择操作的主要控件之一,应用十分广泛。在程序运行期间,用鼠标单击命令按钮,会触发该命令按钮的Click事件,进而执行Click事件过程中的代码。命令按钮的默认控件名为以 Command1、Command2、…。

    2、按钮常用属性

    (1)Caption:标题显示的文本内容。其值为字符型,默认属性。

    命令按钮设置快捷键:&+字母
    例如:命令按钮的Caption属性被设置为“清除(&C)”,则命令按钮表面显示为“清除C”。
    当用户按下Alt + C时,相当于选择该按钮。

    (2)Enabled:决定一个对象能否响应用户操作产生的事件。
       设置为True,响应键盘及鼠标事件(默认值)
       设置为False,表面呈灰色,禁止响应键盘及鼠标事件
    (3)Style:设置控件的外观是标准的文本样式,还是图形样式。
       0 - 文本样式(默认值)

       1 - 图形样式,文本和图形均可显示 

    (4)Picture:设置命令按钮表面显示的图形。

        当Style为1时,该属性才起作用
    (5)DownPicture: 当按下鼠标键时,命令按钮表面显示的图形。
        当Style为1时,该属性才起作用。
    (6)DisabledPicture:当命令按钮暂不起作用时,命令按钮显示的图形。

        当Style为1,并且Enabled为False时,该属性才起作用。

    3、按钮常用事件

    命令按钮控件的最主要、最常用的事件是Click事件。当鼠标单击命令按钮时,就会触发Click事件,转而执行Click事件中的程序。


    案例:用户登录(标签、文本框和按钮)


    输入错误的用户名:

    输入正确的用户名,输入错误的密码:

    六、图片框与图像框

    图片框(PictureBox)和图像框(Image)都具有显示图形图像的功能,可以加载到这两个控件上的图形文件有:   位图文件(.bmp)、图标文件(.ico)、JPEG文件、GIF文件。

    1、图片框属性

    (1)Picture:设置在控件内显示的图像,默认属性
    (2)AutoSize:决定图片框控件是否自动调整以适应图片的大小(图片不变)。
      设置为True,则调整自身大小适应图片
      设置为False,则载入图片从图片框的左上角开始显示

    2、图像框属性

    (1)Picture:设置在控件内显示的图像,默认属性
    (2)Stretch:决定图片是否自动调整以适应图像控件的大小(可能会导致图片变形)。
      设置为True,则会自动调整图片的大小将就图像框
      设置为False,则自动调整图像框大小显示完整图片
      利用Stretch属性可以实现图片的放大与缩小。

    3、图像框与图片框的区别

    (1)图片框控件可以作为其他控件的容器,图像框则不可以。
    (2)图片框可以使用Print方法和绘画语句,而图像框则不可以。
    (3)图像框比图片框占用的内存少,显示速度快。
    (4)图像框具有Stretch属性,若设置为True,是保持自身尺寸不变,通过挤压或拉伸载入的图片,使图片装满图像框。图片框具有AutoSize属性,若设置为True,则是通过改变自身的尺寸来适应载入的图片。

    4、图片框和图像框的事件和方法

    图片框可以响应Click、DblClick、Change等事件。图片框的方法与窗体几乎相同:Print、Cls、TextHeight、TextWidth、PSet、Line、Circle。在图片框中调用这些方法时,不能省略对象名,否则会被误认为是在窗体上调用这些方法。

    用Cls方法可以清除Print、PSet、Line、Circle等方法在图片框中输出的文字和图形信息,不能清除Picture属性中的图形,在图片框中加载的图片只能用不带参数的LoadPicture函数清除。
    图像框(Image)虽然也有各种事件,但实际应用中很少涉及到,一般不要求为之编写事件过程代码。

    案例:交换图片

    运行程序:

    单击【加载图片】按钮:

    单击【交换图片】按钮:

    案例:图片框与图像框比较

    图片文件放在工程所在目录:

    编写窗体加载事件过程,设置图片框与图像框的属性,给两个图片框与两个图像框加载图片:

    运行程序,观察效果:

    七、单选按钮与复选框

    复选框(CheckBox)和单选按钮(OptionButton) 是应用程序的用户界面上常用的两类控件。这两类控件单个使用通常是没有意义的,实际应用中总是成组出现。

    复选框:用于从一组选项中选择所需的多个选项。复选框的默认控件名为Check1、Check2…

    单选按钮:只能在一组选项中选择其中的一项。单选按钮的默认控件名为Option1、Option2…

    1、单选按钮与复选框常用属性

    (1)Value:设置或返回选择的状态。复选框和单选按钮的默认属性均为Value。

    (2)Caption:在控件表面显示的文本内容。用法与命令按钮类似。
    (3)Enabled:决定对象的有效性。能否响应用户操作产生的事件。
       True - 控件起作用(默认值)
       False - 在控件的表面显示的文本为灰色(或暗淡字体),此时控件暂不起作用。
    (4)Style设置控件的外观是标准的文本样式,还是图形样式。 用法与命令按钮类似。


    0 - 标准风格 

    1 - 图形风格 控件的Picture、DownPicture和DisabledPicture 属性有效,这样就能像命令按钮一样,用图形代表不同的状态。

    2、单选按钮与复选框常用事件

    单选按钮和复选框的最常用事件是Click事件。

    单选按钮案例演示:

    复选框案例演示:

    八、列表框与组合框

    (一)列表框

    1、列表框概述

    列表框以列表形式显示多个数据项,供用户选择,实现交互操作。
    如果列表中的数据项较多,超过设计时给定的长度,不能一次全部显示,就会自动添加滚动条。
    但是,用户只能从列表中选择所需的数据项,而不能直接修改其中的内容。

    2、列表框常用属性

    (1)Text:存放被选定选项的文本内容。可在程序中引用该属性以获得当前选定的数据项的内容。该属性是只读的,只能在程序运行期间引用。
    (2)
    List:是一个字符串数组,用来存放列表框中的数据选项。List数组下标编号从0开始。
      List (0) 存放第1个数,List (1) 存放第2个数,依此类推……
      List属性可以在设计阶段通过属性窗口设置,也可以在程序运行期间添加、删除或引用。其访问格式为:
      <object>.List(下标) 

    (3)ListCount:表示列表框的选项总数,只能在程序运行期间设置或引用,设计阶段无效。
    (4)
    ListIndex:表示程序运行期间被选定的选项的序号。由于序号是从0开始编起的,因此最后一个选项的序号为ListCount-1,只能在程序运行期间设置或引用,设计阶段无效ListIndex为-1时,表明用户没有选择列表项。
    (5)Sorted:决定程序运行期间列表框的选项是否按字母顺序排列。如果Sorted属性为True,则选项按字母顺序排列显示;如果为False,则按选项加入的先后顺序排列,只能在设计阶段设置。

    (6)MultiSelect 决定用户是否可以在一个控件中做多重选择。0表示不允许;1表示简单复选(用空格或鼠标选取;2表示扩展复选(可用shift+Ctrl等组合键)。该属性必须在设计阶段设置,运行时只能读取该属性。
    (7)
    Selected 是一个逻辑型数组,表示对应的选项在程序运行期间是否被选中。当数组元素的值为True时,表明选择了List属性的对应项;若为False,则表示对应项未被选择。可向Selected属性的某个元素赋值,也可以指定或取消选择。访问格式为:<object>.Selected(下标) = True | False

    (二)组合框

    1、组合框概述

    组合框是一种兼具文本框和列表框特性的控件,是由一个文本框和一个列表框组合而成的,允许用户通过键入文本或选择列表两种方法来进行选择。

    2、组合框三种样式

    组合框常用的属性、方法与列表框类似,组合框有三种不同的样式,由Style属性值决定,组合框的三种样式。 
    Style属性:0 - 下拉组合框、1 - 简单组合框、2 - 下拉列表框

    (三)列表框与组合框常用事件

    列表框能够响应Click和DblClick事件。所有类型的组合框都能响应Click事件,但只有简单组合框(Style属性为1)才能接受DblClick事件。通常情况下,列表框和组合框的主要作用是通过它们的Text属性为应用程序的其它部分提供被选择的信息,程序员一般不需要为列表框和组合框编写事件过程代码。

    列表框案例:选择水果

    1、创建工程,更名为“列表框演示——选择水果”

    2、添加三个按钮

    (1)第一个按钮名称设置为“cmdSelect”,标题设置为“选择(&S)”

    (2)第二个按钮名称设置为“cmdReset”,标题设置为“复位(&R)”

    (3)第三个按钮名称设置为“cmdExit”,标题设置为“退出(&X)”

    3、添加一个列表框

    (1)列表框名称属性设置为“lstFruit”

    (2)列表框MultiSelect属性设置为1——Simple

    (3)列表框Style属性设置为1——Checkbox

    4、添加一个显示用户选择结果的文本框

    (1)文本框名称设置为“txtResult”

    (2)文本框Text属性设置为空

    (3)文本框MultiLine属性设置为True

    (4)文本框ScrollBars属性设置为2——Vertical

    (5)文本框Locked属性设置为True——用户不能编辑

    5、在窗体加载事件里给列表框添加列表项

    6、编写【选择】按钮单击事件处理代码

    7、编写【复位】按钮单击事件处理代码

    8、编写【退出】按钮单击事件处理

    9、运行程序,测试效果

    组合框案例:

    九、滚动条

    滚动条(ScrollBar)在Windows的工作环境中,经常可以见到。
    滚动条的组成:滚动条两端的滚动箭头、滚动条上的滚动块。
    Visual Basic在工具箱中提供:水平滚动条(HScrollBar)、垂直滚动条(VScrollBar)。
    二者只是表现形式不同,其实功能是完全一样的,用户可以根据界面设计的需要选择适当样式的滚动条。

    1、滚动条常用属性

    (1)Value:滚动条中滚动块当前位置的整数值。
    当用户拖动滚动块,或单击滚动条两端的滚动箭头时,Value属性值随之改变。
    在程序中改变Value属性赋值,可改变滚动块的位置。

    (2)Max、Min:设置滚动条所能表示的最大范围(极限取值范围为-32768 ~ +32767)。
    当滚动块位于水平滚动条最右端或垂直滚动条最下端时,Value属性的值即为Max属性的值。
    当滚动块位于水平滚动条最左端或垂直滚动条最上端时,Value属性的值即为Min属性的值。

    (3)LargeChange、SmallChange:设置滚动块滚动时Value属性的增量,默认值均为1。
    LargeChange用来设置当鼠标单击滚动箭头与滚动块之间区域时Value属性的增量。
    SmallChange属性用来设置鼠标单击滚动箭头时Value属性的增量。

    2、滚动条常用事件

    (1)Change:当滚动条中的滚动块被用户改变后,会触发此事件。
        改变滚动块的方法:  拖动滚动块、单击滚动箭头、通过代码对Value属性赋值
    (2)Scroll:当用户在滚动条内拖动滚动块时,会触发此事件。

     


    十、计时器

    计时器(Timer)不仅可以准确地控制Timer事件发生间隔,也可用于显示系统时间。计时器在运行时总是不可见的,所以在设计阶段可放置在窗体的任意位置上,同时,计时器无Visible属性。

    1、计时器属性

    (1)Enabled:决定计时器是否有效。为默认属性。
      设置为True时,计时器控件可触发其Timer事件。 
      设置为False时,计时器控件不会触发其Timer事件。 
    (2)Interval:决定计时器触发其Timer事件的时间间隔。
      时间间隔的单位为毫秒(ms)。
      不为0,则表示触发其Timer事件的时间间隔为多少ms。
      若为0,则表示计时器不起作用,此时与Enabled 为False时的效果一样,不会触发Timer事件。

    2、计时器事件

    计时器控件只在一个Timer事件, 每经过一个由Interval属性指定的时间间隔就自动触发一次Timer事件。

    案例:滚动字幕

    1、创建工程,更名为“滚动字幕”,设置窗体名称与标题

    2、添加一个标签

    (1)标签名称设置为“lblMessage

    (2)标签标题设置为“二级VB培训开始咯!”

    (3)标签AutoSize属性设置为True

    (4)标签Font设置成隶书、小二号

    3、添加两个按钮

    (1)第一个按钮名称设置为“cmdStart”,标题设置为“启动(&S)”

    (2)第二个按钮名称设置为“cmdStop”,标题设置为“停止(&T)”

    4、添加定时器

     

    (1)定时器的Enabled属性设置为False

    (2)定时器的Interval属性设置为100(单位是毫秒)

    5、编写两个按钮单击事件处理代码

    6、编写定时器代码

    7、运行程序,测试效果

    十一、框架

    1、框架概述

    框架(Frame)用于窗体上的对象分组。可把不同的对象放在一个框架中,使其在视觉上进行区分,总体上被激活或被屏蔽。框架实际上是一个容器。所谓容器,就是可以在其上放置其它控件对象的一种对象。容器内的所有控件成为一个组合,随容器一起移动、显示、隐藏等,若将框框架的Visiable属性设置为False,则框架及在它内部的所有控件在程序运行时均不可见。在已介绍的控件对象中,窗体就是一种容器,另外,图片框也是一种容器。通常用框架将每一组单选按钮框起来,使得同一框架内的单选按钮成为一组,每一组单选按钮的操作均不会影响到其它组的单选按钮的选择。

    2、框架常用属性

    (1)Caption:显示位于框架左上角的标题,用于表明框架的作用。
    (2)Enabled:决定框架中的对象是否可用。
      True - 框架及框架中的所有控件在程序运行时均处于可用状态(默认值)
      False - 框架及框架中的所有控件在程序运行时均处于不可用状态

    十二、形状控件

    1、Shape概述

    利用Shape控件可以绘制基本几何图形:矩形、正方形、椭圆、圆、圆角矩形和圆角正方形。

    2、常用属性

    (1)Shape属性

    Shape属性有6个值,为0~5,分别表示矩形、正方形、椭圆形、圆形、圆角矩形和圆角正方形六种形状。默认形状为矩形,值为0。

    (2)BackStyle属性

    0 - Transparent 透明

    1 - Opaque 不透明

    (3)FillStyle属性

    比如FillStyle设置为6,效果如下:

    案例:展示六种基本形状以及交替显示两种图形

    (1)创建工程,更名为“形状控件演示”

    添加一个框架,在框架里添加六个Shape控件,都改名为MyShape,构成控件数组。

    将六个形状控件的Shape属性依次设置为0、1、2、3、4、5。

    在窗体里添加一个形状控件Shape1和一个定时器控件Timer1。

    (2)编写事件处理代码

    (3)运行程序,查看效果

    大家可以看到,交替显示正方形和圆,每秒钟交替一次。

    十三、焦点与Tab顺序

    1、焦点

    一个窗体上可以添加多个控件,但最多只允许一个控件能够接受键盘输入。这个能处理键盘事件的控件称为“拥有焦点”。原先不拥有焦点的对象,现在能够接受键盘输入了,称为“获得焦点”,同时触发GotFocus事件;反之,则称为“失去焦点”,同时触发LostFocus事件。例如:在用户界面上,若某个文本框中有表示插入点的竖线在闪动,则表示该文本框拥有焦点。

    要将焦点移到指定的对象上,可使用SetFocus方法。
    SetFocus方法适用于大部分可见控件,其代码格式:<Object.> SetFocus
    例如:将焦点移到文本框Text1上,使用代码为:Text1.SetFocus

    2、Tab顺序

    利用键盘上的Tab键,可以使焦点在不同对象之间按TabIndex 属性指定的顺序依次转移。但是,如果某个对象的TabStop属性设置为False,利用Tab键转移焦点时将跳过该对象。

    十四、综合案例:个人信息注册

    1、创建工程,修改名称为“个人信息注册”,窗体名称设置为“frmRegistration”,StartUpPosition设置2

    2、添加两个框架Frame1和Frame2

    Frame1的Caption设置为“注册个人信息”,Frame2的Caption设置为空。

    3、在Frame2里添加三个按钮

    (1)修改三个按钮名称

    cmdRegistrate、cmdReset、cmdExit

    (2)修改三个按钮标题

    (3)将注册按钮设置为默认按钮

    4、在框架Frame1里添加一个标签和一个文本框

    (1)标签的标题设置为“用户名:”

    (2)文本框的名称设置为txtUsername,Text属性设置为空,TabIndex设置为0

    5、编写注册按钮cmdRegistrate的单击事件处理代码

    6、运行程序,测试一下效果

    输入一个用户名:张三丰,然后单击【注册】按钮:

    7、添加一个标签和两个单选按钮

    (1)标签的Caption设置为“性别”

    (2)第一个单选按钮名称设置为optMale,Caption设置为“男”,Value属性设置为True

    (3)第二个单选按钮名称设置为optFemale,Caption设置为“女”

    8、修改【注册】按钮单击事件处理代码

    9、运行程序,测试效果

    输入姓名“陈玉莲”,性别设置为“女”:

    10、在框架Frame1里添加一个标签和三个复选框

    (1)标签的AutoSize设置为True,标题设置为“爱好”

    (2)第一个复选框名称设置为“chkBook”,标题设置为“阅读”

    (3)第二个复选框名称设置为“chkTravel”,标题设置为“旅行”

    (4)第三个复选框名称设置为“chkMusic”,标题设置为“音乐”

    11、修改【注册】按钮单击事件处理代码

    12、运行程序,测试效果

    13、在框架Frame1里添加一个标签和一个列表框

    (1)标签的标题设置为“城市”

    (2)列表框的名称设置为“lstCity”

    (3)列表框的list属性设置为“北京,上海,广州,深圳,泸州”

    14、在窗体加载事件Form_Load()里给城市列表控件再添加三个列表项

    15、运行程序,看看城市列表框里是否有8个城市

    16、修改【注册】按钮单击事件处理代码

    对于单选列表框,没有必要通过上述循环嵌套选择的方式来获取用户选择的列表项,只需ListIndex属性即可,但是对于多选列表框,就需要上述方式才能获取用户选择的多个列表项,可以参看前面的列表框案例:选择水果。注意:ListIndex是从0开始的,比如用户选择了列表框的第1项,那么ListIndex为0,用户选择了列表框的第4项,那么ListIndex为3。即ListIndex = 选中的列表项的序号 - 1。0 <= ListIndex <= ListCount - 1。

    代码简化如下:

    17、运行程序,看看效果

    18、在框架Frame1里添加一个标签和一个组合框

    (1)标签的标题设置为“职业”

    (2)组合框的名称设置为“cboCareer”

    (3)设置组合框的List属性,添加4项内容:医生、教师、律师和公务员

    (4)组合框的Text属性设置为“医生”

    (5)组合框的Style属性设置为2——Dropdown List(下拉列表框),只能选择,不能输入

    19、在窗体加载事件里给组合框再添加3项内容

    20、修改【注册】按钮单击事件处理代码

    21、运行程序,测试效果

    22、 编写【重置】按钮单击事件处理代码

    23、编写【退出】按钮单击事件处理代码

    展开全文
  • 深入Windows窗体原理及控件重绘技巧

    万次阅读 多人点赞 2013-12-29 13:46:35
    之前有学MFC的同学告诉我觉得Windows的控件重绘难以理解,就算重绘成功了还是有些地方不明白,我觉得可能很多人都有这样的问题,这里我从Windows窗体的最基本原理来讲解控件的WM_DRAWITEM重绘和子类化重绘,如果你...

    之前有学MFC的同学告诉我觉得Windows的控件重绘难以理解,就算重绘成功了还是有些地方不明白,我觉得可能很多人都有这样的问题,在这里我从Windows窗体的最基本原理来讲解,如果你有类似的疑惑希望这篇文章可以帮你解惑。

    1.Windows窗体原理

    首先,如果看过Win32 SDK编程的都知道Windows的三大核心系统:负责窗口对象产生和消息分发的USER模块,负责图像显示绘制的GDI模块,负责内存、进程、IO管理的KERNEL模块。试想象一下如何在一个像素阵列上产生窗口对象,其实就是使用GDI绘制窗口,不停的以一定的频率刷新显示在屏幕上,这就是图形界面,如果由在DOS或Windows DOS模拟器下编写图形界面的经验这个比较好理解。所以说其实USER模块中的窗口产生是依靠GDI模块的(包括菜单、滚动条等都是使用GDI来绘制的)。

    那么,下面我们就从USER模块和GDI模块来说说Windows 的窗体原理。

    如果接触过Win32 SDK编程的知道一个标准Windows窗体的产生过程:设计窗口类、注册窗口类、创建窗口、显示窗口、启动消息循环泵循环获取消息分发到窗体过程函数处理。为了保证博客的连贯性,在这里我贴上一个标准Windows窗体的产生代码。

    #include <windows.h>
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    {
        static TCHAR szAppName[] = TEXT ("窗口类名称");
        HWND         hwnd;
        MSG          msg;
        WNDCLASSEX   wndclassex = {0};
    
    	//设计窗口类
        wndclassex.cbSize        = sizeof(WNDCLASSEX);
        wndclassex.style         = CS_HREDRAW | CS_VREDRAW;
        wndclassex.lpfnWndProc   = WndProc;
        wndclassex.cbClsExtra    = 0;
        wndclassex.cbWndExtra    = 0;
        wndclassex.hInstance     = hInstance;
        wndclassex.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
        wndclassex.hCursor       = LoadCursor (NULL, IDC_ARROW);
        wndclassex.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
        wndclassex.lpszMenuName  = NULL;
        wndclassex.lpszClassName = szAppName;
        wndclassex.hIconSm       = wndclassex.hIcon;
    	
    	//注册窗口类
        if (!RegisterClassEx (&wndclassex))
        {
            MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR);
            return 0;
        }
    
    	//产生窗口
        hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, 
    		                  szAppName, 
            		          TEXT ("窗口名称"),
                    		  WS_OVERLAPPEDWINDOW,
    		                  CW_USEDEFAULT, 
            		          CW_USEDEFAULT, 
                    		  CW_USEDEFAULT, 
    		                  CW_USEDEFAULT, 
            		          NULL, 
                    		  NULL, 
    		                  hInstance,
            		          NULL); 
    			
    	//显示窗口
        ShowWindow (hwnd, iCmdShow);
        UpdateWindow (hwnd);
    	
    	//启动消息循环泵循环获取消息分配到窗体过程函数处理
        while (GetMessage (&msg, NULL, 0, 0))
        {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        }
    
        return msg.wParam;
    }
    
    //窗体过程函数
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        HDC hdc;
        PAINTSTRUCT ps;
    
        switch (message)
        {
        case WM_CREATE:
            return (0);
    		
        case WM_PAINT:
            hdc = BeginPaint (hwnd, &ps);
            EndPaint (hwnd, &ps);
            return (0);
    		
        case WM_DESTROY:
            PostQuitMessage (0);
            return (0);
        }
    
        return DefWindowProc (hwnd, message, wParam, lParam);
    }

    需要明白的是,所有Windows的窗体及控件归根结底都是使用CreateWindow或CreateWindowEx来创建的,他们都需要标准Windows窗体的产生过程

    普通的窗体好理解,主要需要弄清楚是对话框及控件的产生和消息分派处理流程。

    对话框及其子控件的管理依靠Windows内建的对话框管理器,对话框管理器的工作包括:

    1.根据我们在资源设计器中设计的对话框及子控件产生的.rc文件来自动生成对话框和子控件(如果有手动编写.rc文件的经历的话,知道编写RC文件其实就是指定窗口和子控件大小、类型、样式等参数,对话框管理器将这些参数传入CreateWindow函数产生窗体)

    2.模态对话框直接显示窗体,非模态对话框消息指明WS_VISIBLE属性的话,需要调用ShowWindow来显示窗体。

    4.维护一个消息循环泵,对于模态对话框来说这个消息泵的消息不经过父窗口,所以表现为模态;对于非模态对话框这个消息泵消息经过主窗口,必须由主窗口传给非模态对话框,表现为非模态。

    3.维护一个内建的窗体过程函数,对于对话框来说会处理对话框的关闭打开及子窗口的焦点、tab等,对于子控件也是一样,每个子控件会有自己类型的窗体过程函数,窗体过程函数处理子控件的获得或失去焦点、按下或弹起、创建等表现样式和行为。对于对话框来说,他会开放一个对话框过程函数,让部分消息先通过对话框管理函数处理,如果对话框过程函数不处理才交给默认的内建过程函数处理,对于子控件来说,他们并没有开放过程函数,而是由内建窗体函数将要处理的消息发给父窗口处理。

    那么对话框管理器完成了标准Windows窗体的产生中后半部分工作,至于设计窗口类和注册窗口类这是由Windows自己预先做好了的,如常见的“button”、“listbox”、“edit”类等等。

    一个简要的示意图如下


    那么既然所有的窗体(包括对话框和控件)产生过程一样,那么我们就可以将对话框管理器的部分工作替换掉:

    1.不使用对话框读取.rc模板的方式,直接将参数传递给CreateWindow函数来创建对话框和控件,这就是常见的动态创建控件原理。

    2.设置控件自绘制如BS_OWNDRAW属性,开放控件的WM_DRAWITEM消息给父窗口,由父窗口来绘制按钮样式,这就是常见的控件重绘原理。

    3.替换掉内建的窗体函数,将消息传到自定义的窗体过程函数处理,这就是常见的控件子类化原理。


    下面,为了做演示,先用通用模板创建的方式创建一个模态对话框和其子控件,然后模板创建一个非模态对话框,在非模态对话框中使用动态创建的方式创建和模态对话框中模板创建一样的按钮(当然位置和大小等可能不一样,这里只是为了说明原理故笔者并没有去管这些细节,如果你愿意完全可以把它们做的一模一样)。

    代码太长,这里只贴出部分代码,详细代码请下载演示文件

    主窗口消息泵

        while (GetMessage (&msg, NULL, 0, 0))
        {
    		//注意非模态对话框消息由主窗口分发
    		if (hDlgModeless == NULL || !IsDialogMessage(hDlgModeless, &msg))
    		{
    			TranslateMessage (&msg);
    			DispatchMessage (&msg);
    		}
        }
    主窗口菜单响应

    		case IDM_TEMPLATE:
    			DialogBox(GetWindowLong(hwnd, GWL_HINSTANCE),
    					  IDD_TEMPLATE,
    					  hwnd,
    					  TemplateProc);
    			break;
    		case IDM_CREATE:
    			hDlgModeless = CreateDialog(GetWindowLong(hwnd, GWL_HINSTANCE),
    										 MAKEINTRESOURCE(IDD_CREATE),
    										 hwnd,
    										 CreateProc);
    			ShowWindow(hDlgModeless, SW_NORMAL);//注意非模态对话框不指明WS_VISIBLE属性必须显示调用ShowWindow来显示
    			break;
    模板创建的模态对话框对话框过程函数

    BOOL CALLBACK  TemplateProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch(message)
    	{
    	case WM_CLOSE:
            {
                EndDialog(hDlg,0);
            }
    		return (TRUE);
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case IDCANCEL:
                {
                    SendMessage(hDlg, WM_CLOSE, 0, 0);
                }
                return (TRUE);
    		case IDOK:
                {
                    
                }
    			return (TRUE);
    		}
    		return (FALSE);
    	}
    
    	return (FALSE);
    }

    模板创建的非模态对话框的对话框过程函数

    BOOL CALLBACK  CreateProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch(message)
    	{
    	case WM_INITDIALOG:
    		{
    			//动态创建控件子窗口
    			CreateWindow(TEXT("button"), 
    						 TEXT("确定"), 
    						 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    						 10, 10,
    						 100, 50,
    						 hDlg,
    						 (HMENU)IDOK,
    						 GetWindowLong(hDlg, GWL_HINSTANCE),
    						 NULL);
    			CreateWindow(TEXT("button"), 
    						TEXT("取消"), 
    						WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    						10, 100,
    						100, 50,
    						hDlg,
    						(HMENU)IDCANCEL,
    						GetWindowLong(hDlg, GWL_HINSTANCE),
    						NULL);
    		}
    		return (TRUE);
    
    	case WM_CLOSE:
    		DestroyWindow(hDlg);
    		hDlgModeless = NULL;//注意及时将指针置0防止窗口销毁后消窗口分发消息
    		return (TRUE);
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case IDCANCEL:
    			{
    				SendMessage(hDlg, WM_CLOSE, 0, 0);
    			}
    			return (TRUE);
    		case IDOK:
    			{
    
    			}
    			return (TRUE);
    		}
    		return (FALSE);
    	}
    创建效果

    模态对话框


    非模态对话框

    二者起到了相同的作用,动态创建比模板创建要灵活的多,这个深入学习请自行查找相关资料。上例中需要注意的模态对话框和非模态对话框,前者的消息不流经主窗口消息泵,后者的消息要先流经主窗口消息泵。

    2.控件重绘(WM_DRAWITEM)

    写这篇博文的初衷就是讲解控件重绘原理,自然不能少了这一内容,在刚刚提到了修改对话框管理器的行为的几种方式,后两种(开放WM_DRAWITEM消息和控件子类化)都是常用的控件重绘技巧,在这一节先讲WM_DRAWITEM消息重绘,下一节讲控件子类化重绘,都是以按钮的重绘为例来讲解。
    WM_DRAWITEM顾名思义当控件需要重绘的时候发给主窗口的消息,一般在按钮按下或弹起、获得焦点或失去焦点、创建等时候会产生这一消息,默认是不开启重绘消息的,如果使用模板创建按钮必须在按钮属性中设置OwnDraw属性为True,如果动态创建按钮必须加上BS_OWNDRAW这一属性。
    下面我要重绘两个个按钮,按钮是模板创建的,是默认的IDOK和IDCANCEL按钮,希望达到的效果是
    按钮普通状态分别为

    按钮获得焦点分别为

    按钮按下状态分别为


    下面先贴出绘制部分代码,再讲解
    BOOL CALLBACK SelfDrawProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	LPDRAWITEMSTRUCT pdis;
    	HDC hdc;
    	HDC hMemDc;
    
    	static HINSTANCE hInstance;
    	static HBITMAP hBitmapOK_D;
    	static HBITMAP hBitmapOK_U;
    	static HBITMAP hBitmapOK_F;
    	static HBITMAP hBitmapCANCEL_D;
    	static HBITMAP hBitmapCANCEL_U;
    	static HBITMAP hBitmapCANCEL_F;
    	static HWND	   hwndOk;
    	static HWND    hwndCanel;
    	static BITMAP  bm;
    
    
    	switch(message)
    	{
    	case WM_INITDIALOG:
            {
    			hInstance = GetWindowLong(hDlg, GWL_HINSTANCE);
    			hwndOk = GetDlgItem(hDlg, IDOK);
    			hwndCanel = GetDlgItem(hDlg, IDCANCEL);
    
    			hBitmapOK_D = LoadBitmap(hInstance, TEXT("image1d"));
    			hBitmapOK_U = LoadBitmap(hInstance, TEXT("image1u"));
    			hBitmapOK_F = LoadBitmap(hInstance, TEXT("image1f"));
    			hBitmapCANCEL_D = LoadBitmap(hInstance, TEXT("image2d"));
    			hBitmapCANCEL_U = LoadBitmap(hInstance, TEXT("image2u"));
    			hBitmapCANCEL_F = LoadBitmap(hInstance, TEXT("image2f"));
    
    			GetObject(hBitmapCANCEL_D, sizeof(BITMAP), (PTSTR)&bm);
    
    			//调整按钮大小和最大图片一样大
    			SetWindowPos(hwndOk, HWND_TOPMOST, 0, 0, bm.bmWidth, bm.bmHeight, SWP_NOZORDER | SWP_NOMOVE);
    			SetWindowPos(hwndCanel, HWND_TOPMOST, 0, 0,  bm.bmWidth, bm.bmHeight, SWP_NOZORDER | SWP_NOMOVE);
            }
    		return (TRUE);
    
    	case WM_CLOSE:
            {
                EndDialog(hDlg,0);
            }
    		return (TRUE);
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case IDCANCEL:
                {
                    SendMessage(hDlg, WM_CLOSE, 0, 0);
                }
                return (TRUE);
    		case IDOK:
                {
                    
                }
    			return (TRUE);
    		}
    		return (FALSE);
    	
    	//自绘制按钮
    	case WM_DRAWITEM:
    		//获得绘制结构体,包含绘制的按钮DC和当前按钮状态等
    		pdis = (LPDRAWITEMSTRUCT)lParam;
    			
    		if (pdis->CtlType == ODT_BUTTON)//只绘制button类型
    		{
    			hdc = pdis->hDC;
    			SaveDC(hdc);//保存DC,绘制完必须恢复默认
    
    			//绘制默认状态
    			hMemDc = CreateCompatibleDC(hdc);
    			SelectObject(hMemDc, pdis->CtlID == IDOK ? hBitmapOK_U : hBitmapCANCEL_U);
    			BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);
    			DeleteDC(hMemDc);
    
    			//绘制获取焦点时状态
    			if (pdis->itemState & ODS_FOCUS)
    			{
    				hMemDc = CreateCompatibleDC(hdc);
    				SelectObject(hMemDc, pdis->CtlID == IDOK ? hBitmapOK_F : hBitmapCANCEL_F);
    				BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);
    				DeleteDC(hMemDc);
    			}
    
    			//绘制下压状态
    			if (pdis->itemState & ODS_SELECTED)
    			{
    				hMemDc = CreateCompatibleDC(hdc);
    				SelectObject(hMemDc, pdis->CtlID == IDOK ? hBitmapOK_D : hBitmapCANCEL_D);
    				BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);
    				DeleteDC(hMemDc);
    			}
    			
    			RestoreDC(hdc, -1);
    		}
    
    		return (TRUE);
    	}
    	return (FALSE);
    }
    在WM_INITDIALOG函数中加载相关资源和设置按钮大小
    在WM_DRAWITEM完成主要绘制工作,获得WM_DRAWITEM消息时获得绘制的结构体,这个结构体包括当前要绘制的按钮的ID、状态等,我们主要的工作就是将对应状态的按钮贴上相应的位图即可。
    效果如下
    WM_DRAWITEM消息控件重绘是最常用的重绘技巧,在网上常见的别人封装好的自定义控件都是这样的原理。

    3.控件重绘(控件子类化)

    子类化是借鉴C++的面向对象中的继承和重载的思想,基本意思就是如果子类对消息处理了的话对应C++的重载,这时候父类就没办法再处理这个消息,除非人为的将消息传递给父类,所有的消息先流经子类再到父类,当然这一过程需要子类的配合,具体意思我们用代码来说明。
    同样是达到上一节WM_DRAWITEM绘制的按钮效果
    我们用控件子类化完成这一效果,贴出部分代码,完整代码请下载演示文件
    对话框过程函数
    WNDPROC btnOkOldProc, btnCancelOldProc;
    BOOL CALLBACK SubclassProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static HWND	   hwndOk;
    	static HWND    hwndCanel;
    
    	switch(message)
    	{
    	case WM_INITDIALOG:
            {
    			hwndOk = GetDlgItem(hDlg, IDOK);
    			hwndCanel = GetDlgItem(hDlg, IDCANCEL);
    
    			//窗口子类化
    			btnOkOldProc = SetWindowLong(hwndOk, GWL_WNDPROC, (LONG)BtnProc);
    			btnCancelOldProc = SetWindowLong(hwndCanel, GWL_WNDPROC, (LONG)BtnProc);
            }
    		return (TRUE);
    
    	case WM_CLOSE:
            {
                EndDialog(hDlg,0);
            }
    		return (TRUE);
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case IDCANCEL:
                {
                    SendMessage(hDlg, WM_CLOSE, 0, 0);
                }
                return (TRUE);
    		case IDOK:
                {
                    
                }
    			return (TRUE);
    		}
    		return (FALSE);
    	}
    	return (FALSE);
    }
    按钮过程函数(子类)
    typedef enum tagBUTTONSTATE
    {
    	BTNSTATE_DEFAULT=0,
    	BTNSTATE_FOCUS,
    	BTNSTATE_SELECTED
    }BUTTONSTATE;
    LRESULT CALLBACK  BtnProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        HDC hdc;
    	HDC hMemDc;
        PAINTSTRUCT ps;
    	
    	static int      id;
    	static HINSTANCE hInstance;
    	static HBITMAP hBitmapOK_D;
    	static HBITMAP hBitmapOK_U;
    	static HBITMAP hBitmapOK_F;
    	static HBITMAP hBitmapCANCEL_D;
    	static HBITMAP hBitmapCANCEL_U;
    	static HBITMAP hBitmapCANCEL_F;
    	static BITMAP  bm;
    	static BOOL	   bOnce = TRUE;
    	static BUTTONSTATE btnOkState=BTNSTATE_FOCUS;
    	static BUTTONSTATE btnCancelState=BTNSTATE_DEFAULT;
    
    	id = GetWindowLong(hwnd, GWL_ID);
    
    	//初次进入函数加载资源,模拟WM_CREATE
    	if (TRUE == bOnce)
    	{
    		hInstance = GetWindowLong(hwnd, GWL_HINSTANCE);
    
    		hBitmapOK_D = LoadBitmap(hInstance, TEXT("image1d"));
    		hBitmapOK_U = LoadBitmap(hInstance, TEXT("image1u"));
    		hBitmapOK_F = LoadBitmap(hInstance, TEXT("image1f"));
    		hBitmapCANCEL_D = LoadBitmap(hInstance, TEXT("image2d"));
    		hBitmapCANCEL_U = LoadBitmap(hInstance, TEXT("image2u"));
    		hBitmapCANCEL_F = LoadBitmap(hInstance, TEXT("image2f"));
    
    		GetObject(hBitmapCANCEL_D, sizeof(BITMAP), (PTSTR)&bm);
    		SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, bm.bmWidth, bm.bmHeight, SWP_NOZORDER | SWP_NOMOVE);
    
    		bOnce = FALSE;
    	}
    
        switch (message)
        {
        case WM_CREATE:
    		//注意这个消息不会进入
            return (0);
    		
        case WM_PAINT:
            hdc = BeginPaint (hwnd, &ps);
    
    		hMemDc = CreateCompatibleDC(hdc);
    
    		//绘制不同状态下的按钮样式
    		if (btnOkState == BTNSTATE_DEFAULT && id == IDOK)
    		{
    			SelectObject(hMemDc, hBitmapOK_U);
    		}
    		if(btnCancelState == BTNSTATE_DEFAULT && id==IDCANCEL)
    		{
    			SelectObject(hMemDc, hBitmapCANCEL_U);
    		}
    		if (btnOkState == BTNSTATE_FOCUS && id==IDOK)
    		{
    			SelectObject(hMemDc, hBitmapOK_F);
    		}
    		if(btnCancelState == BTNSTATE_FOCUS && id==IDCANCEL)
    		{
    			SelectObject(hMemDc, hBitmapCANCEL_F);
    		}
    		if (btnOkState == BTNSTATE_SELECTED && id==IDOK)
    		{
    			SelectObject(hMemDc, hBitmapOK_D);
    		}
    		if(btnCancelState == BTNSTATE_SELECTED && id==IDCANCEL)
    		{
    			SelectObject(hMemDc, hBitmapCANCEL_D);
    		}
    
    		BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);
    		DeleteDC(hMemDc);
    
            EndPaint (hwnd, &ps);
            return (0);
    
    	case WM_SETFOCUS:
    		if (id==IDOK)
    		{
    			btnOkState = BTNSTATE_FOCUS;
    		}
    		else
    		{
    			btnCancelState = BTNSTATE_FOCUS;
    		}
    		return (0);
    
    	case WM_KILLFOCUS:
    		if (id==IDOK)
    		{
    			btnOkState = BTNSTATE_DEFAULT;
    		}
    		else
    		{
    			btnCancelState = BTNSTATE_DEFAULT;
    		}
    		return (0);
    
    	case WM_KEYDOWN:
       		if (wParam == VK_SPACE)
    		{
    			if (id==IDOK)
    			{
    				btnOkState = BTNSTATE_SELECTED;
    			}
    			else
    			{
    				btnCancelState = BTNSTATE_SELECTED;
    			}
    
    			InvalidateRect(hwnd, NULL, TRUE);
    		}
     		return (0);
    
    	case WM_KEYUP:
    		if (wParam == VK_SPACE)
    		{
    			if (id==IDOK)
    			{
    				btnOkState = BTNSTATE_FOCUS;
    			}
    			else
    			{
    				btnCancelState = BTNSTATE_FOCUS;
    			}
    
    			InvalidateRect(hwnd, NULL, TRUE);
    		}
    		return (0);
    
    	case WM_LBUTTONDOWN:
    			SetCapture(hwnd);
    			if (id==IDOK)
    			{
    				btnOkState = BTNSTATE_SELECTED;
    			}
    			else
    			{
    				btnCancelState = BTNSTATE_SELECTED;
    			}
    
    			InvalidateRect(hwnd, NULL, TRUE);
    		return (0);
    
    	case WM_LBUTTONUP:
    			if (id==IDOK)
    			{
    				btnOkState = BTNSTATE_FOCUS;
    			}
    			else
    			{
    				btnCancelState = BTNSTATE_FOCUS;
    			}
    
    			InvalidateRect(hwnd, NULL, TRUE);
    			ReleaseCapture();
    		return (0);
    		
        case WM_DESTROY:
            DestroyWindow(hwnd);
            return (0);
        }
    	return CallWindowProc (id == IDOK ? btnOkOldProc : btnCancelOldProc, hwnd, message, wParam, lParam);
    }
    在以上代码,我们在对话框的WM_INITDIALOG消息中强制换掉按钮原有的内建窗体过程函数,使用我们自己的BtnProc过程函数。需要注意的是在我们换掉
    按钮原有的内建窗体过程函数的时候按钮已经创建完成,所以如果我们在BtnProc的WM_CREATE设置断点,程序是不会进入的。和WM_DRAWITEM一样,我们需要按钮的不同状态时绘制,因为我们采用自己的BtnProc过程函数,所以我们只能自己来维护按钮的状态,在WM_PAINT函数中根据不同状态绘制不同样式的按钮,在其他消息中处理按钮的按下或弹起、获得焦点、或失去焦点等状态转变。
    创建效果如下

    我们基本上模拟了WM_DRAWITEM消息重绘效果:按Tab键切换焦点,按Space键按钮按下弹起(当然只是为了演示原理,会有一些Bug,你可以想办法完善他们)。在上诉代码中,我们在最后调用了原来的内建的窗体过程函数,我们处理了WM_PAINT、WM_KEYUP、WM_KEYDOWN等消息,这些消息都return (0)直接返回了,即内建的窗体过程函数没有机会处理这些消息,其他的子类没有处理的消息都传给原来内建的窗体过程函数处理了,如果我们想原来的内建窗体过程函数也处理WM_PAINT,那么将return (0)改成break即可。这就是我上面提到的子类化的实现必须依靠子类化窗体函数的配合,我们也可以将所有的消息都在子类中处理不回传给原来的内建窗口,但是这样的工作量太大,一般是不会这样做的。
    另外,可以看到相比于WM_DRAWITEM消息重绘,子类化实现控件重绘工作量要大得多,当然这样的灵活性要更大。实际上,微软提供子类化的作用更多是为了重新定制子控件的行为,比如说要将一组相同按钮按下时发送一个自定义消息,这时候就可以将这些按钮的消息子类化都先流经一个子类化窗体过程函数,然后再调用内建的窗体过程函数。

    总结来说,一般重绘控件样式使用WM_DRAWITEM消息,重新定制控件行为使用窗体子类化

    博客完整演示代码下载链接
    毕竟现在来说如果不是为了实现一个自绘控件库的话,不会使用SDK自绘的方式,下一次我会讲一下MFC中自绘方式的具体流程和实例。

    原创,转载请注明来自http://blog.csdn.net/wenzhou1219
    展开全文
  • Web窗体的基本控件

    千次阅读 2012-04-26 13:18:42
    第5章 Web窗体的基本控件 与ASP不同的是,ASP.NET提供了大量的控件,这些控件能够轻松的实现一个交互复杂的Web应用功能。传统的ASP开发中,让开发人员最为烦恼的是代码的重用性太低,以及事件代码和页面代码不能...

    第5章  Web窗体的基本控件

    与ASP不同的是,ASP.NET提供了大量的控件,这些控件能够轻松的实现一个交互复杂的Web应用功能。在传统的ASP开发中,让开发人员最为烦恼的是代码的重用性太低,以及事件代码和页面代码不能很好的分开。而在ASP.NET中,控件不仅解决了代码重用性的问题,对于初学者而言,控件还简单易用并能够轻松上手、投入开发。

    5.1  控件的属性

    每个控件都有一些公共属性,例如字体颜色、边框的颜色、样式等。在Visual Studio 2008中,当开发人员将鼠标选择了相应的控件后,属性栏中会简单的介绍该属性的作用。如图5-1所示。

    图5-1  控件的属性

    属性栏用来设置控件的属性,当控件在页面被初始化时,这些将被应用到控件。控件的属性也可以通过编程的方法在页面相应代码区域编写,示例代码如下所示。

           protected void Page_Load(object sender, EventArgs e)

            {

               Label1.Visible = false                                                                                                                  ;//在Page_Load中设置Label1的可见性

            }

    上述代码编写了一个Page_Load(页面加载事件),当页面初次被加载时,会执行Page_Load中的代码。这里通过编程的方法对控件的属性进行更改,当页面加载时,控件的属性会被应用并呈现在浏览器。

    5.2  简单控件

    ASP.NET提供了诸多控件,这些控件包括简单控件、数据库控件、登录控件等强大的控件。在ASP.NET中,简单控件是最基础也是经常被使用的控件,简单控件包括标签控件(Label)、超链接控件(HyperLink)以及图像控件(Image)等。

    5.2.1  标签控件(Label)

    在Web应用中,希望显式的文本不能被用户更改,或者当触发事件时,某一段文本能够在运行时更改,则可以使用标签控件(Label)。开发人员可以非常方便的将标签控件拖放到页面,拖放到页面后,该页面将自动生成一段标签控件的声明代码,示例代码如下所示。

            <asp:LabelID="Label1" runat="server"Text="Label"></asp:Label>

    上述代码中,声明了一个标签控件,并将这个标签控件的ID属性设置为默认值Label1。由于该控件是服务器端控件,所以在控件属性中包含runat=“server”属性。该代码还将标签控件的文本初始化为Label,开发人员能够配置该属性进行不同文本内容的呈现。

    注意:通常情况下,控件的ID也应该遵循良好的命名规范,以便维护。

    同样,标签控件的属性能够在相应的.cs代码中初始化,示例代码如下所示。

           protected void Page_PreInit(object sender, EventArgs e)

            {

               Label1.Text = "Hello World";                                                                                                                                                                                           //标签赋值

            }

    上述代码在页面初始化时为Label1的文本属性设置为“Hello World”。值得注意的是,对于Label标签,同样也可以显式HTML样式,示例代码如下所示。

           protected void Page_PreInit(object sender, EventArgs e)

            {

               Label1.Text = "Hello World<hr/><spanstyle=\"color:red\">A Html Code</span>";         //输出 HTML

               Label1.Font.Size = FontUnit.XXLarge;                                                                                                                                                       //设置字体大小

            }

    上述代码中,Label1的文本属性被设置为一串HTML代码,当Label文本被呈现时,会以HTML效果显式,运行结果如图5-2所示。

    图5-2  Label的Text属性的使用

    如果开发人员只是为了显示一般的文本或者HTML效果,不推荐使用Label控件,因为当服务器控件过多,会导致性能问题。使用静态的HTML文本能够让页面解析速度更快。

    5.2.2  超链接控件(HyperLink)(seen)

    超链接控件相当于实现了HTML代码中的“<a href=“”></a>”效果,当然,超链接控件有自己的特点,当拖动一个超链接控件到页面时,系统会自动生成控件声明代码,示例代码如下所示。

           <asp:HyperLink ID="HyperLink1"runat="server">HyperLink</asp:HyperLink>

    上述代码声明了一个超链接控件,相对于HTML代码形式,超链接控件可以通过传递指定的参数来访问不同的页面。当触发了一个事件后,超链接的属性可以被改变。超链接控件通常使用的两个属性如下所示:

    q     ImageUrl:要显式图像的URL。

    q     NavigateUrl:要跳转的URL。

    1.ImageUrl属性

    设置ImageUrl属性可以设置这个超链接是以文本形式显式还是以图片文件显式,示例代码如下所示。

           <asp:HyperLink ID="HyperLink1" runat="server"

               ImageUrl="http://www.shangducms.com/images/cms.jpg">

                HyperLink

            </asp:HyperLink>

    上述代码将文本形式显示的超链接变为了图片形式的超链接,虽然表现形式不同,但是不管是图片形式还是文本形式,全都实现的相同的效果。

    2.Navigate属性

    Navigate属性可以为无论是文本形式还是图片形式的超链接设置超链接属性,即即将跳转的页面,示例代码如下所示。

           <asp:HyperLink ID="HyperLink1" runat="server"

                ImageUrl="http://www.shangducms.com/images/cms.jpg"

               NavigateUrl="http://www.shangducms.com">

                HyperLink

            </asp:HyperLink>

    上述代码使用了图片超链接的形式。其中图片来自“http://www.shangducms.com/images/cms.jpg”,当点击此超链接控件后,浏览器将跳到URL为“http://www.shangducms.com”的页面。

    3.动态跳转

    在前面的小结讲解了超链接控件的优点,超链接控件的优点在于能够对控件进行编程,来按照用户的意愿跳转到自己跳转的页面。以下代码实现了当用户选择QQ时,会跳转到腾讯网站,如果选择SOHU,则会跳转到SOHU页面,示例代码如下所示。

           protected void DropDownList1_SelectedIndexChanged(object sender,EventArgs e)

            {

               if (DropDownList1.Text == "qq")                                                                                                                                      //如果选择qq

               {

                   HyperLink1.Text = "qq";                                                                                                                                                                //文本为qq

                   HyperLink1.NavigateUrl = "http://www.qq.com";                                                             //URL为qq.com

               }

               else                                                                                                                                                                                                                                     //选择sohu

                {

                   HyperLink1.Text = "sohu";                                                                                                                                        //文本为sohu

                   HyperLink1.NavigateUrl = "http://www.sohu.com";                                                                           //URL为sohu.com

               }

            }

    上述代码使用了DropDownList控件,当用户选择不同的值时,对HyperLink1控件进行操作。当用户选择qq,则为HyperLink1控件配置连接为http://www.qq.com。

    注意:与标签控件相同的是,如果只是为了单纯的实现超链接,同样不推荐使用HyperLink控件,因为过多的使用服务器控件同样有可能造成性能问题。

    5.2.3  图像控件(Image)

    图像控件用来在Web窗体中显示图像,图像控件常用的属性如下:

    q     AlternateText:在图像无法显式时显示的备用文本。

    q     ImageAlign:图像的对齐方式。

    q     ImageUrl:要显示图像的URL。

    当图片无法显示的时候,图片将被替换成AlternateText属性中的文字,ImageAlign属性用来控制图片的对齐方式,而ImageUrl属性用来设置图像连接地址。同样,HTML中也可以使用<img src=“” alt=“”>来替代图像控件,图像控件具有可控性的优点,就是通过编程来控制图像控件,图像控件基本声明代码如下所示。

           <asp:Image ID="Image1" runat="server" />

    除了显示图形以外,Image控件的其他属性还允许为图像指定各种文本,各属性如下所示。

    q     ToolTip:浏览器显式在工具提示中的文本。

    q     GenerateEmptyAlternateText:如果将此属性设置为true,则呈现的图片的alt属性将设置为空。

    开发人员能够为Image控件配置相应的属性以便在浏览时呈现不同的样式,创建一个Image控件也可以直接通过编写HTML代码进行呈现,示例代码如下所示。

            <asp:ImageID="Image1" runat="server"

            AlternateText="图片连接失效" ImageUrl="http://www.shangducms.com/images/cms.jpg"/>

    上述代码设置了一个图片,并当图片失效的时候提示图片连接失效。

    注意:当双击图像控件时,系统并没有生成事件所需要的代码段,这说明Image控件不支持任何事件。

    5.3  文本框控件(TextBox)

    在Web开发中,Web应用程序通常需要和用户进行交互,例如用户注册、登录、发帖等,那么就需要文本框控件(TextBox)来接受用户输入的信息。开发人员还可以使用文本框控件制作高级的文本编辑器用于HTML,以及文本的输入输出。

    5.3.1  文本框控件的属性

    通常情况下,默认的文本控件(TextBox)是一个单行的文本框,用户只能在文本框中输入一行内容。通过修改该属性,则可以将文本框设置为多行/或者是以密码形式显示,文本框控件常用的控件属性如下所示。

    q     AutoPostBack:在文本修改以后,是否自动重传

    q     Columns:文本框的宽度。

    q     EnableViewState:控件是否自动保存其状态以用于往返过程。

    q     MaxLength:用户输入的最大字符数。

    q     ReadOnly:是否为只读。

    q     Rows:作为多行文本框时所显式的行数。

    q     TextMode:文本框的模式,设置单行,多行或者密码。

    q     Wrap:文本框是否换行。

    1.AutoPostBack(自动回传)属性

    在网页的交互中,如果用户提交了表单,或者执行了相应的方法,那么该页面将会发送到服务器上,服务器将执行表单的操作或者执行相应方法后,再呈现给用户,例如按钮控件、下拉菜单控件等。如果将某个控件的AutoPostBack属性设置为true时,则如果该控件的属性被修改,那么同样会使页面自动发回到服务器。

    2.EnableViewState(控件状态)属性

    ViewState是ASP.NET中用来保存Web控件回传状态的一种机制,它是由ASP.NET页面框架管理的一个隐藏字段。在回传发生时,ViewState数据同样将回传到服务器,ASP.NET框架解析ViewState字符串并为页面中的各个控件填充该属性。而填充后,控件通过使用ViewState将数据重新恢复到以前的状态。

    在使用某些特殊的控件时,如数据库控件,来显示数据库。每次打开页面执行一次数据库往返过程是非常不明智的。开发人员可以绑定数据,在加载页面时仅对页面设置一次,在后续的回传中,控件将自动从ViewState中重新填充,减少了数据库的往返次数,从而不使用过多的服务器资源。在默认情况下,EnableViewState的属性值通常为true。

    3.其他属性

    上面的两个属性是比较重要的属性,其他的属性也经常使用。

    q     MaxLength:在注册时可以限制用户输入的字符串长度。

    q     ReadOnly:如果将此属性设置为true,那么文本框内的值是无法被修改的。

    q     TextMode:此属性可以设置文本框的模式,例如单行、多行和密码形式。默认情况下,不设置TextMode属性,那么文本框默认为单行。

    5.3.2  文本框控件的使用

    在默认情况下,文本框为单行类型,同时文本框模式也包括多行和密码,示例代码如下所示。

           <asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

           <br />

           <br />

           <asp:TextBox ID="TextBox2" runat="server"Height="101px" TextMode="MultiLine"

               Width="325px"></asp:TextBox>

           <br />

           <br />

           <asp:TextBox ID="TextBox3" runat="server"TextMode="Password"></asp:TextBox>

    上述代码演示了三种文本框的使用方法,上述代码运行后的结果如图5-3所示。

    图5-3  文本框的三种形式

    文本框无论是在Web应用程序开发还是Windows应用程序开发中都是非常重要的。文本框在用户交互中能够起到非常重要的作用。在文本框的使用中,通常需要获取用户在文本框中输入的值或者检查文本框属性是否被改写。当获取用户的值的时候,必须通过一段代码来控制。文本框控件HTML页面示例代码如下所示。

        <formid="form1" runat="server">

       <div>   

            <asp:Label ID="Label1"runat="server" Text="Label"></asp:Label>

           <br />

           <asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

           <br />

           <asp:Button ID="Button1" runat="server"οnclick="Button1_Click" Text="Button" />

            <br />  

       </div>

       </form>

    上述代码声明了一个文本框控件和一个按钮控件,当用户单击按钮控件时,就需要实现标签控件的文本改变。为了实现相应的效果,可以通过编写cs文件代码进行逻辑处理,示例代码如下所示:

    namespace _5_3                                                                                                                                                                                      //页面命名空间

    {

        publicpartial class _Default : System.Web.UI.Page

        {

           protected void Page_Load(object sender, EventArgs e)      //页面加载时触发

            {

            }

           protected void Button1_Click(object sender, EventArgs e)  //双击按钮时触发的事件

            {

               Label1.Text = TextBox1.Text;                                                                                    //标签控件的值等于文本框中控件的值

            }

        }

    }

    上述代码中,当双击按钮时,就会触发一个按钮事件,这个事件就是将文本框内的值赋值到标签内,运行结果如图5-4所示。

    图5-4  文本框控件的使用

    同样,双击文本框控件,会触发TextChange事件。而当运行时,当文本框控件中的字符变化后,并没有自动回传,是因为默认情况下,文本框的AutoPostBack属性被设置为false。当AutoPostBack属性被设置为true时,文本框的属性变化,则会发生回传,示例代码如下所示。

           protected void TextBox1_TextChanged(object sender, EventArgs e)                                         //文本框事件

            {

               Label1.Text = TextBox1.Text;                                                                                                                                                              //控件相互赋值

            }

    上述代码中,为TextBox1添加了TextChanged事件。在TextChanged事件中,并不是每一次文本框的内容发生了变化之后,就会重传到服务器,这一点和WinForm是不同的,因为这样会大大的降低页面的效率。而当用户将文本框中的焦点移出导致TextBox就会失去焦点时,才会发生重传。

    5.4  按钮控件(Button,LinkButton,ImageButton)

    在Web应用程序和用户交互时,常常需要提交表单、获取表单信息等操作。在这其间,按钮控件是非常必要的。按钮控件能够触发事件,或者将网页中的信息回传给服务器。在ASP.NET中,包含三类按钮控件,分别为Button、LinkButton、ImageButton。

    5.4.1  按钮控件的通用属性

    按钮控件用于事件的提交,按钮控件包含一些通用属性,按钮控件的常用通用属性包括有:

    q     Causes Validation:按钮是否导致激发验证检查。

    q     CommandArgument:与此按钮管理的命令参数。

    q     CommandName:与此按钮关联的命令。

    q     ValidationGroup:使用该属性可以指定单击按钮时调用页面上的哪些验证程序。如果未建立任何验证组,则会调用页面上的所有验证程序。

    下面的语句声明了三种按钮,示例代码如下所示。

           <asp:Button ID="Button1" runat="server"Text="Button" />                                                                              //普通的按钮

           <br />

           <asp:LinkButton ID="LinkButton1"runat="server">LinkButton</asp:LinkButton>                   //Link类型的按钮

           <br />

           <asp:ImageButton ID="ImageButton1" runat="server"/>                                                                                //图像类型的按钮

    对于三种按钮,他们起到的作用基本相同,主要是表现形式不同,如图5-5所示。

    图5-5  三种按钮类型

    5.4.2  Click单击事件

    这三种按钮控件对应的事件通常是Click单击和Command命令事件。在Click单击事件中,通常用于编写用户单击按钮时所需要执行的事件,示例代码如下所示。

           protected void Button1_Click(object sender, EventArgs e)

            {

               Label1.Text = "普通按钮被触发";                                                                                                                                                                 //输出信息

            }

           protected void LinkButton1_Click(object sender, EventArgs e)

            {

               Label1.Text = "连接按钮被触发";                                                                                                                                                                 //输出信息

            }

           protected void ImageButton1_Click(object sender, ImageClickEventArgs e)

            {

               Label1.Text = "图片按钮被触发";                                                                                                                                                                 //输出信息

            }

    上述代码分别为三种按钮生成了事件,其代码都是将Label1的文本设置为相应的文本,运行结果如图5-6所示。

    图5-6  按钮的Click事件

    5.4.3  Command命令事件

    按钮控件中,Click事件并不能传递参数,所以处理的事件相对简单。而Command事件可以传递参数,负责传递参数的是按钮控件的CommandArgument和CommandName属性。如图5-7所示。

    图5-7  CommandArgument和CommandName属性

    将CommandArgument和CommandName属性分别设置为Hello!和Show,单击创建一个Command事件并在事件中编写相应代码,示例代码如下所示。

            protectedvoid Button1_Command(object sender, CommandEventArgs e)

            {

               if (e.CommandName == "Show")                                      //如果CommandNmae属性的值为Show,则运行下面代码

               {

                   Label1.Text = e.CommandArgument.ToString();//CommandArgument属性的值赋值给Label1

                }

            }

    注意:当按钮同时包含Click和Command事件时,通常情况下会执行Command事件。

    Command有一些Click不具备的好处,就是传递参数。可以对按钮的CommandArgument和CommandName属性分别设置,通过判断CommandArgument和CommandName属性来执行相应的方法。这样一个按钮控件就能够实现不同的方法,使得多个按钮与一个处理代码关联或者一个按钮根据不同的值进行不同的处理和响应。相比Click单击事件而言,Command命令事件具有更高的可控性。

    5.5  单选控件和单选组控件(RadioButton和RadioButtonList)

    在投票等系统中,通常需要使用单选控件和单选组控件。顾名思义,在单选控件和单选组控件的项目中,只能在有限种选择中进行一个项目的选择。在进行投票等应用开发并且只能在选项中选择单项时,单选控件和单选组控件都是最佳的选择。

    5.5.1  单选控件(RadioButton)

    单选控件可以为用户选择某一个选项,单选控件常用属性如下所示。

    q     Checked:控件是否被选中。

    q     GroupName:单选控件所处的组名。

    q     TextAlign:文本标签相对于控件的对齐方式。

    单选控件通常需要Checked属性来判断某个选项是否被选中,多个单选控件之间可能存在着某些联系,这些联系通过GroupName进行约束和联系,示例代码如下所示。

           <asp:RadioButton ID="RadioButton1" runat="server"GroupName="choose"

               Text="Choose1" />

           <asp:RadioButton ID="RadioButton2" runat="server"GroupName="choose"

               Text="Choose2" />

    上述代码声明了两个单选控件,并将GroupName属性都设置为“choose”。单选控件中最常用的事件是CheckedChanged,当控件的选中状态改变时,则触发该事件,示例代码如下所示。

           protected void RadioButton1_CheckedChanged(object sender, EventArgs e)

            {

               Label1.Text = "第一个被选中";

            }

           protected void RadioButton2_CheckedChanged(object sender, EventArgs e)

            {

               Label1.Text = "第二个被选中";

            }

    上述代码中,当选中状态被改变时,则触发相应的事件。运行结果如图5-8所示。

    图5-8  单选控件的使用

    与TextBox文本框控件相同的是,单选控件不会自动进行页面回传,必须将AutoPostBack属性设置为true时才能在焦点丢失时触发相应的CheckedChanged事件。

    5.5.2  单选组控件(RadioButtonList)

    与单选控件相同,单选组控件也是只能选择一个项目的控件,而与单选控件不同的是,单选组控件没有GroupName属性,但是却能够列出多个单选项目。另外,单选组控件所生成的代码也比单选控件实现的相对较少。单选组控件添加项如图5-9所示。

    图5-9  单选组控件添加项

    添加项目后,系统自动在.aspx页面声明服务器控件代码,代码如下所示。

           <asp:RadioButtonList ID="RadioButtonList1"runat="server">

               <asp:ListItem>Choose1</asp:ListItem>

               <asp:ListItem>Choose2</asp:ListItem>

               <asp:ListItem>Choose3</asp:ListItem>

           </asp:RadioButtonList>

    上述代码使用了单选组控件进行单选功能的实现,单选组控件还包括一些属性用于样式和重复的配置。单选组控件的常用属性如下所示:

    q     DataMember:在数据集用做数据源时做数据绑定。

    q     DataSource:向列表填入项时所使用的数据源。

    q     DataTextFiled:提供项文本的数据源中的字段。

    q     DataTextFormat:应用于文本字段的格式。

    q     DataValueFiled:数据源中提供项值的字段。

    q     Items:列表中项的集合。

    q     RepeatColumn:用于布局项的列数。

    q     RepeatDirection:项的布局方向。

    q     RepeatLayout:是否在某个表或者流中重复。

    同单选控件一样,双击单选组控件时系统会自动生成该事件的声明,同样可以在该事件中确定代码。当选择一项内容时,提示用户所选择的内容,示例代码如下所示。

           protected void RadioButtonList1_SelectedIndexChanged(object sender,EventArgs e)

            {

               Label1.Text = RadioButtonList1.Text;                                                                    //文本标签段的值等于选择的控件的值

            }

    5.6  复选框控件和复选组控件(CheckBox和CheckBoxList)

    当一个投票系统需要用户能够选择多个选择项时,则单选框控件就不符合要求了。ASP.NET还提供了复选框控件和复选组控件来满足多选的要求。复选框控件和复选组控件同单选框控件和单选组控件一样,都是通过Checked属性来判断是否被选择。

    5.6.1  复选框控件(CheckBox)

    同单选框控件一样,复选框也是通过Check属性判断是否被选择,而不同的是,复选框控件没有GroupName属性,示例代码如下所示。

           <asp:CheckBox ID="CheckBox1" runat="server"Text="Check1" AutoPostBack="true" />

            <asp:CheckBoxID="CheckBox2" runat="server" Text="Check2"  AutoPostBack="true"/>

    上述代码中声明了两个复选框控件。对于复选框空间,并没有支持的GroupName属性,当双击复选框控件时,系统会自动生成方法。当复选框控件的选中状态被改变后,会激发该事件。示例代码如下所示。

           protected void CheckBox1_CheckedChanged(object sender, EventArgs e)

            {

               Label1.Text = "选框1被选中";                                                                                                    //当选框1被选中时

            }

           protected void CheckBox2_CheckedChanged(object sender, EventArgs e)

            {

               Label1.Text = "选框2被选中,并且字体变大";                                                        //当选框2被选中时

               Label1.Font.Size = FontUnit.XXLarge;

            }

    上述代码分别为两个选框设置了事件,设置了当选择选框1时,则文本标签输出“选框1被选中” ,如图5-10所示。当选择选框2时,则输出“选框2被选中,并且字体变大”,运行结果如图5-11所示。

     

    图5-10  选框1被选中                        图5-11  选框2被选中

    对于复选框而言,用户可以在复选框控件中选择多个选项,所以就没有必要为复选框控件进行分组。在单选框控件中,相同组名的控件只能选择一项用于约束多个单选框中的选项,而复选框就没有约束的必要。

    5.6.2  复选组控件(CheckBoxList)

    同单选组控件相同,为了方便复选控件的使用,.NET服务器控件中同样包括了复选组控件,拖动一个复选组控件到页面可以同单选组控件一样添加复选组列表。添加在页面后,系统生成代码如下所示。

           <asp:CheckBoxList ID="CheckBoxList1"runat="server" AutoPostBack="True"

               onselectedindexchanged="CheckBoxList1_SelectedIndexChanged">

               <asp:ListItem Value="Choose1">Choose1</asp:ListItem>

               <asp:ListItem Value="Choose2">Choose2</asp:ListItem>

               <asp:ListItemValue="Choose3">Choose3</asp:ListItem>

           </asp:CheckBoxList>

    上述代码中,同样增加了3个项目提供给用户选择,复选组控件最常用的是SelectedIndexChanged事件。当控件中某项的选中状态被改变时,则会触发该事件。示例代码如下所示。

           protected void CheckBoxList1_SelectedIndexChanged(object sender,EventArgs e)

            {

               if (CheckBoxList1.Items[0].Selected)                                                                                        //判断某项是否被选中

                {

                   Label1.Font.Size = FontUnit.XXLarge;                                                           //更改字体大小

               }

               if (CheckBoxList1.Items[1].Selected)                                                                                        //判断是否被选中

               {

                   Label1.Font.Size = FontUnit.XLarge;                                                              //更改字体大小

               }

               if (CheckBoxList1.Items[2].Selected)

               {

                   Label1.Font.Size = FontUnit.XSmall;

               }

            }

    上述代码中,CheckBoxList1.Items[0].Selected是用来判断某项是否被选中,其中Item数组是复选组控件中项目的集合,其中Items[0]是复选组中的第一个项目。上述代码用来修改字体的大小,如图5-12所示,当选择不同的选项时,字体的大小也不相同,运行结果如图5-13所示。

      

    图5-12  选择大号字体                  图5-13  选择小号字体

    正如图5-12、5-13所示,当用户选择不同的选项时,Label标签的字体的大小会随之改变。

    注意:复选组控件与单选组控件不同的是,不能够直接获取复选组控件某个选中项目的值,因为复选组控件返回的是第一个选择项的返回值,只能够通过Item集合来获取选择某个或多个选中的项目值。

    5.7  列表控件(DropDownList,ListBox和BulletedList)

    在Web开发中,经常会需要使用列表控件,让用户的输入更加简单。例如在用户注册时,用户的所在地是有限的集合,而且用户不喜欢经常键入,这样就可以使用列表控件。同样列表控件还能够简化用户输入并且防止用户输入在实际中不存在的数据,如性别的选择等。

    5.7.1  DropDownList列表控件

    列表控件能在一个控件中为用户提供多个选项,同时又能够避免用户输入错误的选项。例如,在用户注册时,可以选择性别是男,或者女,就可以使用DropDownList列表控件,同时又避免了用户输入其他的信息。因为性别除了男就是女,输入其他的信息说明这个信息是错误或者是无效的。下列语句声明了一个DropDownList列表控件,示例代码如下所示。

           <asp:DropDownList ID="DropDownList1"runat="server">

               <asp:ListItem>1</asp:ListItem>

               <asp:ListItem>2</asp:ListItem>

               <asp:ListItem>3</asp:ListItem>

                <asp:ListItem>4</asp:ListItem>

               <asp:ListItem>5</asp:ListItem>

               <asp:ListItem>6</asp:ListItem>

               <asp:ListItem>7</asp:ListItem>

           </asp:DropDownList>

    上述代码创建了一个DropDownList列表控件,并手动增加了列表项。同时DropDownList列表控件也可以绑定数据源控件。DropDownList列表控件最常用的事件是SelectedIndexChanged,当DropDownList列表控件选择项发生变化时,则会触发该事件,示例代码如下所示。

           protected void DropDownList1_SelectedIndexChanged1(object sender,EventArgs e)

            {

               Label1.Text = "你选择了第" +DropDownList1.Text + "项";

            }

    上述代码中,当选择的项目发生变化时则会触发该事件,如图5-14所示。当用户再次进行选择时,系统会将更改标签1中的文本,如图5-15所示。

      

    图5-14  选择第三项                        5-15  选择第一项

    当用户选择相应的项目时,就会触发SelectedIndexChanged事件,开发人员可以通过捕捉相应的用户选中的控件进行编程处理,这里就捕捉了用户选择的数字进行字体大小的更改。

    5.7.2  ListBox列表控件

    相对于DropDownList控件而言,ListBox控件可以指定用户是否允许多项选择。设置SelectionMode属性为Single时,表明只允许用户从列表框中选择一个项目,而当SelectionMode属性的值为Multiple时,用户可以按住Ctrl键或者使用Shift组合键从列表中选择多个数据项。当创建一个ListBox列表控件后,开发人员能够在控件中添加所需的项目,添加完成后示例代码如下所示。

           <asp:ListBox ID="ListBox1" runat="server"Width="137px" AutoPostBack="True">

               <asp:ListItem>1</asp:ListItem>

               <asp:ListItem>2</asp:ListItem>

               <asp:ListItem>3</asp:ListItem>

               <asp:ListItem>4</asp:ListItem>

               <asp:ListItem>5</asp:ListItem>

               <asp:ListItem>6</asp:ListItem>

           </asp:ListBox>

    从结构上看,ListBox列表控件的HTML样式代码和DropDownList控件十分相似。同样,SelectedIndexChanged也是ListBox列表控件中最常用的事件,双击ListBox列表控件,系统会自动生成相应的代码。同样,开发人员可以为ListBox控件中的选项改变后的事件做编程处理,示例代码如下所示。

           protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)

            {

                Label1.Text = "你选择了第" + ListBox1.Text + "项";

            }

    上述代码中,当ListBox控件选择项发生改变后,该事件就会被触发并修改相应Label标签中文本,如图5-16所示。

    上面的程序同样实现了DropDownList中程序的效果。不同的是,如果需要实现让用户选择多个ListBox项,只需要设置SelectionMode属性为“Multiple”即可,如图5-17所示。

     

    图5-16  ListBox单选                        图5-17  SelectionMode属性

    当设置了SelectionMode属性后,用户可以按住Ctrl键或者使用Shift组合键选择多项。同样,开发人员也可以编写处理选择多项时的事件,示例代码如下所示。

           protected void ListBox1_SelectedIndexChanged1(object sender, EventArgse)

            {

               Label1.Text += ",你选择了第" + ListBox1.Text+ "项";

            }

    上述代码使用了“+=”运算符,在触发SelectedIndexChanged事件后,应用程序将为Label1标签赋值,如图5-18所示。当用户每选一项的时候,就会触发该事件,如图5-19所示。

      

    图5-18  单选效果                                图5-19  多选效果

    从运行结果可以看出,当单选时,选择项返回值和选择的项相同,而当选择多项的时候,返回值同第一项相同。所以,在选择多项时,也需要使用Item集合获取和遍历多个项目。

    5.7.3  BulletedList列表控件

    BulletedList与上述列表控件不同的是,BulleteList控件可呈现项目符号或编号。对BulleteList属性的设置为呈现项目符号,则当BulletedList被呈现在页面时,列表前端会则会显式项目符号或者特殊符号,效果如图5-20所示。

    图5-20  BulletedList显式效果

    BulletedList可以通过设置BulletStyle属性来编辑列表前的符号样式,常用的BulletStyle项目符号编号样式如下所示。

    q     Circle:项目符号设置为○。

    q     CustomImage:项目符号为自定义图片。

    q     Disc:项目符号设置为●。

    q     LowerAlpha:项目符号为小写字母格式,如a、b、c等。

    q     LowerRoman:项目符号为罗马数字格式,如i、ii等。

    q     NotSet:表示不设置,此时将以Disc样式为默认样式。

    q     Numbered:项目符号为1、2、3、4等。

    q     Square:项目符号为黑方块■。

    q     UpperAlpha:项目符号为大写字母格式,如A、B、C等。

    q     UpperRoman:项目符号为大写罗马数字格式如Ⅰ、Ⅱ、Ⅲ等。

    同样,BulletedList控件也同DropDownList以及ListBox相同,可以添加事件。不同的是生成的事件是Click事件,代码如下所示。

           protected void BulletedList1_Click(object sender, BulletedListEventArgse)

            {

               Label1.Text += ",你选择了第" +BulletedList1.Items[e.Index].ToString() + "项";

            }

    DropDownList和ListBox生成的事件是SelectedIndexChanged,当其中的选择项被改变时,则触发该事件。而BulletedList控件生成的事件是Click,用于在其中提供逻辑以执行特定的应用程序任务。

    5.8  面板控件(Panel)

    面板控件就好像是一些控件的容器,可以将一些控件包含在面板控件内,然后对面板控制进行操作来设置在面板控件内的所有控件是显示还是隐藏,从而达到设计者的特殊目的。当创建一个面板控件时,系统会生成相应的HTML代码,示例代码如下所示。

        <asp:PanelID="Panel1" runat="server">

       </asp:Panel>

    面板控件的常用功能就是显示或隐藏一组控件,示例HTML代码如下所示。

        <formid="form1" runat="server">

            <asp:Button ID="Button1"runat="server" Text="Show" />

            <asp:PanelID="Panel1" runat="server" Visible="False">

                <asp:LabelID="Label1" runat="server" Text="Name:"style="font-size: xx-large"></asp:Label>

                <asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

                <br />

                This is a Panel!

            </asp:Panel>

       </form>

    上述代码创建了一个Panel控件, Panel控件默认属性为隐藏,并在控件外创建了一个Button控件Button1,当用户单击外部的按钮控件后将显示Panel控件,cs代码如下所示。

           protected void Button1_Click(object sender, EventArgs e)

            {

               Panel1.Visible = true;                                                                                                                                     //Panel控件显示可见

            }

    当页面初次被载入时,Panel控件以及Panel控件内部的服务器控件都为隐藏,如图5-21所示。当用户单击Button1时,则Panel控件可见性为可见,则页面中的Panel控件以及Panel控件中的所有服务器控件也都为可见,如图5-22所示。

     

    图5-21  Panel控件隐藏                                  图5-22  Panel被显示

    将TextBox控件和Button控件放到Panel控件中,可以为Panel控件的DefaultButton属性设置为面板中某个按钮的ID来定义一个默认的按钮。当用户在面板中输入完毕,可以直接按Enter键来传送表单。并且,当设置了Panel控件的高度和宽度时,当Panel控件中的内容高度或宽度超过时,还能够自动出现滚动条。

    Panel控件还包含一个GroupText属性,当Panel控件的GroupText属性被设置时,Panel将会被创建一个带标题的分组框,效果如图5-23所示。

    图5-23  Panel控件的GroupText属性

    GroupText属性能够进行Panel控件的样式呈现,通过编写GroupText属性能够更加清晰的让用户了解Panel控件中服务器控件的类别。例如当有一组服务器用于填写用户的信息时,可以将Panel控件的GroupText属性编写成为“用户信息”,让用户知道该区域是用于填写用户信息的。

    5.9  占位控件(PlaceHolder)

    在传统的ASP开发中,通常在开发页面的时候,每个页面有很多相同的元素,例如导航栏、GIF图片等。使用ASP进行应用程序开发通常使用include语句在各个页面包含其他页面的代码,这样的方法虽然解决了相同元素的很多问题,但是代码不够美观,而且时常会出现问题。ASP.NET中可以使用PlaceHolder来解决这个问题,与面板控件Panel控件相同的是,占位控件PlaceHolder也是控件的容器,但是在HTML页面呈现中本身并不产生HTML,创建一个PlaceHolder控件代码如下所示。

           <asp:PlaceHolder ID="PlaceHolder1"runat="server"></asp:PlaceHolder>

    在CS页面中,允许用户动态的在PlaceHolder上创建控件,CS页面代码如下所示。

           protected void Page_Load(object sender, EventArgs e)

            {

               TextBox text = new TextBox();                                                                                   //创建一个TextBox对象

               text.Text = "NEW";

               this.PlaceHolder1.Controls.Add(text);                                                                    //为占位控件动态增加一个控件

            }

    上述代码动态的创建了一个TextBox控件并显示在占位控件中,运行效果如图5-24所示。

    图5-24  PlaceHolder控件的使用

    开发人员不仅能够通过编程在PlaceHolder控件中添加控件,开发人员同样可以在PlaceHolder控件中拖动相应的服务器控件进行控件呈现和分组。

    5.10  日历控件(Calendar)

    在传统的Web开发中,日历是最复杂也是最难实现的功能,好在ASP.NET中提供了强大的日历控件来简化日历控件的开发。日历控件能够实现日历的翻页、日历的选取以及数据的绑定,开发人员能够在博客、OA等应用的开发中使用日历控件从而减少日历应用的开发。

    5.10.1  日历控件的样式

    日历控件通常在博客、论坛等程序中使用,日历控件不仅仅只是显式了一个日历,用户还能够通过日历控件进行时间的选取。在ASP.NET中,日历控件还能够和数据库进行交互操作,实现复杂的数据绑定。开发人员能够将日历控件拖动在主窗口中,在主窗口的代码视图下会自动生成日历控件的HTML代码,示例代码如下所示。

           <asp:Calendar ID="Calendar1"runat="server"></asp:Calendar>

    ASP.NET通过上述简单的代码就创建了一个强大的日历控件,其效果如图5-25所示。

    图5-25  日历控件

    日历控件通常用于显示月历,日历控件允许用户选择日期和移动到下一页或上一页。通过设置日历控件的属性,可以更改日历控件的外观。常用的日历控件的属性如下所示:

    q     DayHeaderStype:月历中显示一周中每一天的名称和部分的样式。

    q     DayStyle:所显示的月份中各天的样式。

    q     NextPrevStyle:标题栏左右两端的月导航所在部分的样式。

    q     OtherMonthDayStyle:上一个月和下一个月的样式。

    q     SelectedDayStyle:选定日期的样式。

    q     SelectorStyle:位于月历控件左侧,包含用于选择一周或整个月的连接的列样式。

    q     ShowDayHeader:显示或隐藏一周中的每一天的部分。

    q     ShowGridLines:显示或隐藏一个月中的每一天之间的网格线。

    q     ShowNextPrevMonth:显示或隐藏到下一个月或上一个月的导航控件。

    q     ShowTitle:显示或隐藏标题部分。

    q     TitleStyle:位于月历顶部,包含月份名称和月导航连接的标题栏样式。

    q     TodayDayStyle:当前日期的样式。

    q     WeekendDayStyle:周末日期的样式。

    Visual Studio还为开发人员提供了默认的日历样式从而能够选择自动套用格式进行样式控制,如图5-26所示。

    图5-26  使用系统样式

    除了上述样式可以设置以外,ASP.NET还为用户设计了若干样式,若开发人员觉得设置样式非常困难,则可以使用系统默认的样式进行日历控件的样式呈现。

    5.10.2  日历控件的事件(seen2)

    同所有的控件相同,日历控件也包含自身的事件,常用的日历控件的事件包括有:

    q     DayRender:当日期被显示时触发该事件。

    q     SelectionChanged:当用户选择日期时触发该事件。

    q     VisibleMonthChanged:当所显示的月份被更改时触发该事件。

    在创建日历控件中每个日期单元格时,则会触发DayRender事件。当用户选择月历中的日期时,则会触发SelectionChanged事件,同样,当双击日历控件时,会自动生成该事件的代码块。当对当前月份进行切换,则会激发VisibleMonthChanged事件。开发人员可以通过一个标签来接受当前事件,当选择月历中的某一天,则此标签显示当前日期,示例代码如下所示。

           protected void Calendar1_SelectionChanged(object sender, EventArgs e)

            {

               Label1.Text =

                "现在的时间是:" + Calendar1.SelectedDate.Year.ToString() + "年"

                +Calendar1.SelectedDate.Month.ToString()+"月"

                +Calendar1.SelectedDate.Day.ToString()+"号"

                +Calendar1.SelectedDate.Hour.ToString()+"点";

            }

    在上述代码中,当用户选择了月历中的某一天时,则标签中的文本会变为当前的日期文本,如“现在的时间是xx”之类。在进行逻辑编程的同时,也需要对日历控件的样式做稍许更改,日历控件的HTML代码如下所示。

           <asp:Calendar ID="Calendar1" runat="server"BackColor="#FFFFCC"

               BorderColor="#FFCC66" BorderWidth="1px"DayNameFormat="Shortest"

               Font-Names="Verdana" Font-Size="8pt" ForeColor="#663399"Height="200px"

               onselectionchanged="Calendar1_SelectionChanged"ShowGridLines="True"

               Width="220px">

                        <SelectedDayStyleBackColor="#CCCCFF" Font-Bold="True" />

                        <SelectorStyleBackColor="#FFCC66" />

                        <TodayDayStyleBackColor="#FFCC66" ForeColor="White" />

                        <OtherMonthDayStyle ForeColor="#CC9966"/>

                        <NextPrevStyle Font-Size="9pt" ForeColor="#FFFFCC"/>

                        <DayHeaderStyleBackColor="#FFCC66" Font-Bold="True" Height="1px"/>

                        <TitleStyleBackColor="#990000" Font-Bold="True" Font-Size="9pt"

                        ForeColor="#FFFFCC"/>

           </asp:Calendar>

    上述代码中的日历控件选择的是ASP.NET的默认样式,如图5-27所示。当确定了日历控件样式后,并编写了相应的SelectionChanged事件代码后,就可以通过日历控件获取当前时间,或者对当前时间进行编程,如图5-28所示。

     

    图5-27  日历控件                         图5-28  选择一个日期

    5.11  广告控件(AdRotator)

    在Web应用开发中,广告总是必不可少的。而ASP.NET为开发人员提供了广告控件为页面在加载时提供一个或一组广告。广告控件可以从固定的数据源中读取(如XML或数据源控件),并从中自动读取出广告信息。当页面每刷新一次时,广告显示的内容也同样会被刷新。

    广告控件必须放置在Form或Panel控件,以及模板内。广告控件需要包含图像的地址的XML文件。并且该文件用来指定每个广告的导航连接。广告控件最常用的属性就是AdvertisementFile,使用它来配置相应的XML文件,所以必须首先按照标准格式创建一个XML文件,如图5-29所示。

    图5-29  创建一个XML文件

    创建了XML文件之后,开发人员并不能按照自己的意愿进行XML文档的编写,如果要正确的被广告控件解析形成广告,就需要按照广告控件要求的标准的XML格式来编写代码,示例代码如下所示。

    <?xml version="1.0"encoding="utf-8" ?>

    <Advertisements>

     [<Ad>

     <ImageUrl></ImageUrl>

     <NavigateUrl></NavigateUrl>

     [<OptionalImageUrl></OptionalImageUrl>]*

     [<OptionalNavigateUrl></OptionalNavigateUrl>]*

     <AlternateText></AlternateText>

     <Keyword></Keyword>

     <Impression></Impression>

     </Ad>]*

    </Advertisements>

    上述代码实现了一个标准的广告控件的XML数据源格式,其中各标签意义如下所示:

    q     ImageUrl:指定一个图片文件的相对路径或绝对路径,当没有ImageKey元素与OptionalImageUrl匹配时则显示该图片。

    q     NavigateUrl:当用户单击广告时单没有NaivigateUrlKey元素与OptionalNavigateUrl元素匹配时,会将用户发送到该页面。

    q     OptionalImageUrl:指定一个图片文件的相对路径或绝对路径,对于ImageKey元素与OptionalImageUrl匹配时则显示该图片。

    q     OptionalNavigateUrl:当用户单击广告时单有NaivigateUrlKey元素与OptionalNavigateUrl元素匹配时,会将用户发送到该页面。

    q     AlternateText:该元素用来替代IMG中的ALT元素。

    q     KeyWord:KeyWord用来指定广告的类别。

    q     Impression:该元素是一个数值,指示轮换时间表中该广告相对于文件中的其他广告的权重。

    当创建了一个XML数据源之后,就需要对广告控件的AdvertisementFile进行更改,如图5-30所示。

    图5-30  指定相应的数据源

    配置好数据源之后,就需要在广告控件的数据源XML文件中加入自己的代码了,XML广告文件示例代码如下所示。

    <?xml version="1.0"encoding="utf-8" ?>

    <Advertisements>

      <Ad>

     <ImageUrl>http://www.shangducms.com/images/cms.jpg</ImageUrl>

     <NavigateUrl>http://www.shangducms.com</NavigateUrl>

     <AlternateText>我的网站</AlternateText>

     <Keyword>software</Keyword>

     <Impression>100</Impression>

     </Ad>

      <Ad>

       <ImageUrl>http://www.shangducms.com/images/hello.jpg</ImageUrl>

       <NavigateUrl>http://www.shangducms.com</NavigateUrl>

       <AlternateText>我的网站</AlternateText>

       <Keyword>software</Keyword>

       <Impression>100</Impression>

     </Ad>

    </Advertisements>

    运行程序,广告对应的图像在页面每次加载的时候被呈现,如图5-31所示。页面每次刷新时,广告控件呈现的广告内容都会被刷新,如图5-32所示。

     

    图5-31  一个广告被呈现               图5-32  刷新后更换广告内容

    注意:广告控件本身并不提供点击统计,所以无法计算广告是否被用户点击或者统计用户最关心的广告。

    5.12  文件上传控件(FileUpload)

    在网站开发中,如果需要加强用户与应用程序之间的交互,就需要上传文件。例如在论坛中,用户需要上传文件分享信息或在博客中上传视频分享快乐等等。上传文件在ASP中是一个复杂的问题,可能需要通过组件才能够实现文件的上传。在ASP.NET中,开发环境默认的提供了文件上传控件来简化文件上传的开发。当开发人员使用文件上传控件时,将会显示一个文本框,用户可以键入或通过“浏览”按键浏览和选择希望上传到服务器的文件。创建一个文件上传控件系统生成的HTML代码如下所示。

           <asp:FileUpload ID="FileUpload1" runat="server"/>

    文件上传控件可视化设置属性较少,大部分都是通过代码控制完成的。当用户选择了一个文件并提交页面后,该文件作为请求的一部分上传,文件将被完整的缓存在服务器内存中。当文件完成上传,页面才开始运行,在代码运行的过程中,可以检查文件的特征,然后保存该文件。同时,上传控件在选择文件后,并不会立即执行操作,需要其他的控件来完成操作,例如按钮控件(Button)。实现文件上传的HTML核心代码如下所示。

    <body>

        <form id="form1"runat="server">

            <div>

                <asp:FileUploadID="FileUpload1" runat="server" />

                <asp:Button ID="Button1"runat="server" Text="选择好了,开始上传"/> 

            </div>

            </form>

    </body>

    上述代码通过一个Button控件来操作文件上传控件,当用户单击按钮控件后就能够将上传控件中选中的控件上传到服务器空间中,示例代码如下所示。

           protected void Button1_Click(object sender, EventArgs e)

            {

               FileUpload1.PostedFile.SaveAs(Server.MapPath("upload/beta.jpg"));                                                //上传文件另存为

            }

    上述代码将一个文件上传到了upload文件夹内,并保存为jpg格式,如图5-33所示。打开服务器文件,可以看到文件已经上传了,如图5-34所示。

     

    图5-33  上传文件                      图5-34  文件已经被上传

    上述代码将文件保存在UPLOAD文件夹中,并保存为JPG格式。但是通常情况下,用户上传的并不全部都是JPG格式,也有可能是DOC等其他格式的文件,在这段代码中,并没有对其他格式进行处理而全部保存为了JPG格式。同时,也没有对上传的文件进行过滤,存在着极大的安全风险,开发人员可以将相应的文件上传的cs更改,以便限制用户上传的文件类型,示例代码如下所示。

           protected void Button1_Click(object sender, EventArgs e)

            {

               if (FileUpload1.HasFile)                                                                                                                                                                                                  //如果存在文件

               {

                   string fileExtension =System.IO.Path.GetExtension(FileUpload1.FileName);//获取文件扩展名

                   if (fileExtension != ".jpg")                                                                                                                                           //如果扩展名不等于jpg时

                   {

                       Label1.Text = "文件上传类型不正确,请上传jpg格式";                      //提示用户重新上传

                   }

                   else

                   {

                       FileUpload1.PostedFile.SaveAs(Server.MapPath("upload/beta.jpg"));              //文件保存

                       Label1.Text = "文件上传成功";                                                                                                                                            //提示用户成功

                   }

               }

            }

    上述代码中决定了用户只能上传JPG格式,如果用户上传的文件不是JPG格式,那么用户将被提示上传的文件类型有误并停止用户的文件上传,如果文件的类型为JPG格式,用户就能够上传文件到服务器的相应目录中,如图5-35所示。运行上传控件进行文件上传,运行结果如图5-36所示。

     

    图5-35  文件类型错误                          图5-36  文件类型正确

    值得注意的是,上传的文件在.NET中,默认上传文件最大为4M左右,不能上传超过该限制的任何内容。当然,开发人员可以通过配置.NET相应的配置文件来更改此限制,但是推荐不要更改此限制,否则可能造成潜在的安全威胁。

    注意:如果需要更改默认上传文件大小的值,通常可以直接修改存放在C:\WINDOWS\Microsoft.NET\FrameWork\V2.0.50727\CONFIG的ASP.NET 2.0配置文件,通过修改文件中的maxRequestLength标签的值,或者可以通过web.config来覆盖配置文件。

    5.13  视图控件(MultiView和View)

    视图控件很像在WinForm开发中的TabControl控件,在网页开发中,可以使用MultiView控件作为一个或多个View控件的容器,让用户体验得到更大的改善。在一个MultiView控件中,可以放置多个View控件(选项卡),当用户点击到关心的选项卡时,可以显示相应的内容,很像Visual Studio 2008中的设计、视图、拆分等类型的功能。

    无论是MultiView还是View,都不会在HTML页面中呈现任何标记。而MultiView控件和View没有像其他控件那样多的属性,惟一需要指定的就是ActiveViewIndex属性,视图控件HTML代码如下所示。

       <asp:MultiView ID="MultiView1" runat="server"ActiveViewIndex="0">

            <asp:View ID="View1"runat="server">

                abc<br />

                   <asp:Button ID="Button1" runat="server"CommandArgument="View2"

                       CommandName="SwitchViewByID" Text="下一个" />

               </asp:View>

            <asp:View ID="View2"runat="server">

                123<br />

                <asp:Button ID="Button2"runat="server" CommandArgument="View1"

                    CommandName="SwitchViewByID"Text="上一个" />

            </asp:View>

        </asp:MultiView>

    上述代码中,使用了Button来对视图控件进行选择,通过单击按钮,来选择替换到【下一个】或者是【上一个】按钮,如图5-37所示。在用户注册中,这一步能够制作成Web向导,让用户更加方便的使用Web应用。当标签显式完毕后,会显式上一步按钮5-38所示。

     

    图5-37  第一个标签                            图5-38  第二个标签

    注意:在HTML代码中,并没有为每个按钮的事件编写代码,是因为按钮通过CommandArgument和CommandName属性操作视图控件。

    MultiView和View控件能够实现Panel控件的任务,但可以让用户选择其他条件。同时MultiView和View能够实现Wizard控件相似的行为,并且可以自己编写实现细节。相比之下,当不需要使用Wizard提供的方法时,可以使用MultiView和View控件来代替,并且编写过程更加“可视化”,如图5-39所示。

    图5-39  为每个View编写不同的应用

    MultiView和View控件也可以实现导航效果,可以通过编程指定MultiView的ActiveViewIndex属性显示相应的View控件。

    注意:在MultiView控件中,第一个被放置的View控件的索引为0而不是1,后面的View控件的索引依次递增。

    5.14  表控件(Table)

    在ASP.NET中,也提供了表控件(Table)来提供可编程的表格服务器控件。表中的行可以通过TableRow创建,而表中的列通过TableCell来实现,当创建一个表控件时,系统生成代码如下所示。

        <asp:Table ID="Table1"runat="server" Height="121px" Width="177px">

        </asp:Table>

    上述代码自动生成了一个表控件代码,但是没有生成表控件中的行和列,必须通过TableRow创建行,通过TableCell来创建列,示例代码如下所示。

        <asp:Table ID="Table1"runat="server" Height="121px" Width="177px">

        <asp:TableRow>

         <asp:TableCell>1.1</asp:TableCell>

        <asp:TableCell>1.2</asp:TableCell>

         <asp:TableCell>1.3</asp:TableCell>

         <asp:TableCell>1.4</asp:TableCell>

        </asp:TableRow>

        <asp:TableRow>

         <asp:TableCell>2.1</asp:TableCell>

         <asp:TableCell>2.2</asp:TableCell>

        <asp:TableCell>2.3</asp:TableCell>

         <asp:TableCell>2.4</asp:TableCell>

        </asp:TableRow>

        </asp:Table>

    上述代码创建了一个两行四列的表,如图5-40所示。

    图5-40  表控件

    父Table控件支持一些控制整个表的外观的属性,例如字体、背景颜色等,如图5-41所示。TableRow控件和TableCell控件也支持这些属性,同样可以用来指定个别的行或单元格的外观,运行后如图5-42所示。

     

    图5-41  Table的属性设置                     图5-42  TableCell控件的属性设置

    表控件和静态表的区别在于,表控件能够动态的为表格创建行或列,实现一些特定的程序需求。Web服务器控件中,Table控件中的行是TableRow对象,Table控件中的列是TableCell对象。可以声明这两个对象并初始化,可以为表控件增加行或列,实现动态创建表的程序,HTML核心代码如下所示。

    <body style="font-style: italic">

        <formid="form1" runat="server">

       <div>

           <asp:Table ID="Table1" runat="server"Height="121px" Width="177px"

               BackColor="Silver">

           <asp:TableRow>

            <asp:TableCell>1.1</asp:TableCell>

            <asp:TableCell>1.2</asp:TableCell>

            <asp:TableCell>1.3</asp:TableCell>

            <asp:TableCellBackColor="White">1.4</asp:TableCell>

           </asp:TableRow>

           <asp:TableRow>

            <asp:TableCell>2.1</asp:TableCell>

            <asp:TableCellBackColor="White">2.2</asp:TableCell>

            <asp:TableCell>2.3</asp:TableCell>

            <asp:TableCell>2.4</asp:TableCell>

           </asp:TableRow>

           </asp:Table>

           <br />

           <asp:Button ID="Button1" runat="server"οnclick="Button1_Click" Text="增加一行" />

            </div>

       </form>

    </body>

    上述代码中,创键了一个二行一列的表格,同时创建了一个Button按钮控件来实现增加一行的效果,cs核心代码如下所示。

    namespace _5_14

    {

        publicpartial class _Default : System.Web.UI.Page

        {

           public TableRow row = new TableRow();                                                                                                          //定义一个TableRow对象

           protected void Page_Load(object sender, EventArgs e)

            {

            }

           protected void Button1_Click(object sender, EventArgs e)

            {

               Table1.Rows.Add(row);                                                                                                                                                    //创建一个新行

               for (int i = 0; i < 4; i++)                                                                                                                                                        //遍历四次创建新列

               {

                   TableCell cell = new TableCell();                                                                                                         //定义一个TableCell对象

                   cell.Text = "3."+i.ToString();                                                                                                                    //编写TableCell对象的文本

                   row.Cells.Add(cell);                                                                                                                                                   //增加列

                }

            }

        }

    }

    上述代码动态的创建了一行并动态的在该行创建了四列,如图5-43所示。单击【增加一列】按钮,系统会在表格中创建新行,运行效果如图5-44所示。

     

    图5-43  原表格                               图5-44  动态创建行和列

    在动态创建行和列的时候,也能够修改行和列的样式等属性,创建自定义样式的表格。通常,表不仅用来显示表格的信息,还是一种传统的布局网页的形式,创建网页表格有如下几种形式:

    q     HTML格式的表格:如<table>标记显示的静态表格。

    q     HtmlTable控件:将传统的<table>控件通过添加runat=server属性将其转换为服务器控件。

    q     Table表格控件:就是本节介绍的表格控件。

    虽然创建表格有以上三种创建方法,但是推荐开发人员在使用静态表格,当不需要对表格做任何逻辑事物处理时,最好使用HTML格式的表格,因为这样可以极大的降低页面逻辑、增强性能。

    5.15  向导控件(Wizard)

    在WinForm开发中,安装程序会一步一步的提示用户安装,或者在应用程序配置中,同样也有向导提示用户,让应用程序安装和配置变得更加的简单。与之相同的是,在ASP.NET中,也提供了一个向导控件,便于在搜集用户信息、或提示用户填写相关的表单时使用。

    5.15.1  向导控件的样式

    当创建了一个向导控件时,系统会自动生成向导控件的HTML代码,示例代码如下所示。

       <asp:Wizard ID="Wizard1" runat="server">

           <WizardSteps>

               <asp:WizardStep runat="server" title="Step 1">

               </asp:WizardStep>

               <asp:WizardStep runat="server" title="Step 2">

               </asp:WizardStep>

           </WizardSteps>

       </asp:Wizard>

    上述代码生成了Wizard控件,并在Wizard控件中自动生成了WizardSteps标签,这个标签规范了向导控件中的步骤,如图5-45所示。在向导控件中,系统会生成WizardSteps控件来显示每一个步骤,如图5-46所示。

     

    图5-45  向导控件                            图5-46  完成后的向导控件

    在ASP.NET 2.0之前,并没有Wizard向导控件,必须创建自定义控件来实现Wizard向导控件的效果,如视图控件。而在ASP.NET 2.0之后,系统就包含了向导控件,同样该控件也保留到了ASP.NET 3.5。向导控件能够根据步骤自动更换选项,如当还没有执行到最后一步时,会出现【上一步】或【下一步】按钮以便用户使用,当向导执行完毕时,则会显示完成按钮,极大的简化了开发人员的向导开发过程。

    向导控件还支持自动显示标题和控件的当前步骤。标题使用HeaderText属性自定义,同时还可以配置DisplayCancelButton属性显示一个取消按钮,如图5-47所示。不仅如此,当需要让向导控件支持向导步骤的添加时,只需配置WizardSteps属性即可,如图5-48所示。

     

    图5-47  显式“取消”按钮                  图5-48  配置步骤

    Wizard向导控件还支持一些模板。用户可以配置相应的属性来配置向导控件的模板。用户可以通过编辑StartNavigationTemplate属性、FinishNavigationTemplate属性、StepNavigationTemplate属性以及SideBarTemplate属性来进行自定义控件的界面设定。这些属性的意义如下所示:

    q     StartNavigationTemplate:该属性指定为Wizard控件的Start步骤中的导航区域显示自定义内容。

    q     FinishNavigationTemplate:该属性为Wizard控件的Finish步骤中的导航区域指定自定义内容。

    q     StepNavigationTemplate:该属性为Wizard控件的Step步骤中的导航区域指定自定义内容。

    q     SideBarTemplate:该属性为Wizard控件的侧栏区域中指定自定义内容。

    以上属性都可以通过可视化功能来编辑或修改,如图5-49所示。

    图5-49  导航控件的模板支持

    导航控件还能够自定义模板来实现更多的特定功能,同时导航控件还能够为导航控件的其他区域定义进行样式控制,如导航列表和导航按钮等。

    5.15.2  导航控件的事件

    当双击一个导航控件时,导航控件会自动生成FinishButtonClick事件。该事件是当用户完成导航控件时被触发。导航控件页面HTML核心代码如下所示。

    <body>

        <formid="form1" runat="server">

       <asp:Wizard ID="Wizard1" runat="server"ActiveStepIndex="2"

           DisplayCancelButton="True"onfinishbuttοnclick="Wizard1_FinishButtonClick">

           <WizardSteps>

                <asp:WizardSteprunat="server" title="Step 1">

                   执行的是第一步</asp:WizardStep>

               <asp:WizardStep runat="server" title="Step 2">

                   执行的是第二步</asp:WizardStep>

               <asp:WizardStep runat="server" Title="Step3">

                    感谢您的使用</asp:WizardStep>

           </WizardSteps>

       </asp:Wizard>

       <div>

           <asp:Label ID="Label1" runat="server"Text="Label"></asp:Label>

       </div>

       </form>

    </body>

    上述代码为向导控件进行了初始化,并提示用户正在执行的步骤,当用户执行完毕后,会提示感谢您的使用并在相应的文本标签控件中显示“向导控件执行完毕”。当单击示导航控件时,会触发FinishButtonClick事件,通过编写FinishButtonClick事件能够为导航控件进行编码控制,示例代码如下所示。

           protected void Wizard1_FinishButtonClick(object sender,WizardNavigationEventArgs e)

            {

               Label1.Text = "向导控件执行完毕";

            }

    在执行的过程中,标签文本会显式执行的步骤,如图5-50所示。当运行完毕时,Label标签控件会显示“向导控件执行完毕”,同时向导控件中的文本也会呈现“感谢您的使用”字样。运行结果如图5-51所示。

     

    图5-50  执行第二步                            图5-51  用户单击完成后执行事件

    向导控件不仅能够使用FinishButtonClick事件,同样也可以使用PreviousButtonClick和FinishButtonClick事件来自定义【上一步】按钮和【下一步】按钮的行为,同样也可以编写CancelButtonClick事件定义单击【取消】按钮时需要执行的操作。

    5.16  XML控件

    XML控件可以读取XML并将其写入该控件所在的ASP.NET网页。XML控件能够将XSL转换应用到XML,还能够将最终转换的内容输出呈现在该页中。当创建一个XML控件时,系统会生成XML控件的HTML代码,示例代码如下所示。

           <asp:Xml ID="Xml1"runat="server"></asp:Xml>

    上述代码实现了简单的XML控件,XML控件还包括两个常用的属性,这两个属性分别如下所示:

    q     DocumentSource:应用转换的XML文件。

    q     TransformSource:用于转换XML数据的XSL文件。

    开发人员可以通过XML控件的DocumentSource属性提供的XML,XSL文件的路径来进行加载,并将相应的代码呈现到控件上,示例代码如下所示。

           <asp:Xml ID="Xml1" runat="server"DocumentSource="~/XMLFile1.xml"></asp:Xml>

    上述代码为XML控件指定了DocumentSource属性,通过加载XML文档进行相应的代码呈现,运行后如图5-52所示。

    图5-52  加载XML文档

    XML控件不仅能够呈现XML文档的内容,还能够进行相应的XML的文本操作。在本书的第14章中,会详细讲解如何使用ASP.NET进行操作XML。

    5.17  验证控件

    ASP.NET提供了强大的验证控件,它可以验证服务器控件中用户的输入,并在验证失败的情况下显示一条自定义错误消息。验证控件直接在客户端执行,用户提交后执行相应的验证无需使用服务器端进行验证操作,从而减少了服务器与客户端之间的往返过程。

    5.17.1  表单验证控件(RequiredFieldValidator)

    在实际的应用中,如在用户填写表单时,有一些项目是必填项,例如用户名和密码。在传统的ASP中,当用户填写表单后,页面需要被发送到服务器并判断表单中的某项HTML控件的值是否为空,如果为空,则返回错误信息。在ASP.NET中,系统提供了RequiredFieldValidator验证控件进行验证。使用RequiredFieldValidator控件能够指定某个用户在特定的控件中必须提供相应的信息,如果不填写相应的信息,RequiredFieldValidator控件就会提示错误信息,RequiredFieldValidator控件示例代码如下所示。

    <body>

        <formid="form1" runat="server">

       <div>  

            姓名:<asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

                 <asp:RequiredFieldValidatorID="RequiredFieldValidator1" runat="server"

               ControlToValidate="TextBox1" ErrorMessage="必填字段不能为空"></asp:RequiredFieldValidator>

           <br />

            密码:<asp:TextBox ID="TextBox2"runat="server"></asp:TextBox>

           <br />

           <asp:Button ID="Button1" runat="server"Text="Button" />

           <br />  

       </div>

       </form>

    </body>

    在进行验证时,RequiredFieldValidator控件必须绑定一个服务器控件,在上述代码中,验证控件RequiredFieldValidator控件的服务器控件绑定为TextBox1,当TextBox1中的值为空时,则会提示自定义错误信息“必填字段不能为空”,如图5-53所示。

    图5-53  RequiredFieldValidator验证控件

    当姓名选项未填写时,会提示必填字段不能为空,并且该验证在客户端执行。当发生此错误时,用户会立即看到该错误提示而不会立即进行页面提交,当用户填写完成并再次单击按钮控件时,页面才会向服务器提交。

    5.17.2  比较验证控件(CompareValidator)

    比较验证控件对照特定的数据类型来验证用户的输入。因为当用户输入用户信息时,难免会输入错误信息,如当需要了解用户的生日时,用户很可能输入了其他的字符串。CompareValidator比较验证控件能够比较控件中的值是否符合开发人员的需要。CompareValidator控件的特有属性如下所示:

    q     ControlToCompare:以字符串形式输入的表达式。要与另一控件的值进行比较。

    q     Operator:要使用的比较。

    q     Type:要比较两个值的数据类型。

    q     ValueToCompare:以字符串形式输入的表达式。

    当使用CompareValidator控件时,可以方便的判断用户是否正确输入,示例代码如下所示。

    <body>

        <formid="form1" runat="server">

       <div>  

            请输入生日:

           <asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

           <br />

            毕业日期:

           <asp:TextBox ID="TextBox2"runat="server"></asp:TextBox>

        <asp:CompareValidatorID="CompareValidator1" runat="server"

               ControlToCompare="TextBox2" ControlToValidate="TextBox1"

               CultureInvariantValues="True" ErrorMessage="输入格式错误!请改正!"

                Operator="GreaterThan"

               Type="Date">

        </asp:CompareValidator>

           <br />

           <asp:Button ID="Button1" runat="server"Text="Button" />

            <br/>

       </div>

       </form>

    </body>

    上述代码判断TextBox1的输入的格式是否正确,当输入的格式错误时,会提示错误,如图5-54所示。

    图5-54  CompareValidator验证控件

    CompareValidator验证控件不仅能够验证输入的格式是否正确,还可以验证两个控件之间的值是否相等。如果两个控件之间的值不相等,CompareValidator验证控件同样会将自定义错误信息呈现在用户的客户端浏览器中。

    5.17.3  范围验证控件(RangeValidator)

    范围验证控件(RangeValidator)可以检查用户的输入是否在指定的上限与下限之间。通常情况下用于检查数字、日期、货币等。范围验证控件(RangeValidator)控件的常用属性如下所示。

    q     MinimumValue:指定有效范围的最小值。

    q     MaximumValue:指定有效范围的最大值。

    q     Type:指定要比较的值的数据类型。

    通常情况下,为了控制用户输入的范围,可以使用该控件。当输入用户的生日时,今年是2008年,那么用户就不应该输入2009年,同样基本上没有人的寿命会超过100,所以对输入的日期的下限也需要进行规定,示例代码如下所示。

       <div>

            请输入生日:<asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

           <asp:RangeValidator ID="RangeValidator1"runat="server"

               ControlToValidate="TextBox1" ErrorMessage="超出规定范围,请重新填写"

                MaximumValue="2009/1/1"MinimumValue="1990/1/1"Type="Date">

                </asp:RangeValidator>

           <br />

           <asp:Button ID="Button1" runat="server"Text="Button" /> 

       </div>

    上述代码将MinimumValue属性值设置为1990/1/1,并能将MaximumValue的值设置为2009/1/1,当用户的日期低于最小值或高于最高值时,则提示错误,如图5-55所示。

    图5-55  RangeValidator验证控件

    注意:RangeValidator验证控件在进行控件的值的范围的设定时,其范围不仅仅可以是一个整数值,同样还能够是时间、日期等值。

    5.17.4  正则验证控件(RegularExpressionValidator)

    在上述控件中,虽然能够实现一些验证,但是验证的能力是有限的,例如在验证的过程中,只能验证是否是数字,或者是否是日期。也可能在验证时,只能验证一定范围内的数值,虽然这些控件提供了一些验证功能,但却限制了开发人员进行自定义验证和错误信息的开发。为实现一个验证,很可能需要多个控件同时搭配使用。

    正则验证控件(RegularExpressionValidator)就解决了这个问题,正则验证控件的功能非常的强大,它用于确定输入的控件的值是否与某个正则表达式所定义的模式相匹配,如电子邮件、电话号码以及序列号等。

    正则验证控件(RegularExpressionValidator)常用的属性是ValidationExpression,它用来指定用于验证的输入控件的正则表达式。客户端的正则表达式验证语法和服务端的正则表达式验证语法不同,因为在客户端使用的是JSript正则表达式语法,而在服务器端使用的是Regex类提供的正则表达式语法。使用正则表达式能够实现强大字符串的匹配并验证用户的输入的格式是否正确,系统提供了一些常用的正则表达式,开发人员能够选择相应的选项进行规则筛选,如图5-56所示。

    图5-56  系统提供的正则表达式

    当选择了正则表达式后,系统自动生成的HTML代码如下所示。

           <asp:RegularExpressionValidator ID="RegularExpressionValidator1"runat="server"

               ControlToValidate="TextBox1" ErrorMessage="正则不匹配,请重新输入!"

               ValidationExpression="\d{17}[\d|X]|\d{15}">

            </asp:RegularExpressionValidator>

    运行后当用户单击按钮控件时,如果输入的信息与相应的正则表达式不匹配,则会提示错误信息,如图5-57所示。

    图5-57 RegularExpressionValidator验证控件

    同样,开发人员也可以自定义正则表达式来规范用户的输入。使用正则表达式能够加快验证速度并在字符串中快速匹配,而另一方面,使用正则表达式能够减少复杂的应用程序的功能开发和实现。

    注意:在用户输入为空时,其他的验证控件都会验证通过。所以,在验证控件的使用中,通常需要同表单验证控件(RequiredFieldValidator)一起使用。

    5.17.5  自定义逻辑验证控件(CustomValidator)

    自定义逻辑验证控件(CustomValidator)允许使用自定义的验证逻辑创建验证控件。例如,可以创建一个验证控件判断用户输入的是否包含“.”号,示例代码如下所示。

           protected void CustomValidator1_ServerValidate(object source,ServerValidateEventArgs args)

            {

               args.IsValid = args.Value.ToString().Contains(".");                                             //设置验证程序,并返回布尔值

            }

           protected void Button1_Click(object sender, EventArgs e)                                        //用户自定义验证

            {

               if (Page.IsValid)                                                                                                                                                                                     //判断是否验证通过

               {

                   Label1.Text = "验证通过";                                                                                                                       //输出验证通过

               }

               else

                {

                   Label1.Text = "输入格式错误";                                                                                                               //提交失败信息

               }

            }

    上述代码不仅使用了验证控件自身的验证,也使用了用户自定义验证,运行结果如图5-58所示。

    图5-58  CustomValidator验证控件

    从CustomValidator验证控件的验证代码可以看出,CustomValidator验证控件可以在服务器上执行验证检查。如果要创建服务器端的验证函数,则处理CustomValidator控件的ServerValidate事件。使用传入的ServerValidateEventArgs的对象的IsValid字段来设置是否通过验证。

    而CustomValidator控件同样也可以在客户端实现,该验证函数可用VBScript或Jscript来实现,而在CustomValidator控件中需要使用ClientValidationFunction属性指定与CustomValidator控件相关的客户端验证脚本的函数名称进行控件中的值的验证。

    5.17.6  验证组控件(ValidationSummary)

    验证组控件(ValidationSummary)能够对同一页面的多个控件进行验证。同时,验证组控件(ValidationSummary)通过ErrorMessage属性为页面上的每个验证控件显式错误信息。验证组控件(ValidationSummary)的常用属性如下所示。

    q     DisplayMode:摘要可显示为列表,项目符号列表或单个段落。

    q     HeaderText:标题部分指定一个自定义标题。

    q     ShowMessageBox:是否在消息框中显示摘要。

    q     ShowSummary:控制是显示还是隐藏ValidationSummary控件。

    验证控件能够显示页面的多个控件产生的错误,示例代码如下所示。

    <body>

        <formid="form1" runat="server">

       <div>

            姓名:

           <asp:TextBox ID="TextBox1"runat="server"></asp:TextBox>

           <asp:RequiredFieldValidator ID="RequiredFieldValidator1"runat="server"

               ControlToValidate="TextBox1" ErrorMessage="姓名为必填项">

            </asp:RequiredFieldValidator>

           <br />

            身份证:

           <asp:TextBox ID="TextBox2"runat="server"></asp:TextBox>

           <asp:RegularExpressionValidator ID="RegularExpressionValidator1"runat="server"

               ControlToValidate="TextBox1" ErrorMessage="身份证号码错误"

               ValidationExpression="\d{17}[\d|X]|\d{15}"></asp:RegularExpressionValidator>

           <br />

           <asp:Button ID="Button1" runat="server"Text="Button" />

           <asp:ValidationSummary ID="ValidationSummary1"runat="server" /> 

       </div>

       </form>

    </body>

    运行结果如图5-59所示。

    图5-59  ValidationSummary验证控件

    当有多个错误发生时,ValidationSummary控件能够捕获多个验证错误并呈现给用户,这样就避免了一个表单需要多个验证时需要使用多个验证控件进行绑定,使用ValidationSummary控件就无需为每个需要验证的控件进行绑定。

    5.18  导航控件

    在网站制作中,常常需要制作导航来让用户能够更加方便快捷的查阅到相关的信息和资讯,或能跳转到相关的版块。在Web应用中,导航是非常重要的。ASP.NET提供了站点导航的一种简单的方法,即使用站点图形站点导航控件SiteMapPath、TreeView、Menu等控件。

    导航控件包括SiteMapPath、TreeView、Menu三个控件,这三个控件都可以在页面中轻松建立导航。这三个导航控件的基本特征如下所示:

    q     SiteMapPath:检索用户当前页面并显示层次结构的控件。这使用户可以导航回到层次结构中的其他页。SiteMap控件专门与SiteMapProvider一起使用。

    q     TreeView:提供纵向用户界面以展开和折叠网页上的选定节点,以及为选定像提供复选框功能。并且TreeView控件支持数据绑定。

    q     Menu:提供在用户将鼠标指针悬停在某一项时弹出附加子菜单的水平或垂直用户界面。

    这三个导航控件都能够快速的建立导航,并且能够调整相应的属性为导航控件进行自定义。

    SiteMapPath控件使用户能够从当前导航回站点层次结构中较高的页,但是该控件并不允许用户从当前页面向前导航到层次结构中较深的其他页面。相比之下,使用TreeView或Menu控件,用户可以打开节点并直接选择需要跳转的特定页。这些控件不会像SiteMapPath控件一样直接读取站点地图。TreeView和Menu控件不仅可以自定义选项,也可以绑定一个SiteMapDataSource。TreeView和Menu控件的基本样式如图5-60和图5-61所示。

     

    图5-60  Menu导航控件                          图5-61  TreeView导航控件

    TreeView和Menu控件生成的代码并不相同,因为TreeView和Menu控件所实现的功能也不尽相同。TreeView和Menu控件的代码分别如下所示。

           <asp:Menu ID="Menu1" runat="server">

               <Items>

                   <asp:MenuItem Text="新建项"Value="新建项"></asp:MenuItem>

                   <asp:MenuItem Text="新建项"Value="新建项">

                       <asp:MenuItem Text="新建项"Value="新建项"></asp:MenuItem>

                   </asp:MenuItem>

                   <asp:MenuItem Text="新建项"Value="新建项">

                       <asp:MenuItem Text="新建项"Value="新建项"></asp:MenuItem>

                    </asp:MenuItem>

                   <asp:MenuItem Text="新建项"Value="新建项">

                       <asp:MenuItem Text="新建项"Value="新建项">

                            <asp:MenuItemText="新建项" Value="新建项"></asp:MenuItem>

                       </asp:MenuItem>

                    </asp:MenuItem>

                   <asp:MenuItem Text="新建项"Value="新建项"></asp:MenuItem>

               </Items>

           </asp:Menu>

    上述代码声明了一个Menu控件,并添加了若干节点。

           <asp:TreeView ID="TreeView1" runat="server">

               <Nodes>

                   <asp:TreeNode Text="新建节点"Value="新建节点"></asp:TreeNode>

                   <asp:TreeNode Text="新建节点"Value="新建节点">

                       <asp:TreeNode Text="新建节点"Value="新建节点"></asp:TreeNode>

                   </asp:TreeNode>

                   <asp:TreeNode Text="新建节点" Value="新建节点">

                       <asp:TreeNode Text="新建节点"Value="新建节点"></asp:TreeNode>

                   </asp:TreeNode>

                   <asp:TreeNode Text="新建节点"Value="新建节点">

                       <asp:TreeNode Text="新建节点"Value="新建节点"></asp:TreeNode>

                    </asp:TreeNode>

                   <asp:TreeNode Text="新建节点"Value="新建节点"></asp:TreeNode>

               </Nodes>

           </asp:TreeView>

    上述代码声明了一个TreeView控件,并添加了若干节点。

    从上面的代码和运行后的实例图可以看出,TreeView和Menu控件有一些区别,这些具体区别如下所示:

    q     Menu展开时,是弹出形式的展开,而TreeView控件则是就地展开。

    q     Menu控件并不是按需下载,而TreeView控件则是按需下载的。

    q     Menu控件不包含复选框,而TreeView控件包含复选框。

    q     Menu控件允许编辑模板,而TreeView控件不允许模板编辑。

    q     Menu在布局上是水平和垂直,而TreeView只是垂直布局。

    q     Menu可以选择样式,而TreeView不行。

    开发人员在网站开发的时候,可以通过使用导航控件来快速的建立导航,为浏览者提供方便,也为网站做出信息指导。在用户的使用中,通常情况下导航控件中的导航值是不能被用户所更改的,但是开发人员可以通过编程的方式让用户也能够修改站点地图的节点。

    5.19  其他控件

    在ASP.NET中,除了以上常用的一些基本控件以外,还有一些其他基本控件,虽然在应用程序开发中并不经常使用,但是在特定的程序开发中,还是需要使用到这些基本的控件进行特殊的应用程序开发和逻辑处理。

    5.19.1  隐藏输入框控件(HiddenField)

    HiddenField控件就是隐藏输入框控件,用来保存那些不需要显示在页面上的对安全性要求不高的数据。隐藏输入框控件作为<input type=“hidden”/>元素呈现在HTML页面。由于HiddenField隐藏输入框控件的值会呈现在客户端浏览器,所以对于安全性较高的数据,并不推荐将它保存在隐藏输入框控件中。隐藏输入框控件的值通过Value属性保存,同时也可以通过代码来控制Value的值,利用隐藏输入框对页面的值进行传递,示例代码如下所示。

           protected void Button1_Click(object sender, EventArgs e)

            {

               Label1.Text = HiddenField1.Value;                                                                                                                                //获取隐藏输入框控件的值

            }

    上述代码通过Value属性获取一个隐藏输入框的值,如图5-62所示。单击后如图5-63所示。

     

    图5-62  HiddenField的值被隐藏                图5-63  HiddenField的值被获取

    HiddenField是通过HTTP协议进行参数传递的,所以当打开新的窗体或者使用method=get都无法使用HiddenField隐藏输入框控件。同时,隐藏输入框控件还能初始化或保存一些安全性不高的数据。当双击隐藏输入框控件时,系统会自动生成ValueChanged事件代码段,当隐藏输入框控件内的值被改变时则触发该事件,示例代码如下所示。

           protected void Button1_Click(object sender, EventArgs e)

            {

               HiddenField1.Value = "更改了值";                                                                                                                                                     //更改隐藏输入框控件的值

            }

           protected void HiddenField1_ValueChanged(object sender, EventArgs e)           //更改将触发此事件

            {

               Label1.Text = "值被更改了,并被更改成\"" + HiddenField1.Value + "\"";

            }

    上述代码创建了一个ValueChanged事件,并当隐藏输入框控件的值被更改时,如图5-64所示。单击【change】按钮后会触发按钮事件,运行结果如图5-65所示。

     

    图5-64  更新前                                   图5-65  更新后

    5.19.2  图片热点控件(ImageMap)

    ImageMap控件是一个让你可以在图片上定义热点(HotSpot)区域的服务器控件。用户可以通过点击这些热点区域进行回发(PostBack)操作或者定向(Navigate)到某个URL位址。该控件一般用在需要对某张图片的局部范围进行互动操作。ImageMap控件主要由两个部分组成,第一部分是图像。第二部分是作用点控件的集合。其主要属性有HotSpotMode、HotSpots,具体如下所示。

    1.HotSpotMode(热点模式)常用选项

    q     NotSet:未设置项。虽然名为未设置,但其实默认情况下会执行定向操作,定向到你指定的URL位址去。如果你未指定URL位址,那默认将定向到自己的Web应用程序根目录。

    q     Navigate:定向操作项。定向到指定的URL位址去。如果你未指定URL位址,那默认将定向到自己的Web应用程序根目录。

    q     PostBack:回发操作项。点击热点区域后,将执行后部的Click事件。

    q     Inactive:无任何操作,即此时形同一张没有热点区域的普通图片。

    2.HotSpots(图片热点)常用属性

    该属性对应着System.Web.UI.WebControls.HotSpot对象集合。HotSpot类是一个抽象类,它之下有CircleHotSpot(圆形热区)、RectangleHotSpot(方形热区)和PolygonHotSpot(多边形热区)三个子类。实际应用中,都可以使用上面三种类型来定制图片的热点区域。如果需要使用到自定义的热点区域类型时,该类型必须继承HotSpot抽象类。同时,ImageMap最常用的事件有Click,通常在HotSpotMode为PostBack时用到。当需要设置HotSpots属性时,可以可视化设置,如图5-66所示。

    图5-66  可视化设置HotSpots属性

    当可视化完毕后,系统会自动生成HTML代码,核心代码如下所示。

        <asp:ImageMapID="ImageMap1" runat="server"HotSpotMode="PostBack"

            ImageUrl="~/images/mobile.jpg"οnclick="ImageMap1_Click">

            <asp:CircleHotSpotRadius="15" X="15" Y="15"HotSpotMode="PostBack" PostBackValue="0" />

            <asp:CircleHotSpotRadius="100" X="15" Y="15"HotSpotMode="PostBack" PostBackValue="1" />

            <asp:CircleHotSpotRadius="300" X="15" Y="15"HotSpotMode="PostBack" PostBackValue="2" />

        </asp:ImageMap>

    上述代码还添加了一个Click事件,事件处理的核心代码如下所示。

           protected void ImageMap1_Click(object sender, ImageMapEventArgs e)

            {

               string str="";

               switch (e.PostBackValue)                                                                                                                                                                   //获取传递过来的参数

               {

                   case "0":

                        str = "你点击了1号位置,图片大小将变为1号"; break;

                   case "1":

                       str = "你点击了2号位置,图片大小将变为3号"; break;

                   case "2":

                       str = "你点击了3号位置,图片大小将变为3号"; break;

               }

               Label1.Text = str;

               ImageMap1.Height =120*(Convert.ToInt32(e.PostBackValue)+1);                                    //更改图片的大小

            }

    上述代码通过获取ImageMap中的CricleHotSpot控件中的PostBackVlue值来获取传递的参数,如图5-67所示。当获取到传递的参数时,可以通过参数做相应的操作,如图5-68所示。

     

    图5-67  单击图片变大                              图5-68  单击图片变小

    5.19.3  静态标签控件(Lieral)

    通常情况下Lieral控件无需添加任何HTML元素即可将静态文本呈现在网页上。与Label不同的是,Label控件在生成HTML代码时,会呈现<span>元素。而Lieral控件不会向文本中添加任何HTML代码。如果开发人员希望文本和控件直接呈现在页面中而不使用任何附加标记时,推荐使用Lieral控件。

    与Label不同的是,Lieral控件有一个Mode属性,用来控制Lieral控件中的文本的呈现形式。当HTML代码被输出到页面的时候,会以解释后的HTML形式输出。例如图片代码,在HTML中是以<img src=“”>的形式显示的,输出到HTML页面后,会被显示成一个图片。

    在Label中,Label可以作为一段HTML代码的容器,当输出时,Label控件所呈现的效果是HTML被解释后的样式。而Lieral可以通过Mode属性来选择输出的是HTML样式还是HTML代码,核心代码如下所示。

    namespace _5_17

    {

        publicpartial class Lieral : System.Web.UI.Page

        {

            protectedvoid Page_Load(object sender, EventArgs e)

            {

               string str = "<span style=\"color:red\">大家好</span>,您现在查看的是HTML样式。";//HTML字符

                Literal1.Text = str +

                "<divstyle=\"border-top:1px dashed #ccc;background:gray\">单击按钮查看HTML代码</div>";

               Label1.Text = str;                                                                                                                                                                                                                                 //赋值Label

            }

           protected void Button1_Click(object sender, EventArgs e)

            {

               Literal1.Mode = LiteralMode.Encode;                                                                                                                                                        //转换显示的模式

            }

        }

    }

    上述代码将一个HTML形式的字符串分别赋值给Literal和Label控件,并通过转换查看赋值的源代码,运行结果如图5-69和图5-70所示。

     

    图5-69  Literal控件直接显式HTML样式             图5-70  Literal控件显式HTML代码

    当点击了按钮后,更改了Literal的模式之后,Literal中的HTML文本被直接显示,Literal的具有三种模式,具体如下:

    q     Transform:添加到控件中的任何标记都将进行转换,以适应请求浏览器的协议。

    q     PassThrough:添加到控件中的任何标记都将按照原样输出在浏览器中。

    q     Encode:添加到控件中的任何标记都将使用HtmlEncode方法进行编码,该方法将把HTML编码转换为其文本表示形式。

    注意:PassThrough模式和Ttansform模式在通常情况下,呈现的效果并没有区别。

    5.19.4  动态缓存更新控件(Substitution)

    在ASP.NET中,缓存的使用能够极大的提高网站的性能,降低服务器的压力。而通常情况下,对ASP.NET整个页面的缓存是没有任何意义的,这样经常会给用户带来疑惑。Substitution动态缓存更新控件允许用户在页上创建一些区域,这些区域可以用动态的方式进行更新,然后集成到缓存页。

    Substitution动态缓存更新控件将动态内容插入到缓存页中,并且不会呈现任何HTML标记。用户可以将控件绑定到页上或父用户控件上的方法,自行创建静态方法,以返回要插入到页面中的任何信息。同时,要使用Substitution控件,则必须符合以下标准:

    q     此方法被定义为静态方法。

    q     此方法接受HttpContext类型的参数。

    q     此方法返回String类型的值。

    在ASP.NET页面中,为了减少用户与页面的交互中数据库的更新,可以对ASP.NET页面进行缓存,缓存代码可以使用页面参数的@OutputCatch,示例代码如下所示。

    <%@ Page

    Language="C#"AutoEventWireup="true" CodeBehind="Substitution.aspx.cs"Inherits="_5_17.Substitution" %>

    <%@ OutputCache Duration="100"VaryByParam="none" %>                                                                                                        //增加一个页面缓存

    <!DOCTYPE html

    PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <htmlxmlns="http://www.w3.org/1999/xhtml" >

    <head runat="server">

       <title>无标题页</title>

    </head>

    <body>

        <formid="form1" runat="server">

       <div>

            当前的时间为:<asp:Label ID="Label1" runat="server"Text="Label"></asp:Label>

            (有缓存)<br />

            当前的时间为:<asp:Substitution ID="Substitution1"runat="server" MethodName="GetTimeNow"/>

            (动态更新)</div>

       </form>

    </body>

    </html>

    执行事件操作的cs页面核心代码如下所示。

           protected void Page_Load(object sender, EventArgs e)

            {

               Label1.Text = DateTime.Now.ToString();                         //页面初始化时,当前时间赋值给Label1标签

            }

           protected static string GetTimeNow(HttpContext con)          //注意事件的格式

            {

                returnDateTime.Now.ToString();                                                         //Substitution控件执行的方法

            }

    上述代码对ASP.NET页面进行了缓存,当用户访问页面时,除了Substitution控件的区域以外区域都会被缓存,而使用了Substitution控件局部在刷新后会进行更新,运行结果如图5-71所示。

    图5-71  Substitution动态更新

    如运行结果可见,没有使用Substitution控件的区域,当页面再次被请求时,会直接在缓存中执行。而Substitution控件区域内的值并不会缓存。在每次刷新时,页面将进行Substitution控件的区域的局部的动态更新。

    5.20  小结

    本章讲解了ASP.NET中常用的控件,对于这些控件,能够极大的提高开发人员的效率,对于开发人员而言,能够直接拖动控件来完成应用的目的。虽然控件是非常的强大,但是这些控件却制约了开发人员的学习,人们虽然能够经常使用ASP.NET中的控件来创建强大的多功能网站,却不能深入的了解控件的原理,所以对这些控件的熟练掌握,是了解控件的原理的第一步。本章还介绍了:

    q     控件的属性:介绍了控件的属性。

    q     简单控件:介绍了标签控件等简单控件。

    q     文本框控件:介绍了文本框控件。

    q     按钮控件:介绍了按钮控件的实现和按钮事件的运行过程。

    q     单选控件和单选组控件:介绍了单选控件和单选组控件。

    q     复选框控件和复选组控件:介绍了复选框控件和复选组控件。

    这些控件为ASP.NET应用程序的开发提供了极大的遍历,在ASP.NET控件中,不仅仅包括这些基本的服务器控件,还包括高级的数据源控件和数据绑定控件用于数据操作,但是在了解ASP.NET高级控件之前,需要熟练的掌握基本控件的使用。

    展开全文
  • Windows窗体原理及控件重绘技巧

    千次阅读 2018-01-29 11:42:21
    之前有学MFC的同学告诉我觉得Windows的控件重绘难以理解,就算重绘成功了还是有些地方不明白,我觉得可能很多人都有这样的问题,这里我从Windows窗体的最基本原理来讲解,如果你有类似的疑惑希望这篇文章可以帮你...

    之前有学MFC的同学告诉我觉得Windows的控件重绘难以理解,就算重绘成功了还是有些地方不明白,我觉得可能很多人都有这样的问题,在这里我从Windows窗体的最基本原理来讲解,如果你有类似的疑惑希望这篇文章可以帮你解惑。

    1.Windows窗体原理

    首先,如果看过Win32 SDK编程的都知道Windows的三大核心系统:负责窗口对象产生和消息分发的USER模块,负责图像显示绘制的GDI模块,负责内存、进程、IO管理的KERNEL模块。试想象一下如何在一个像素阵列上产生窗口对象,其实就是使用GDI绘制窗口,不停的以一定的频率刷新显示在屏幕上,这就是图形界面,如果由在DOS或Windows DOS模拟器下编写图形界面的经验这个比较好理解。所以说其实USER模块中的窗口产生是依靠GDI模块的(包括菜单、滚动条等都是使用GDI来绘制的)。

    那么,下面我们就从USER模块和GDI模块来说说Windows 的窗体原理。

    如果接触过Win32 SDK编程的知道一个标准Windows窗体的产生过程:设计窗口类、注册窗口类、创建窗口、显示窗口、启动消息循环泵循环获取消息分发到窗体过程函数处理。为了保证博客的连贯性,在这里我贴上一个标准Windows窗体的产生代码。

    [cpp] view plain copy
    1. #include <windows.h>  
    2.   
    3. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);  
    4.   
    5. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)  
    6. {  
    7.     static TCHAR szAppName[] = TEXT ("窗口类名称");  
    8.     HWND         hwnd;  
    9.     MSG          msg;  
    10.     WNDCLASSEX   wndclassex = {0};  
    11.   
    12.     //设计窗口类  
    13.     wndclassex.cbSize        = sizeof(WNDCLASSEX);  
    14.     wndclassex.style         = CS_HREDRAW | CS_VREDRAW;  
    15.     wndclassex.lpfnWndProc   = WndProc;  
    16.     wndclassex.cbClsExtra    = 0;  
    17.     wndclassex.cbWndExtra    = 0;  
    18.     wndclassex.hInstance     = hInstance;  
    19.     wndclassex.hIcon         = LoadIcon (NULL, IDI_APPLICATION);  
    20.     wndclassex.hCursor       = LoadCursor (NULL, IDC_ARROW);  
    21.     wndclassex.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);  
    22.     wndclassex.lpszMenuName  = NULL;  
    23.     wndclassex.lpszClassName = szAppName;  
    24.     wndclassex.hIconSm       = wndclassex.hIcon;  
    25.       
    26.     //注册窗口类  
    27.     if (!RegisterClassEx (&wndclassex))  
    28.     {  
    29.         MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR);  
    30.         return 0;  
    31.     }  
    32.   
    33.     //产生窗口  
    34.     hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW,   
    35.                           szAppName,   
    36.                           TEXT ("窗口名称"),  
    37.                           WS_OVERLAPPEDWINDOW,  
    38.                           CW_USEDEFAULT,   
    39.                           CW_USEDEFAULT,   
    40.                           CW_USEDEFAULT,   
    41.                           CW_USEDEFAULT,   
    42.                           NULL,   
    43.                           NULL,   
    44.                           hInstance,  
    45.                           NULL);   
    46.               
    47.     //显示窗口  
    48.     ShowWindow (hwnd, iCmdShow);  
    49.     UpdateWindow (hwnd);  
    50.       
    51.     //启动消息循环泵循环获取消息分配到窗体过程函数处理  
    52.     while (GetMessage (&msg, NULL, 0, 0))  
    53.     {  
    54.         TranslateMessage (&msg);  
    55.         DispatchMessage (&msg);  
    56.     }  
    57.   
    58.     return msg.wParam;  
    59. }  
    60.   
    61. //窗体过程函数  
    62. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
    63. {  
    64.     HDC hdc;  
    65.     PAINTSTRUCT ps;  
    66.   
    67.     switch (message)  
    68.     {  
    69.     case WM_CREATE:  
    70.         return (0);  
    71.           
    72.     case WM_PAINT:  
    73.         hdc = BeginPaint (hwnd, &ps);  
    74.         EndPaint (hwnd, &ps);  
    75.         return (0);  
    76.           
    77.     case WM_DESTROY:  
    78.         PostQuitMessage (0);  
    79.         return (0);  
    80.     }  
    81.   
    82.     return DefWindowProc (hwnd, message, wParam, lParam);  
    83. }  

    需要明白的是,所有Windows的窗体及控件归根结底都是使用CreateWindow或CreateWindowEx来创建的,他们都需要标准Windows窗体的产生过程

    普通的窗体好理解,主要需要弄清楚是对话框及控件的产生和消息分派处理流程。

    对话框及其子控件的管理依靠Windows内建的对话框管理器,对话框管理器的工作包括:

    1.根据我们在资源设计器中设计的对话框及子控件产生的.rc文件来自动生成对话框和子控件(如果有手动编写.rc文件的经历的话,知道编写RC文件其实就是指定窗口和子控件大小、类型、样式等参数,对话框管理器将这些参数传入CreateWindow函数产生窗体)

    2.模态对话框直接显示窗体,非模态对话框消息指明WS_VISIBLE属性的话,需要调用ShowWindow来显示窗体。

    4.维护一个消息循环泵,对于模态对话框来说这个消息泵的消息不经过父窗口,所以表现为模态;对于非模态对话框这个消息泵消息经过主窗口,必须由主窗口传给非模态对话框,表现为非模态。

    3.维护一个内建的窗体过程函数,对于对话框来说会处理对话框的关闭打开及子窗口的焦点、tab等,对于子控件也是一样,每个子控件会有自己类型的窗体过程函数,窗体过程函数处理子控件的获得或失去焦点、按下或弹起、创建等表现样式和行为。对于对话框来说,他会开放一个对话框过程函数,让部分消息先通过对话框管理函数处理,如果对话框过程函数不处理才交给默认的内建过程函数处理,对于子控件来说,他们并没有开放过程函数,而是由内建窗体函数将要处理的消息发给父窗口处理。

    那么对话框管理器完成了标准Windows窗体的产生中后半部分工作,至于设计窗口类和注册窗口类这是由Windows自己预先做好了的,如常见的“button”、“listbox”、“edit”类等等。

    一个简要的示意图如下


    那么既然所有的窗体(包括对话框和控件)产生过程一样,那么我们就可以将对话框管理器的部分工作替换掉:

    1.不使用对话框读取.rc模板的方式,直接将参数传递给CreateWindow函数来创建对话框和控件,这就是常见的动态创建控件原理。

    2.设置控件自绘制如BS_OWNDRAW属性,开放控件的WM_DRAWITEM消息给父窗口,由父窗口来绘制按钮样式,这就是常见的控件重绘原理。

    3.替换掉内建的窗体函数,将消息传到自定义的窗体过程函数处理,这就是常见的控件子类化原理。


    下面,为了做演示,先用通用模板创建的方式创建一个模态对话框和其子控件,然后模板创建一个非模态对话框,在非模态对话框中使用动态创建的方式创建和模态对话框中模板创建一样的按钮(当然位置和大小等可能不一样,这里只是为了说明原理故笔者并没有去管这些细节,如果你愿意完全可以把它们做的一模一样)。

    代码太长,这里只贴出部分代码,详细代码请下载演示文件

    主窗口消息泵

    [cpp] view plain copy
    1.   while (GetMessage (&msg, NULL, 0, 0))  
    2.   {  
    3. //注意非模态对话框消息由主窗口分发  
    4. if (hDlgModeless == NULL || !IsDialogMessage(hDlgModeless, &msg))  
    5. {  
    6.     TranslateMessage (&msg);  
    7.     DispatchMessage (&msg);  
    8. }  
    9.   }  
    主窗口菜单响应

    [cpp] view plain copy
    1. case IDM_TEMPLATE:  
    2.     DialogBox(GetWindowLong(hwnd, GWL_HINSTANCE),  
    3.               IDD_TEMPLATE,  
    4.               hwnd,  
    5.               TemplateProc);  
    6.     break;  
    7. case IDM_CREATE:  
    8.     hDlgModeless = CreateDialog(GetWindowLong(hwnd, GWL_HINSTANCE),  
    9.                                  MAKEINTRESOURCE(IDD_CREATE),  
    10.                                  hwnd,  
    11.                                  CreateProc);  
    12.     ShowWindow(hDlgModeless, SW_NORMAL);//注意非模态对话框不指明WS_VISIBLE属性必须显示调用ShowWindow来显示  
    13.     break;  
    模板创建的模态对话框对话框过程函数

    [cpp] view plain copy
    1. BOOL CALLBACK  TemplateProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     switch(message)  
    4.     {  
    5.     case WM_CLOSE:  
    6.         {  
    7.             EndDialog(hDlg,0);  
    8.         }  
    9.         return (TRUE);  
    10.   
    11.     case WM_COMMAND:  
    12.         switch (LOWORD(wParam))  
    13.         {  
    14.         case IDCANCEL:  
    15.             {  
    16.                 SendMessage(hDlg, WM_CLOSE, 0, 0);  
    17.             }  
    18.             return (TRUE);  
    19.         case IDOK:  
    20.             {  
    21.                   
    22.             }  
    23.             return (TRUE);  
    24.         }  
    25.         return (FALSE);  
    26.     }  
    27.   
    28.     return (FALSE);  
    29. }  

    模板创建的非模态对话框的对话框过程函数

    [cpp] view plain copy
    1. BOOL CALLBACK  CreateProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     switch(message)  
    4.     {  
    5.     case WM_INITDIALOG:  
    6.         {  
    7.             //动态创建控件子窗口  
    8.             CreateWindow(TEXT("button"),   
    9.                          TEXT("确定"),   
    10.                          WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,  
    11.                          10, 10,  
    12.                          100, 50,  
    13.                          hDlg,  
    14.                          (HMENU)IDOK,  
    15.                          GetWindowLong(hDlg, GWL_HINSTANCE),  
    16.                          NULL);  
    17.             CreateWindow(TEXT("button"),   
    18.                         TEXT("取消"),   
    19.                         WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,  
    20.                         10, 100,  
    21.                         100, 50,  
    22.                         hDlg,  
    23.                         (HMENU)IDCANCEL,  
    24.                         GetWindowLong(hDlg, GWL_HINSTANCE),  
    25.                         NULL);  
    26.         }  
    27.         return (TRUE);  
    28.   
    29.     case WM_CLOSE:  
    30.         DestroyWindow(hDlg);  
    31.         hDlgModeless = NULL;//注意及时将指针置0防止窗口销毁后消窗口分发消息  
    32.         return (TRUE);  
    33.   
    34.     case WM_COMMAND:  
    35.         switch (LOWORD(wParam))  
    36.         {  
    37.         case IDCANCEL:  
    38.             {  
    39.                 SendMessage(hDlg, WM_CLOSE, 0, 0);  
    40.             }  
    41.             return (TRUE);  
    42.         case IDOK:  
    43.             {  
    44.   
    45.             }  
    46.             return (TRUE);  
    47.         }  
    48.         return (FALSE);  
    49.     }  
    创建效果

    模态对话框


    非模态对话框

    二者起到了相同的作用,动态创建比模板创建要灵活的多,这个深入学习请自行查找相关资料。上例中需要注意的模态对话框和非模态对话框,前者的消息不流经主窗口消息泵,后者的消息要先流经主窗口消息泵。

    2.控件重绘(WM_DRAWITEM)

    写这篇博文的初衷就是讲解控件重绘原理,自然不能少了这一内容,在刚刚提到了修改对话框管理器的行为的几种方式,后两种(开放WM_DRAWITEM消息和控件子类化)都是常用的控件重绘技巧,在这一节先讲WM_DRAWITEM消息重绘,下一节讲控件子类化重绘,都是以按钮的重绘为例来讲解。
    WM_DRAWITEM顾名思义当控件需要重绘的时候发给主窗口的消息,一般在按钮按下或弹起、获得焦点或失去焦点、创建等时候会产生这一消息,默认是不开启重绘消息的,如果使用模板创建按钮必须在按钮属性中设置OwnDraw属性为True,如果动态创建按钮必须加上BS_OWNDRAW这一属性。
    下面我要重绘两个个按钮,按钮是模板创建的,是默认的IDOK和IDCANCEL按钮,希望达到的效果是
    按钮普通状态分别为

    按钮获得焦点分别为

    按钮按下状态分别为


    下面先贴出绘制部分代码,再讲解
    [cpp] view plain copy
    1. BOOL CALLBACK SelfDrawProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     LPDRAWITEMSTRUCT pdis;  
    4.     HDC hdc;  
    5.     HDC hMemDc;  
    6.   
    7.     static HINSTANCE hInstance;  
    8.     static HBITMAP hBitmapOK_D;  
    9.     static HBITMAP hBitmapOK_U;  
    10.     static HBITMAP hBitmapOK_F;  
    11.     static HBITMAP hBitmapCANCEL_D;  
    12.     static HBITMAP hBitmapCANCEL_U;  
    13.     static HBITMAP hBitmapCANCEL_F;  
    14.     static HWND    hwndOk;  
    15.     static HWND    hwndCanel;  
    16.     static BITMAP  bm;  
    17.   
    18.   
    19.     switch(message)  
    20.     {  
    21.     case WM_INITDIALOG:  
    22.         {  
    23.             hInstance = GetWindowLong(hDlg, GWL_HINSTANCE);  
    24.             hwndOk = GetDlgItem(hDlg, IDOK);  
    25.             hwndCanel = GetDlgItem(hDlg, IDCANCEL);  
    26.   
    27.             hBitmapOK_D = LoadBitmap(hInstance, TEXT("image1d"));  
    28.             hBitmapOK_U = LoadBitmap(hInstance, TEXT("image1u"));  
    29.             hBitmapOK_F = LoadBitmap(hInstance, TEXT("image1f"));  
    30.             hBitmapCANCEL_D = LoadBitmap(hInstance, TEXT("image2d"));  
    31.             hBitmapCANCEL_U = LoadBitmap(hInstance, TEXT("image2u"));  
    32.             hBitmapCANCEL_F = LoadBitmap(hInstance, TEXT("image2f"));  
    33.   
    34.             GetObject(hBitmapCANCEL_D, sizeof(BITMAP), (PTSTR)&bm);  
    35.   
    36.             //调整按钮大小和最大图片一样大  
    37.             SetWindowPos(hwndOk, HWND_TOPMOST, 0, 0, bm.bmWidth, bm.bmHeight, SWP_NOZORDER | SWP_NOMOVE);  
    38.             SetWindowPos(hwndCanel, HWND_TOPMOST, 0, 0,  bm.bmWidth, bm.bmHeight, SWP_NOZORDER | SWP_NOMOVE);  
    39.         }  
    40.         return (TRUE);  
    41.   
    42.     case WM_CLOSE:  
    43.         {  
    44.             EndDialog(hDlg,0);  
    45.         }  
    46.         return (TRUE);  
    47.   
    48.     case WM_COMMAND:  
    49.         switch (LOWORD(wParam))  
    50.         {  
    51.         case IDCANCEL:  
    52.             {  
    53.                 SendMessage(hDlg, WM_CLOSE, 0, 0);  
    54.             }  
    55.             return (TRUE);  
    56.         case IDOK:  
    57.             {  
    58.                   
    59.             }  
    60.             return (TRUE);  
    61.         }  
    62.         return (FALSE);  
    63.       
    64.     //自绘制按钮  
    65.     case WM_DRAWITEM:  
    66.         //获得绘制结构体,包含绘制的按钮DC和当前按钮状态等  
    67.         pdis = (LPDRAWITEMSTRUCT)lParam;  
    68.               
    69.         if (pdis->CtlType == ODT_BUTTON)//只绘制button类型  
    70.         {  
    71.             hdc = pdis->hDC;  
    72.             SaveDC(hdc);//保存DC,绘制完必须恢复默认  
    73.   
    74.             //绘制默认状态  
    75.             hMemDc = CreateCompatibleDC(hdc);  
    76.             SelectObject(hMemDc, pdis->CtlID == IDOK ? hBitmapOK_U : hBitmapCANCEL_U);  
    77.             BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);  
    78.             DeleteDC(hMemDc);  
    79.   
    80.             //绘制获取焦点时状态  
    81.             if (pdis->itemState & ODS_FOCUS)  
    82.             {  
    83.                 hMemDc = CreateCompatibleDC(hdc);  
    84.                 SelectObject(hMemDc, pdis->CtlID == IDOK ? hBitmapOK_F : hBitmapCANCEL_F);  
    85.                 BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);  
    86.                 DeleteDC(hMemDc);  
    87.             }  
    88.   
    89.             //绘制下压状态  
    90.             if (pdis->itemState & ODS_SELECTED)  
    91.             {  
    92.                 hMemDc = CreateCompatibleDC(hdc);  
    93.                 SelectObject(hMemDc, pdis->CtlID == IDOK ? hBitmapOK_D : hBitmapCANCEL_D);  
    94.                 BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);  
    95.                 DeleteDC(hMemDc);  
    96.             }  
    97.               
    98.             RestoreDC(hdc, -1);  
    99.         }  
    100.   
    101.         return (TRUE);  
    102.     }  
    103.     return (FALSE);  
    104. }  
    在WM_INITDIALOG函数中加载相关资源和设置按钮大小
    在WM_DRAWITEM完成主要绘制工作,获得WM_DRAWITEM消息时获得绘制的结构体,这个结构体包括当前要绘制的按钮的ID、状态等,我们主要的工作就是将对应状态的按钮贴上相应的位图即可。
    效果如下
    WM_DRAWITEM消息控件重绘是最常用的重绘技巧,在网上常见的别人封装好的自定义控件都是这样的原理。

    3.控件重绘(控件子类化)

    子类化是借鉴C++的面向对象中的继承和重载的思想,基本意思就是如果子类对消息处理了的话对应C++的重载,这时候父类就没办法再处理这个消息,除非人为的将消息传递给父类,所有的消息先流经子类再到父类,当然这一过程需要子类的配合,具体意思我们用代码来说明。
    同样是达到上一节WM_DRAWITEM绘制的按钮效果
    我们用控件子类化完成这一效果,贴出部分代码,完整代码请下载演示文件
    对话框过程函数
    [cpp] view plain copy
    1. WNDPROC btnOkOldProc, btnCancelOldProc;  
    2. BOOL CALLBACK SubclassProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
    3. {  
    4.     static HWND    hwndOk;  
    5.     static HWND    hwndCanel;  
    6.   
    7.     switch(message)  
    8.     {  
    9.     case WM_INITDIALOG:  
    10.         {  
    11.             hwndOk = GetDlgItem(hDlg, IDOK);  
    12.             hwndCanel = GetDlgItem(hDlg, IDCANCEL);  
    13.   
    14.             //窗口子类化  
    15.             btnOkOldProc = SetWindowLong(hwndOk, GWL_WNDPROC, (LONG)BtnProc);  
    16.             btnCancelOldProc = SetWindowLong(hwndCanel, GWL_WNDPROC, (LONG)BtnProc);  
    17.         }  
    18.         return (TRUE);  
    19.   
    20.     case WM_CLOSE:  
    21.         {  
    22.             EndDialog(hDlg,0);  
    23.         }  
    24.         return (TRUE);  
    25.   
    26.     case WM_COMMAND:  
    27.         switch (LOWORD(wParam))  
    28.         {  
    29.         case IDCANCEL:  
    30.             {  
    31.                 SendMessage(hDlg, WM_CLOSE, 0, 0);  
    32.             }  
    33.             return (TRUE);  
    34.         case IDOK:  
    35.             {  
    36.                   
    37.             }  
    38.             return (TRUE);  
    39.         }  
    40.         return (FALSE);  
    41.     }  
    42.     return (FALSE);  
    43. }  
    按钮过程函数(子类)
    [cpp] view plain copy
    1. typedef enum tagBUTTONSTATE  
    2. {  
    3.     BTNSTATE_DEFAULT=0,  
    4.     BTNSTATE_FOCUS,  
    5.     BTNSTATE_SELECTED  
    6. }BUTTONSTATE;  
    7. LRESULT CALLBACK  BtnProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
    8. {  
    9.     HDC hdc;  
    10.     HDC hMemDc;  
    11.     PAINTSTRUCT ps;  
    12.       
    13.     static int      id;  
    14.     static HINSTANCE hInstance;  
    15.     static HBITMAP hBitmapOK_D;  
    16.     static HBITMAP hBitmapOK_U;  
    17.     static HBITMAP hBitmapOK_F;  
    18.     static HBITMAP hBitmapCANCEL_D;  
    19.     static HBITMAP hBitmapCANCEL_U;  
    20.     static HBITMAP hBitmapCANCEL_F;  
    21.     static BITMAP  bm;  
    22.     static BOOL    bOnce = TRUE;  
    23.     static BUTTONSTATE btnOkState=BTNSTATE_FOCUS;  
    24.     static BUTTONSTATE btnCancelState=BTNSTATE_DEFAULT;  
    25.   
    26.     id = GetWindowLong(hwnd, GWL_ID);  
    27.   
    28.     //初次进入函数加载资源,模拟WM_CREATE  
    29.     if (TRUE == bOnce)  
    30.     {  
    31.         hInstance = GetWindowLong(hwnd, GWL_HINSTANCE);  
    32.   
    33.         hBitmapOK_D = LoadBitmap(hInstance, TEXT("image1d"));  
    34.         hBitmapOK_U = LoadBitmap(hInstance, TEXT("image1u"));  
    35.         hBitmapOK_F = LoadBitmap(hInstance, TEXT("image1f"));  
    36.         hBitmapCANCEL_D = LoadBitmap(hInstance, TEXT("image2d"));  
    37.         hBitmapCANCEL_U = LoadBitmap(hInstance, TEXT("image2u"));  
    38.         hBitmapCANCEL_F = LoadBitmap(hInstance, TEXT("image2f"));  
    39.   
    40.         GetObject(hBitmapCANCEL_D, sizeof(BITMAP), (PTSTR)&bm);  
    41.         SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, bm.bmWidth, bm.bmHeight, SWP_NOZORDER | SWP_NOMOVE);  
    42.   
    43.         bOnce = FALSE;  
    44.     }  
    45.   
    46.     switch (message)  
    47.     {  
    48.     case WM_CREATE:  
    49.         //注意这个消息不会进入  
    50.         return (0);  
    51.           
    52.     case WM_PAINT:  
    53.         hdc = BeginPaint (hwnd, &ps);  
    54.   
    55.         hMemDc = CreateCompatibleDC(hdc);  
    56.   
    57.         //绘制不同状态下的按钮样式  
    58.         if (btnOkState == BTNSTATE_DEFAULT && id == IDOK)  
    59.         {  
    60.             SelectObject(hMemDc, hBitmapOK_U);  
    61.         }  
    62.         if(btnCancelState == BTNSTATE_DEFAULT && id==IDCANCEL)  
    63.         {  
    64.             SelectObject(hMemDc, hBitmapCANCEL_U);  
    65.         }  
    66.         if (btnOkState == BTNSTATE_FOCUS && id==IDOK)  
    67.         {  
    68.             SelectObject(hMemDc, hBitmapOK_F);  
    69.         }  
    70.         if(btnCancelState == BTNSTATE_FOCUS && id==IDCANCEL)  
    71.         {  
    72.             SelectObject(hMemDc, hBitmapCANCEL_F);  
    73.         }  
    74.         if (btnOkState == BTNSTATE_SELECTED && id==IDOK)  
    75.         {  
    76.             SelectObject(hMemDc, hBitmapOK_D);  
    77.         }  
    78.         if(btnCancelState == BTNSTATE_SELECTED && id==IDCANCEL)  
    79.         {  
    80.             SelectObject(hMemDc, hBitmapCANCEL_D);  
    81.         }  
    82.   
    83.         BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDc, 0, 0, SRCCOPY);  
    84.         DeleteDC(hMemDc);  
    85.   
    86.         EndPaint (hwnd, &ps);  
    87.         return (0);  
    88.   
    89.     case WM_SETFOCUS:  
    90.         if (id==IDOK)  
    91.         {  
    92.             btnOkState = BTNSTATE_FOCUS;  
    93.         }  
    94.         else  
    95.         {  
    96.             btnCancelState = BTNSTATE_FOCUS;  
    97.         }  
    98.         return (0);  
    99.   
    100.     case WM_KILLFOCUS:  
    101.         if (id==IDOK)  
    102.         {  
    103.             btnOkState = BTNSTATE_DEFAULT;  
    104.         }  
    105.         else  
    106.         {  
    107.             btnCancelState = BTNSTATE_DEFAULT;  
    108.         }  
    109.         return (0);  
    110.   
    111.     case WM_KEYDOWN:  
    112.         if (wParam == VK_SPACE)  
    113.         {  
    114.             if (id==IDOK)  
    115.             {  
    116.                 btnOkState = BTNSTATE_SELECTED;  
    117.             }  
    118.             else  
    119.             {  
    120.                 btnCancelState = BTNSTATE_SELECTED;  
    121.             }  
    122.   
    123.             InvalidateRect(hwnd, NULL, TRUE);  
    124.         }  
    125.         return (0);  
    126.   
    127.     case WM_KEYUP:  
    128.         if (wParam == VK_SPACE)  
    129.         {  
    130.             if (id==IDOK)  
    131.             {  
    132.                 btnOkState = BTNSTATE_FOCUS;  
    133.             }  
    134.             else  
    135.             {  
    136.                 btnCancelState = BTNSTATE_FOCUS;  
    137.             }  
    138.   
    139.             InvalidateRect(hwnd, NULL, TRUE);  
    140.         }  
    141.         return (0);  
    142.   
    143.     case WM_LBUTTONDOWN:  
    144.             SetCapture(hwnd);  
    145.             if (id==IDOK)  
    146.             {  
    147.                 btnOkState = BTNSTATE_SELECTED;  
    148.             }  
    149.             else  
    150.             {  
    151.                 btnCancelState = BTNSTATE_SELECTED;  
    152.             }  
    153.   
    154.             InvalidateRect(hwnd, NULL, TRUE);  
    155.         return (0);  
    156.   
    157.     case WM_LBUTTONUP:  
    158.             if (id==IDOK)  
    159.             {  
    160.                 btnOkState = BTNSTATE_FOCUS;  
    161.             }  
    162.             else  
    163.             {  
    164.                 btnCancelState = BTNSTATE_FOCUS;  
    165.             }  
    166.   
    167.             InvalidateRect(hwnd, NULL, TRUE);  
    168.             ReleaseCapture();  
    169.         return (0);  
    170.           
    171.     case WM_DESTROY:  
    172.         DestroyWindow(hwnd);  
    173.         return (0);  
    174.     }  
    175.     return CallWindowProc (id == IDOK ? btnOkOldProc : btnCancelOldProc, hwnd, message, wParam, lParam);  
    176. }  
    在以上代码,我们在对话框的WM_INITDIALOG消息中强制换掉按钮原有的内建窗体过程函数,使用我们自己的BtnProc过程函数。需要注意的是在我们换掉
    按钮原有的内建窗体过程函数的时候按钮已经创建完成,所以如果我们在BtnProc的WM_CREATE设置断点,程序是不会进入的。和WM_DRAWITEM一样,我们需要按钮的不同状态时绘制,因为我们采用自己的BtnProc过程函数,所以我们只能自己来维护按钮的状态,在WM_PAINT函数中根据不同状态绘制不同样式的按钮,在其他消息中处理按钮的按下或弹起、获得焦点、或失去焦点等状态转变。
    创建效果如下

    我们基本上模拟了WM_DRAWITEM消息重绘效果:按Tab键切换焦点,按Space键按钮按下弹起(当然只是为了演示原理,会有一些Bug,你可以想办法完善他们)。在上诉代码中,我们在最后调用了原来的内建的窗体过程函数,我们处理了WM_PAINT、WM_KEYUP、WM_KEYDOWN等消息,这些消息都return (0)直接返回了,即内建的窗体过程函数没有机会处理这些消息,其他的子类没有处理的消息都传给原来内建的窗体过程函数处理了,如果我们想原来的内建窗体过程函数也处理WM_PAINT,那么将return (0)改成break即可。这就是我上面提到的子类化的实现必须依靠子类化窗体函数的配合,我们也可以将所有的消息都在子类中处理不回传给原来的内建窗口,但是这样的工作量太大,一般是不会这样做的。
    另外,可以看到相比于WM_DRAWITEM消息重绘,子类化实现控件重绘工作量要大得多,当然这样的灵活性要更大。实际上,微软提供子类化的作用更多是为了重新定制子控件的行为,比如说要将一组相同按钮按下时发送一个自定义消息,这时候就可以将这些按钮的消息子类化都先流经一个子类化窗体过程函数,然后再调用内建的窗体过程函数。

    总结来说,一般重绘控件样式使用WM_DRAWITEM消息,重新定制控件行为使用窗体子类化

    博客完整演示代码下载链接
    毕竟现在来说如果不是为了实现一个自绘控件库的话,不会使用SDK自绘的方式,下一次我会讲一下MFC中自绘方式的具体流程和实例。

    原创,转载请注明来自http://blog.csdn.net/wenzhou1219
    展开全文
  • MFC 9中的新控件Command Link Button及Vista之前平台的应用 什么是Command Link? Command LinkVista中是样新事物,请看下图: 它实质有两部分:主文本(Main Text)及注释文本(Note Text),如下图: 
  • 第5章 Web窗体的基本控件

    千次阅读 2013-02-08 15:27:55
    第5章 Web窗体的基本控件 与ASP不同的是,ASP.NET提供了大量的控件,这些控件能够轻松的实现一个交互复杂的Web应用功能。传统的ASP开发中,让开发人员最为烦恼的是代码的重用性太低,以及事件代码和页面代码不能...
  • 今天讲解3种方法传递窗体里的数据:如有窗体“传递窗体”和“接收窗体”两个窗体,通过“传递窗体控件的值传给“接收窗体”的控件。方法一:打开窗体后,直接用打开的窗体控件值等于当前窗体控件值Forms("接收...
  • 窗体添加控件

    2020-09-13 10:40:01
    窗体添加控件 Java中的GUI编程涉及几个用于修改窗口外观的控件,如前一节所述,这些控件位于Palette面板编辑器窗口中。前面的案例中,向JFrame Form添加了一个Label控件。这里有许多关于JFrame Form的基本控件还...
  • 使用下面的命令能够动态添加按钮控件数组: Dim Maxid As Integer Private Sub Command1_Click() ...可是,下次打开窗体时新添加的按钮就不存在了,如何下次打开窗体时动态添加的控件在窗体上
  • WPF的控件Command属性绑定了命令,与通常Click方式不同的是,它没有相应函数,这样的命令是这样执行的。
  • vb中获access窗体的属性及窗体控件的属性。 代码如下: Private Sub Command1_Click() Dim accApp As Access.Application Set accApp = New Access.Application accApp.Visible = False accApp....
  • 为空白窗体添加控件

    2018-09-08 16:36:59
    //定义按钮标识符是指定一个唯一的整数值标识按钮对象,数字要不一样,名称自己定义,一般的做法是程序的头部用define语句声明一个宏常量 #define IDC_MAIN_BUTTON 101 // 按钮标识符 #define IDC_MAIN_EDIT 102...
  • ArcEngine Command控件

    2017-06-21 11:39:59
    ArcEngine类库中有大量的Command控件用来与地图控件进行操作和交互。比如有一系列的地图浏览控件、地图查询控件、图斑选取控件、编辑控件来与MapControl和PageLayoutControl进行交互。这些控件被包含ESRI.ArcGIS...
  • 1 窗体 form 1.1创建窗体 form (默认名:userform) 和插入模块一样,插入用户窗体即可 1.2 如何运行/显示 窗体? 1.2.1 显示窗体的代码方法 据说,单写一句 userform1.show 也是可以的,因为VBE会自动先 load ...
  • 正常窗体下可以通过controls创建自定义控件 Set Dd = Me.Controls.Add("工程1.GrdShow", "GrdShow1") With Dd .Left = 0 .Visible = True End With 但我现在窗体是CreateWindowEx创建的。 ...
  • 我以vsFlexGrid表格控件和普通的按钮控件为例给你举个例子:表格控件名: Mygrid按钮控件名: Command1注意事项:在窗体控件的时候要将按钮控件置前!不然看不到效果的哦!---定义表格Private Sub Form_Load() ...
  • 问题1:拖动窗体时,怎么解决控件闪烁的问题 问题2:控件随着窗体放大而放大,但是当窗体缩放时不想让mschart控件的宽度改变 Public Sub ResizeForm(FormName As Form) Dim Pos(4) As Double Dim i As Long,...
  • 通过执行一段VBS代码来操控窗体内的控件也可以使用AddObject方法添加自己的类,那么动态VBS代码中也一样可以使用增加程序扩展性或是有脚本化需求的时候,这个方法还是不错的. Option Explicit Dim vbs As ...
  • VB新建一工程,有两个窗体,FORM1,FORM2 FORM1有一个PIC控件和一个按钮控件 代码如下: Option Explicit Private Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent ...
  • 今天做C# winform 窗体控件遍历时遇到控件顺序的问题,也就是控件被遍历的先后问题。实际情况如下所述。 窗体界面如下: 界面构成是:主界面有一个 Panel (Panel_14),Panel_14上面有13个子 Panel(Panel_1 ~ ...
  • 文章目录创建主菜单(菜单栏+菜单+菜单项)创建下文菜单(弹出式菜单)创建一个选项菜单 创建主菜单(菜单栏+菜单+菜单项) import tkinter root = tkinter.Tk() menu = tkinter.Menu(root)#实例化菜单栏对象 ...
  • AE Command控件使用

    2017-01-03 15:04:55
    ArcEngine类库中有大量的Command控件用来与地图控件进行操作和交互。比如有一系列的地图浏览控件、地图查询控件、图斑选取控件、编辑控件来与MapControl和PageLayoutControl进行交互。这些控件被包含ESRI.ArcGIS...
  • 我学到现在才发现控件分类不是...窗体控件 代码设置优先级更高,尽量少用属性栏,而是用代码设置, 很多属性,既可以属性栏设置,也可以 除非一些属性,代码几乎不会用到 或者一些麻烦的属性,比如pictur...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,365
精华内容 6,546
关键字:

在窗体上设置控件command