C++ Builder 初学问与答(五~十)
38)问:事件OnChange和OnScorll都可以描述滚动条的滚动事件,他们有什么区别吗?
答:事件OnChange和OnScorll是有区别的。只要属性Position的值发生改变,那么无论这个值是用户操作滚动条引起的还是通过程序修改的,都会产生OnChange事件。而OnScorll事件只有在用户操作滚动条时才发生。当用户操作滚动条时,首先发生OnScorll事件,然后OnChange事件才会发生。
下面我们共同看一个程序来加深对滚动条的理解。
在窗体上放置一个水平滚动条ScrollBar1和一个垂直滚动条ScrollBar2,以及一个记录滚动条事件的Memo组件。当用户操作滚动条时,在Memo组件中就会显示发生的事件。
选择Memo组件,把它的ScrollBars属性设置为ssVertical。
选择水平滚动条组件,双击OnScroll的Value域,系统将生成事件处理模板,输入这样一段代码。
void __fastcall TForm1::ScrollBar1Scroll(TObject *Sender, TScrollCode ScrollCode, int &ScrollPos) { AnsiString temp; if(Sender==ScrollBar2) temp="垂直滚动条的"; else temp="水平滚动条的"; switch(ScrollCode) { case scLineUp:temp+="scLineUp";break; case scLineDown:temp+="scLineDown";break; case scPageUp:temp+="scPageUp";break; case scPageDown:temp+="scPageDown";break; case scPosition:temp+="scPosition";break; case scTrack:temp+="scTrack";break; case scTop:temp+="scTop";break; case scBottom:temp+="scBottom";break; case scEndScroll:temp+="scEndScroll";break; default: temp+="未知";break; } Memo1->Lines->Append(temp+"事件"); |
第一条语句声明一个AnsiString类型的变量,用来存放将要显示的字符串。
第二条语句判断发送事件的对象,如果是垂直滚动条,将temp初始化为 “垂直滚动条的”,否则初始化为 “水平滚动条的”。
第三条语句根据ScrollCode的值,显示相应的事件。
最后一条语句将要显示的字符串加到文本框的末尾。
在窗体中选择垂直滚动条组件,选择Events标签,单击OnScroll的Value域,从下拉列表框中选择ScrollBar1Scroll事件处理程序。这样,垂直滚动条和水平滚动条将共用一个事件处理程序。
选择水平滚动条组件,选择Events标签,双击OnChange域,然后输入:
Memo1->Lines->Append("水平滚动条的OnChange事件");
它将在文本框后面显示水平滚动条改变的信息。
类似的,给垂直滚动条的OnChange事件加上如下代码:
Memo1->Lines->Append("垂直滚动条的OnChange事件");
详细代码如下:
//-------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" //-------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //-------------------------------------------- |
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //-------------------------------------------- void __fastcall TForm1::ScrollBar1Scroll(TObject *Sender, TScrollCode ScrollCode, int &ScrollPos) { AnsiString temp; if(Sender==ScrollBar2) temp="垂直滚动条的"; else temp="水平滚动条的"; switch(ScrollCode) { case scLineUp:temp+="scLineUp";break; case scLineDown:temp+="scLineDown";break; case scPageUp:temp+="scPageUp";break; case scPageDown:temp+="scPageDown";break; case scPosition:temp+="scPosition";break; case scTrack:temp+="scTrack";break; case scTop:temp+="scTop";break; case scBottom:temp+="scBottom";break; case scEndScroll:temp+="scEndScroll";break; default: temp+="未知";break; } Memo1->Lines->Append(temp+"事件"); } |
//-------------------------------------------- void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Memo1->Lines->Append("水平滚动条的OnChange事件"); } //-------------------------------------------- void __fastcall TForm1::ScrollBar2Change(TObject *Sender) { Memo1->Lines->Append("垂直滚动条的OnChange事件"); } |
现在,编译并运行这个程序,点击滚动条,可以看出滚动条的事件产生情况。
哪个事件优先你能看出来吗?你一定会对OnScroll同一事件产生的两个属性值先后被OnChange分开而不解是吗?你好好看一下什么时候产生scEndScroll值J
其实我们如果要了解其他某个组件所产生的事件之间的顺序,也可以用同样的方法来实现。
39)问:轨迹条(TrackBar)有那些主要属性呢?
答:轨迹条与滚动条有相似之处,它也有一个类似于滚动块的滑动块,可以用鼠标或者使用方向键移动。轨迹条的某些属性与滚动条完全相同,例如Max,Min和Position。但是它还有一些特殊的属性。
1.LineSize属性,用于指定当用户按下方向键时,轨迹条的滑动块移动的距离。
2.PageSize属性,用于指定当用户按下PgUp和PgDn时,轨迹条上的滑动块移动的距离。
3.Frequency属性,用于设置轨迹条刻度的单位。如果Max-Min等于100,而这个属性等于10,那么轨迹条就被分成了10等份。
4.Orientation属性,用来确定轨迹条的放置方向,它有两个值,tbHorizontal表示水平放置,tbVertical表示是垂直放置的。缺省情况下是水平放置的。
5.SelStart这个属性用来设置选择的起点。
6.SelEnd这个属性用来设置的选择终点。
7.TickMarks这个属性用来设置轨迹条标尺的位置。它有三个值,分别表示三种不同的滑动块位置:
tmBottomRight表示垂直放置时标尺显示在轨迹条的下面或者水平放置时显示在右边。
tmTopLeft表示垂直放置时标尺显示在轨迹条的上面或者水平放置时显示在左边。
tmBoth则表示轨迹条的两边都有标尺。
8.TickStyle这个属性用来确定轨迹条标尺的样式。它有三个值,分别表示三种不同的标尺样式:
tsAuto表示自动显示标尺的刻度。
tsManual表示需要使用SetTick过程才能设置在某个位置显示刻度。
tsNone表示标尺不显示刻度。
40)问:轨迹条有哪些常用的事件呢?
答:当用户用鼠标或者键盘操作轨迹条时,就会产生OnChange事件。不过要注意,在程序中改变属性Position的值不会产生OnChange事件,这一点与滚动条不同。
41)问:进程条(ProgressBar)如何用呢?
答:它的属性与上面的那些组件差不多,象Max、Min、Orientation、Position与上面的含义大同小异。其中Position是这个组件的关键,它用来设置或者返回进程条的填充小方块的当前位置,当进条结束的时候,这个位置应该是位置上限。
Smooth:布尔类型,缺省为false。这时的填充是按格进行的,如果设置为true,则填充是平滑进行的。
StepBy:其声明为:void _fastcall StepBy(int Detla);这个函数用来向前填充进程条,同时改变Position的属性值。它的增加量由参数Detla来指定。
StepIt:其声明为:void _fastcall StepIt(void);这个函数用来向前填充进程条,同时改变Position的属性值。其填充步长由Step属性来指定。
Step:这个属性用来设置当调用StepIt函数时,Position位置的增加量。缺省为10。
42)问:UpDown控件有什么用?
答:这个控件是一对上下箭头按钮,按下按钮时会自动增加或减少某个数值。它通常有一个附属组件,由UpDown控件的Associate属性指定。如果这个组件是一个编辑框,那么编辑框就会自动显示UpDown控件的属性Position的值。
UpDown控件也有一些滚动条和轨迹条所具备的属性,例如Max,Min,Position,Orientation,它们的意义也基本上相同,不过UpDown控件同样有一些特殊的属性。
AlignButton为udLeft时,表示将UpDown控件放置在附属组件的左边,为udRight时,则表示将UpDown控件放置在附属组件的右边。
Associate用来指定关联的附属组件。
ArrowKey属性,当属性为True时,按下上下方向键就像按下UpDown控件的上下箭头一样。缺省值为True。
Thousands属性, 当属性为True时,如果Position超过1000,那么就会自动给所显示的数值加上一个千分位。缺省值为True。
Wrap属性,当属性为True时,如果Position的值大于Max,那么就会自动回到Min属性指定的值。缺省值为False。
43)问:UpDown控件有哪些常用的事件呢?
答:UpDown控件有两个常用的事件:OnChanging和OnClick。当用户正在按下上下箭头时,将发生OnChanging事件;当用户按下上下箭头时,会发生OnClick事件。
我们共同来看一个例子:
在窗体上放置两个Edit控件,和两个UpDown控件,将两个UpDown控件的Orientation属性分别取值udHorizontal和udVertical。
将第二个UpDown控件的Max的取值为5000,Position值为100,将他们的Associate属性分别与两个编辑框关联。在对应事件中输入如下代码:
void _fastcall TForm1::UpDown1Changeing(TObject *Sender,bool &AllowChange) { Edit1->Text=UpDown1->Position; } // 注意参数AllowChange可以用来指定是否允许改变Position属性的值。 void _fastcall TForm1::UpDown2Click(TObject *Sender,TUBtnType Button) { Edit2->Text=UpDown2->Position; } |
//注意参数Button表示哪个按钮被按下:btNext为上箭头或者右箭头;btPrew为下箭头或者左箭头。
44)问:在UpDown右边有一个HotKey组件是做什么的?
答:热键HotKey用来在程序运行期间动态地指定某个组件或者菜单的快捷键。在它的属性中你只要记住HotKey属性就可以了,这个属性用来返回用户指定的快捷键。
代码如下:
//-------------------------------------- #include <vcl.h> #pragma hdrstop
//-------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //-------------------------------------- |
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //-------------------------------- void __fastcall TForm1::X1Click(TObject *Sender) { Close(); } //------------------------------- void __fastcall TForm1::HotKey1Change(TObject *Sender) { X1->ShortCut=HotKey1->HotKey;//X1为菜单名,注意有些书介绍用OnEnter事件,但我觉得不妥,不防你也试一下,用OnEnter你将得不到什么关联J } //------------------------------- |
为什么这里用了一个Button1?如果你的程序中没有另外一个组件可以获得焦点,你将感受不到快捷键的功能的,因为焦点会停在HotKey组件上,在这里就是想将焦点转移一下,然后用快捷键试一下效果J
6.图形列表组件TreeView
45)问:有时侯,我希望实现类似资源管理器对文件夹管理的显示方式,和对文件的管理显示方式,在C++Builder中应该怎么办?
答:在C++Builder中可以直接使用图形列表组件来实现,它包括TreeView、ListView以及Outline组件。其中Outline组件实际上是基于Windows 3.1环境的组件,而且无论从功能还是使用的角度来看,它都无法与TreeView相比,因此完全可以用TreeView组件代替Outline。所以我将主要介绍前面两种组件。这两个组件都在页Win32中。
46)问:TreeView组件是一个怎样的组件?
答:TreeView组件比较复杂,所以我们首先简单地介绍一下这个组件的基本使用情况,然后再深入讨论。
TreeView组件采用了树形结构,最典型的例子就是Windows 95的资源管理器,它用树形结构显示磁盘上的文件夹和文件。因此TreeView组件能够清晰地显示层次关系。
47)问:请给我演示一下怎样在设计状态编辑TreeView好吗?
答:首先在窗体上放置一个TreeView组件,然后在对象编辑器中单击属性Items中的省略号按钮,就会打开一个项目编辑器。
TreeView组件的项目编辑器是一个在设计时专门增加、删除节点和为节点赋予图标的工具。从屏幕上可以看到,项目编辑器由项目组框和项目属性组框组成。项目组框由一个项目列表框、按钮New Item、按钮New SubItem、按钮Delete和按钮Load组成。当你刚打开项目编辑器时,项目列表框是空的,New SubItem和Delete按钮处于失效状态。
项目属性组框由编辑框Text、编辑框Image Index、编辑框Selected Index和编辑框State Index组成。
项目组框可以创建和删除节点、子节点。如果要载入一个已经存在的TreeView节点,可以单击按钮Load。要创建一个新项目,单击New Item,然后在文本编辑框中输入新节点的标题。这时,New SubItem按钮由失效变为有效,允许你在节点下再嵌套子节点。如果项目列表框中显示了节点,那么Delete按钮也会变的有效。你只要在项目列表框中选中这个节点然后单击Delete按钮,就可以删除这个节点了。
要注意的是:如果删除的节点还包括子节点,那么子节点也会被同时删除。
项目属性组框主要是为当前选中的节点(或子节点)设置属性的。其中编辑框Text可以修改节点的标题。如果要在不是当前被选中的节点的左边显示图像,那么应该在编辑框Image Index中输入图像的索引号。要禁止显示图像可以把这个节点Image Index设置为缺省值-1。
如果要在被选中的节点的左边显示图像,应该在编辑框Selected Index中指定图像的索引号,索引号是从0开始的。要禁止显示图像可以把它设置为缺省值-1。
如果要在节点的左边多显示一个图像,可以在编辑框State Index中输入图像的索引号。这个索引号代表Tree View组件中的StateImages属性所表示的图像列表的索引。要禁止显示图像可以把这个项目设置为缺省值-1。
注意:Image Index与 Selected Index 使用的是Images指定的ImageList;而State Index使用的是StateImages 指定的ImageList。
48)问:TreeView的属性太多了,我想问我一定要清楚哪些主要属性?
答: Items : 该属性包含TreeView组件中的所有节点,它是TtreeNodes的对象,在设计时,你可以使用项目编辑器来增加、删除和修改节点。在运行期间,可以通过Items属性访问每一个节点,并且也能够增加、删除和修改节点。而每一个节点又都是一个TtreeNode对象。
AutoEWxpand:布尔类型。为true时,则当前被选择的节点将自动扩展,没有被选择节点将自动折叠。缺省为false,这时扩展和折叠要用户自己指定。
DragMode:其声明为:_property TdragMode DragMode;这个属性用来设置树状视图的拖放模式。缺省为dmManual,用户要拖放节点需要调用BeginDrag才能拖放。若设置为 dmAutomatic,则树状视图将支持自动拖放,用户可以把一个节点拖到另一个位置。
Images:用来设定TreeView中的不同节点的图标。
StateImages:用不同的的图像来表示节点的不同状态。
在TreeView组件中,每个节点除了可以有一个文字标题外,还可以附加图标,这样可以使用户界面更加直观。这两个属性就包含着节点所使用的图标。
这两个属性都是TImageList的对象,所以如果要在TreeView组件中给节点添加图标,就必须使用图标列表组件给这两个属性赋值。如果在窗体中放置了图标列表组件,那么单击属性Images中的下拉按钮,就可以选取这个组件了。
ShowButtons属性值为True时表示凡是有子节点的节点将自动具有扩展按钮(即+号按钮)和折叠按钮(即减号按钮),单击它们就可以扩展和折叠节点。属性值为False时,不显示这两个按钮,缺省值为True。
ShowLines属性用来确定是否显示在节点和子节点之间的连线。缺省值为True。
ShowRoot属性用来确定与顶层节点连线是否显示。缺省值为True。
HideSelection属性用来确定当输入焦点从TreeView移动到其它控件上时,被选中的节点是否处于被选中状态。当属性值为True时,不再处于选中状态。缺省值为True。
Indent属性用来设置子节点与父节点之间缩进显示的距离。
ReadOnly用来确定用户是否可以直接修改节点的标题。当属性值为False时,表示可以修改。修改方法是,选中要修改的节点并单击进入编辑状态。缺省值为False。
SortType用来设置节点进行排序的方式。它有以下几种取值:
stNone:不排序(缺省值);
stData:当节点所关联的对象发生改变时重新排序;
stText:当节点的标签发生变化时重新排序;
stBoth:当节点所关联的对象或者节点的标签发生改变时重新排序。
以上排序原则为在同一层的节点以标签的字母顺序排序。
此外,还有一些运行时态属性。
TopItem:这个属性用来设置一个节点,这个节点将显示在树状视图的最上面。
RowSelcet:缺省为false。若设置为true,则表示当前选择的节点所在的整行都将加亮显示。当ShowLines设置为true时,这个属性无效。
Selected:用来返回当前选择的节点。
49)问:属性Images和StateImages有什么区别呢?
答:每一个节点可以有两个附带的图标。一般情况下只需要显示一个图标,这时我们经常使用Images来指定图标,也就是说给属性Images赋予一个ImageList对象。如果某个节点要显示两个图标,那么还需要给属性StateImages赋予一个ImageList对象。然后给节点的属性StateIndex指定图标的索引号。具体的方法是在项目编辑器中,修改编辑框State Index的值。
其实在显示一个图标时,也可以使用属性StateImages。但是使用Images属性有一个优点,就是它可以为处于不同状态的节点指定不同的图标。例如在Windows 95的资源管理器中,被选中的节点显示一个打开的文件夹,而没有选中的节点显示一个关闭的文件夹。要实现这一点很简单,因为项目编辑器中的编辑框Image Index指定的图标在节点未选中时显示,而编辑框Seleted Index指定的图像在节点选中时显示。
50)问:请在前面设计的TreeView组件的基础上,给TreeView组件中的节点增加图标。好吗?
答:好的,首先在窗体上放置两个ImageList组件,这两个组件将具有缺省的名字ImageList1和ImageList2。
其次、编辑ImageList组件:ImageList组件实际上是一个图标列表,它可以包含大量的图标,这些图标的大小由属性Width和Height确定,缺省值都是16。
ImageList刚建立时不包含任何图标,你需要把已创建好的图标引入到ImageList中。这个工作可以通过ImageList编辑器完成。
用鼠标右键单击ImageList组件,在快捷菜单中选择ImageList Editor或双击一下该组件,就会出现下面这个编辑器。
单击Add按钮,在弹出的Add Images对话框中选择要引入的图像文件,图标就会出现在Images框内。从图中可以看出,引入的图标被自动赋予索引号。
为了使用上的方便,每一个图标都有一个编号,这个编号就是这个图标的索引号。如果要引用某个图标,只需要使用它的索引号就可以了。
要注意的是:这个编辑器会自动把引入的图像分解成16×16的图标,之所以尺寸为16×16是因为ImageList的属性Width和Height被设置成了16。如果图标的尺寸不一样,那么可以修改这两个属性,使其满足图标的要求。
然后、继续用项目编辑器给节点增加图标:我们已经在图标列表组件中加入了两个图标,其索引号分别为0和1,接着可以使用项目编辑器给节点增加图标。
再次打开项目编辑器,输入所有节点。
修改节点的ImageIndex, SelectedIndex属性,
要注意的是,编辑框Image Index中的索引号是0,编辑框Selected Index中的索引号是1。也就是说,在选中或未选中状态下,该节点显示的图标不同。
修改所有节点的图标索引号,使包含子节点的节点有两种不同的图标,而不含子节点的节点只有一种图标,也就是编辑框Image Index和Selected Index的索引号相同。
最后,修改TreeView1组件的Images属性,从下拉列表中选择ImageList1组件。
从屏幕上可以看到,图标已经出现在控件里面了。
51)问:那怎样给节点增加第二个图标呢?
答:要给节点增加第二个图标,首先必须再创建一个图标列表组件,然后把这个图标列表赋值给TreeView组件的属性StateImages。当然还要给这个图标列表引入图标。
完成这些工作后,我们就可以进入项目编辑器,选中要增加第二个图标的节点,修改编辑框State Index中的索引号即可。
52)问:TreeView有哪些重要的函数呢?
答:AlphaSort:其声明为:bool _fastcall AlphaSort(void);这个函数用来对所有的节点按标签的字母顺序排序。若排序成功,则返回true。
FullCollapse:其声明为void _fastcall FullCollapse(void);这个函数的作用是折叠所有的节点,直到最顶层为止。
FullExpand:其声明为 void _fastcall FullExpand(void);这个函数的作用是展开所有的节点,直到最底层为止。若ShowButtons属性为true,则所有的“+”都会变成“-”。
GetHitTestInfoAt:其声明为THitTests _fastcall GetHitTestInfoAt(int X,int Y);这个函数返回指定点与树状视图之间的位置关系。这个点的位置由参数X、Y确定。THitTests是个集合,它可能包含下面的元素:
HtAbove:在树状视图的客户区上方;
HtBelow:在树状视图的客户区下方;
HtNowhere:在树状视图的客户区内但在最后一个节点下面;
HtOnItem:在某个节点的标签或者图标上;
HtOnButton:在某个节点的左边的图标上;
HtOnIcon:在某个节点的图标上;
HtOnIndent:在某个节点的缩进线上;
HtOnLabel:在某个节点的标签上;
htOnRight:在某个节点的右边;
htOnStateIcon:在某个节点的状态图标上;
htToLeft:在客户区的右边;
htToRight:在客户区的的左边。
GetNodeAt:其声明为TTreeNode*_fastcall GetNodeAt(int X,int Y);这个函数用来返回指定点所在的节点。参数X、Y为这个点的坐标。如果这个点没有节点,则返回NULL。
53)问:能介绍一下TTreeNodes与TTreeNode?
答:在BCB中,树状视图的节点是通过TTreeNodes对象来管理的,通过这个对象可以对树状视图进行动态的增加、删除、插入、移动节点等操作。而每一个节点都是一个TTreeNode对象。
TTreeNode的主要属性和函数:
Count:这个属性用来返回某个节点所拥有的子节点的数目。它不包括其子节点。
Item:其声明为:_property TTreeNode* Item[int Index];这个属性用来访问该节点的某个子节点。其中参数Index为其子节点在所有子节点中的位置。
Index:这个属性用来返回该节点在其父节点的所有子节点中的位置。
Text:这个属性用来设置或者返回节点的标签。
Delete:这个函数用来删除该节点本身。
DeleteChildren:这个函数用来删除该节点的所有子节点。
EditText:这个函数用来对节点进行就地编辑。
IndexOf:其声明为:int _fastcall IndexOf(TTreeNode* Value);这个函数将返回该节点的某个子节点的序号。子节点由参数value指定。如果参数value指定的节点不是该节点的子节点,函数将返回-1。
TTreeNodes的主要属性和函数:
Count:这个属性用来返回树状视图中节点的数目。
Add:其声明为:TTreeNode *_fastcall Add(TTreeNode,const System::AnsiString S);这个函数将在树状视图中增加一个节点,新节点成为参数Node 指定的节点的父节点的最后一个子节点,参数S为新节点的标签。
AddChild:其声明为: TTreeNode *_fastcall AddChild(TTreeNode,const System::AnsiString S);增加的新节点为参数Node指定的节点的最后一个子节点。
AddChildFirst:其声明为: TTreeNode *_fastcall AddChildFirst(TTreeNode,const System::AnsiString S);这个函数与AddChild不同的是新增加的节点为Node节点的第一个子节点。
AddFirst :其声明为:TTreeNode *_fastcall AddFirst(TTreeNode,const System::AnsiString S);这个函数与Add 相似,不同的是新节点成为Node节点的父节点的第一个子节点。
Clear:这个函数将把整个树状视图都清空。
Delete:其声明:void _fastcall Delete(TTreeNode *Node);这个函数将删除参数Node 指定的节点。
Insert:其声明为:TTreeNode _fastcall Insert (TTreeNode* Node,const System::AnsiString S);这个函数用来在参数Node 指定的节点之后插入一个新的节点,新节点的标签由参数S指定。
7.图形列表组件ListView
54)问:TreeView组件能够实现资源管理器左边显示区的内容,可是它右边显示区能将列表用各种不同的方式显示,例如大图标方式、小图标方式、简单列表方式和详细列表方式。这在C++Builder应该怎样来实现?
答:这可以用ListView组件来实现,ListView组件从功能上讲与列表框相似,但是从组件的属性来看与TreeView相似。ListTiew组件所建立的列表可以用各种不同的方式显示,例如大图标方式、小图标方式、简单列表方式和详细列表方式。
ListView组件中的一部分属性与TreeView组件中的属性是相同的,例如Items,StateImages等。还有一些属性是ListView特有的。
LargeImages属性用来设置存放大图标的图标列表,当列表处于大图标显示方式时,列表使用这个图标列表中的图标显示。
SmallImages属性用来设置存放小图标的图标列表,当列表处于小图标显示方式时,列表使用这个图标列表中的图标显示。
MultiSelected属性可确定用户是否可以同时选择多个项目。缺省值为false。
属性ViewStyle确定了显示风格,可以取这样一些值。
vsIcon:列表以大图标方式显示,可以进行拖放操作。只显示第一层节点,放置方式由Arrangement决定。
vsSmallIcon:列表以小图标方式显示,可以进行拖放操作。只显示第一层节点,放置方式由Arrangement决定。
vsList:以简单列表的方式显示,不能进行拖放操作。节点是竖向放置的,只显示第一层节点,Arrangement不起作用。
vsReport:以详细列表的方式显示,显示的信息可以分成多列。最左边的列显示图标和文字,其余的列显示详细信息。节点是竖向放置的,每一层均已经展开,只有此时Columns的设置才起作用,Arrangement不起作用。
属性Columns 当属性ViewStyle被设置为vsReport时,列表中的每一项可以分成多列显示,这个属性用于设置列数和列的标题。
在设计时,按下Columns属性Value列中的省略号按钮可以打开一个ListView Columns编辑器,在这个编辑器中可以生成和编辑列及列的标题。
属性ColumnClick的值为True时,列表中的标题可以作为按钮使用, 这个属性最常见的应用是在Windows 95的资源管理器中,单击文件名或者其它几个列标题,列表中的文件就会按照文件名重新排序。
属性Items 包含列表中所有项目的集合。与TreeView组件中的Items属性类似,这个属性也有一个项目编辑器,你可以使用这个编辑器在设计时增加、删除项目,设置项目的图标索引以及标题等。它的使用方法与TreeView组件的Items编辑器基本相同。
IconOptions属性用来设置图标的显示方式。它还包含几个子属性:
(1)Arrangement属性:当该属性等于iaTop时,表示图标从左到右排列在ListView组件的顶部;等于iaLeft时,表示图标从上到下排列在组件的左边。
(2)AutoArrange属性:等于True时,表示增加、删除或移动图标时图标将自动重排。
(3)WrapText属性:等于True时,表示当项目的标题超过图标的宽度时标题将换行。
此外,还有运行时态属性
Selected属性用来返回列表中被选中的项目。
TopItem属性用来返回列表中当前可见的最顶端的项目。
55)问:请您编写一个程序来帮助我理解和记忆ListView控件好吗?
答:好的,选择File菜单的New Application菜单项,打开一个空的工程,在窗体上放置一个ListView控件,两个ImageList控件,一个ListBox控件。
选中ImageList1控件,将它的Height和Width属性设置为32。双击ImageList1控件,打开图像列表编辑框。点击Add按钮,选择一个大图标文件。点击打开按钮。然后点击OK按钮。同样方法给ImageList2控件加上小图标。选择TreeView控件,将它的LargeImages属性修改为ImageList1。
将它的SmallImages属性修改为ImageList2。在ListView控件上点击鼠标右键,选择Columns Editor选项(当然我们只要点击属性Columns右边的“…”就可以了)。弹出Columns编辑对话框。点击Add New工具按钮加入四个列表,选中它们,将它们的caption属性修改为名称、大小、类型、修改时间。
关闭列表编辑对话框。
在ListView控件上点击鼠标右键(同样我们只要点击 Itmes属性右边的“…”也一样),选择Items Editor选项。弹出Items编辑对话框(与TreeView的Items Editor差不多),输入树型结构文档。在这里,点击“New Item”按钮可以新建一个项,“NewSubItem”按钮用来设置这个项的子项。“Delete”用来删除某个项,包括它的子项。项司性编辑框中,Caption为这个项的标题,另外两个为项的图标。
编写列表框的OnClick事件代码如拢?/P>
void __fastcall TForm1::ListBox1Click(TObject *Sender) { switch(ListBox1->ItemIndex) { case 0: ListView1->ViewStyle=vsIcon; break; case 1: ListView1->ViewStyle=vsSmallIcon; break; case 2: ListView1->ViewStyle=vsList; break; case 3: ListView1->ViewStyle=vsReport; break; } } //------------------------------ |
把ListView中的ViewStyle属性改为vsReport,否则你将得不到上面的那种显示方式。
最后,编译并运行这个程序,选择不同的类型,我们可以看到TreeView控件的效果。
56)问:在TreeView、ListView中的图标管理能再说一下吗?
答:图形列表组件很难理解的,主要问题是不太容易掌握它使用图标的方式。图形列表组件使用一个名为ImageList的组件管理所有的图标,ImageList给每个图标提供一个索引号,这样在TreeView或ListView组件引用时就不必指出图标的名字而直接指定索引号就可以了。用ImageList集中管理图标,用户使用图标索引号的方式的确从某种角度减轻了编程人员的负担,但是也使得他们在编程时必须记住一大堆编号代表的各是哪一种图标,这就在一定程度上削弱了ImageList的优势,也许在C++Builder以后的版本中能有更好的方法。
57)问:如何动态的创建一个列表视图呢?
答:我们举例说明。我先建立如图的界面。
并编写如下的代码:
//------------------------------ #pragma hdrstop #include "Unit1.h" //------------------------------ #pragma resource "*.dfm" TForm1 *Form1; //------------------------------ : TForm(Owner) { } //------------------------------ { const char Names[6][3][10] = {{"广东省","广州市","华南"}, {"上海市", "上海市","华东"}, {"北京市", "北京市","华北"}, {"辽宁省", "沈阳市","东北"}, {"湖北省", "武汉市","华中"}, {"云南省", "昆明市","西南"}};//准备各项的文字 TListColumn *NewColumn;//创建一个栏 TListItem *ListItem;//创建一个Items ListView1->ViewStyle = vsReport;//定义显示方式 Button3->Enabled=false; NewColumn = ListView1->Columns->Add();//增加栏 NewColumn->Caption = "省份";//添置栏的名称 NewColumn = ListView1->Columns->Add(); NewColumn->Caption = "省会"; |
NewColumn = ListView1->Columns->Add(); NewColumn->Caption = "方位"; for (int i = 0; i < 6; i++) { ListItem = ListView1->Items->Add(); ListItem->Caption = Names[i][0];//添置父节点 ListItem->SubItems->Add(Names[i][1]);//添置子节点 ListItem->SubItems->Add(Names[i][2]); } }/*此处有些朋友总感觉为什么还要创建TListColumn *NewColumn; |
TListItem *ListItem;其实我们只要这样想就可以了,我们再手功创建时打开Columns、Items时等于又创建了一个项目,他们并不存在,我们却要用,所以只能用这种方法来动态创建了J*/
//------------------------------ void __fastcall TForm1::Button1Click(TObject *Sender) { TListItem *temp; temp=ListView1->Items->Add(); temp->Caption=Edit1->Text; temp->SubItems->Add(Edit2->Text); temp->SubItems->Add(Edit3->Text); } //------------------------------ { ListView1->Items->Clear(); } //------------------------------ { ListView1->ViewStyle = vsReport; Button3->Enabled=false; } |
//------------------------------ { ListView1->ViewStyle = vsIcon; Button3->Enabled=true; } //------------------------------ { ListView1->AlphaSort(); //这个函数的作用是把列表视图中的项按照它们的标签字母顺序排列,成功返回true。 } //------------------------------ |
58)问:我要想对TreeView、ListView里面的项进行操作如何来进行呢?
答:我们一般都是通过OnChange或OnChangeing事件来完成的,我们通过一个例子来看一下他们的应用及区别,我们在窗体中放上一个TreeView和一个Memo组件,并在TreeView的OnChange和OnChangeing分别写如下代码:
//------------------------------ #pragma hdrstop #include "Unit1.h" //------------------------------ #pragma resource "*.dfm" TForm1 *Form1; //------------------------------ : TForm(Owner) { } //------------------------------ { if(Node->Text=="辽宁") Memo1->Lines->Add("I'm change!") ; } |
//------------------------------ bool &AllowChange) { AllowChange=false; if(Node->Text=="辽宁") Memo1->Lines->Add("I'm changeing!") ; } //------------------------------ 只有OnChangeing起了作用,为什么呢?再看下面代码: //------------------------------ #pragma hdrstop #include "Unit1.h" |
//------------------------------ #pragma resource "*.dfm" TForm1 *Form1; //------------------------------ __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //------------------------------ void __fastcall TForm1::tvwChange(TObject *Sender, TTreeNode *Node) { if(Node->Text=="辽宁") Memo1->Lines->Add("I'm change!") ; } //------------------------------ void __fastcall TForm1::tvwChangeing(TObject *Sender, TTreeNode *Node, bool &AllowChange) { //AllowChange=false;这与把此句写成AllowChange=true是一样的 if(Node->Text=="辽宁") Memo1->Lines->Add("I'm changeing!") ; |
两个事件都起作用了,并且OnChangeing先于Onchange发生,所以原因很显然:这一切都是OnChangeing的参数AllowChange造成的,所以要注意对这个参数的使用。
在ListView中只要把上面的Node->Text改成Item->Caption就可以了。
8.图形类组件
59)问:Windows是一个图形系统,C++Builder是怎样处理图形的呢?
答:C++Builder通过提供图形组件来处理图形,其中比较常用的有图像组件(Image)、几何图形组件(Shape)和画板组件(PaintBox)。前两个组件位于Additional页中,画板组件位于System页中。
在BCB中,Form、Image、BitMap、PaintBox等都具有绘图能力,这些组件中都有Canvas属性。
60)问:图像控件有哪些特殊的属性?
答:图像组件可以显示各种以文件形式存储在磁盘上的图形。它有这样一些特殊的属性。
AutoSize属性,当属性值为True时,图像组件会自动调节尺寸以便适应图像组件的大小。缺省值为True。
Stretch属性,当该属性为True时,图像的尺寸自动调整并填满整个图像组件的范围。但ICO文件无法产生这种效果。缺省值为False。
Transparent属性,确定是否允许图像中指定的颜色透明,从而使组件下面的物体显示出来。缺省值为False。
Center属性,当该属性为True时,图像居中显示;否则,从左上角开始显示。
属性Picture,你可以在设计阶段利用这个属性指定所要显示的图片,也可以在运行期间再显示。前面一种情况下,你可以单击Picture属性的Value列中的省略号按钮,这时会出现一个图片编辑对话框,单击对话框中的Load按钮就可以选择载入要显示的图片了。
61)问:如果是在程序运行期间载入图片,应该怎么办?
答:如果是在程序运行期间载入图片,那么可以使用TPicture类的方法LoadFromFile,这个方法可以载入存储在磁盘上的图像文件。例如要载入D盘根目录下的picture.bmp,可以这样调用:
Image1->Picture->LoadFromFile("d:\\picture.bmp");
这个控件可处理的图片文件的类型有:.BMP文件、.ICO文件、.EMF和.WMF等多种图像文件。如果你试图载入一种无法识别的文件类型,将会产生异常错误。
62)问:能说一下AutoSize与Stretch的区别吗?
答:好的,在窗体上放置两个图像组件,将左边的图像组件的AutoSize属性为True,所以载入任何图片后,组件的尺寸与图片的尺寸完全相同。而且如果你增大或减小图像组件的尺寸,图片的显示仍然不变。将右边的图像组件的AutoSize属性设置为False,将它的Stretch属性设置为True。当你增大或减小图像组件的尺寸,图片将按比例增大或缩小,也就是说图片始终充满整个组件。
63)问:TShape控件有什么重要属性?
答:这个组件可以显示一些常见的几何图形,例如矩形、圆、椭圆等。几何图形组件有几个比较重要的属性:Brush,Pen 和Shape。
Brush属性,用来设置几何图形内部的填充特性,包括填充的图案(Style)和颜色(Color)等。
Pen属性,用来设置几何图形的外框特性,包括画笔的颜色(Color)、线型(Style)和线宽(Width)及Mode关系等。
属性Shape,用来指出所显示的几何图形类型。这个属性可以有六种不同的值,分别表示六种不同的几何图形;
(1)stEllipse表示椭圆。
(2)stRectangle表示矩形。
(3)stRoundRect表示圆角矩形。
(4)stRoundSquare表示圆角正方形。
(5)stSquare表示正方形。
(6)stCircle表示圆。
在设计阶段,你可以使用鼠标改变图形的尺寸。在运行期间,可以通过Height和Width属性改变图形的尺寸。
64)问:属性Brush前面有一个加号,它是什么意思?
答:它表示这个属性是可以展开的,也就是说,它还包含一些子属性。单击加号,可以发现它的子属性是:Color和Style。
Color属性包含一系列C++Builder预定义的颜色,你可以从中为显示的几何图形选择一种填充颜色。
Style属性确定了几何图形的填充样式,它可以分别取这样8种值:bsBDiagonal, bsClear, bsCross, bsDiagCross, bsDiagonal, bsHorizontal, bsSolid和bsVertical。
属性Pen也象属性Brush一样有子属性。它的子属性是Color, Mode, Style和Width。其中最常用的就是Style和Width,它们分别表示线型和线宽。子属性Style可以取这样一些值来画边框:
psSolid
表示实线。
psDash
表示破折号。
psDot
表示圆点。
psDashDot
表示破折号和圆点。
psDashDotDot
表示破折号、圆点、圆点。
psClear
表示没有线。
psInsideFrame
表示内框实线。
65)问:这个画板组件有什么作用?
答:画板组件主要为用户提供一块作图区域,用户可以使用绘图语句在这个作图区域上画出各种不同的图形。
要注意的是,由于画板组件没有边界,所以通常应该把它放在一个有边界的容器类组件上。
画板组件主要使用Canvas属性来进行绘图工作。Canvas(画布)是类TCanvas的对象,类TCanvas包含绘图中使用的各种方法和属性。下面我们首先介绍Canvas对象的各种属性。
画笔(Pen) Canvas对象中有一个画笔成员,它确定绘制几何图形时使用的画笔类型。刚才我们介绍了画笔的线型和线宽。
画笔的Color属性是绘图时使用的前景色。
画笔的属性Mode用来确定画笔与屏幕上原有点的混合方式。可结合当前的颜色、屏幕的颜色或它们的反转值,对线段的颜色重新定义。但不改变Color属性。
它可以取这样一些值:
pmBlack:表示用黑色绘图;
pmWhite:表示用白色绘图;
pmNot:画笔绘制的点的颜色与原有的颜色相反;
pmCopy:画笔用Color属性中定义的颜色绘图。
PmNotCopy:画笔用Color属性中定义的颜色的反转色绘图;
PmMergePenNot:画笔用Color属性中定义的颜色与屏幕颜色的反转色结合后绘图;
PmMaskNotPen:画笔用屏幕颜色与Color属性中定义的颜色结合后绘图;
PmMergeNotPen:画笔用屏幕颜色与Color属性中定义的颜色的反转值结合后绘图。
Brush属性,刷子属性在前面已经详细介绍过,它可以确定图形的填充模式和填充颜色。
属性ClipRect,用来确定绘图区域的范围。任何超出这个范围的图形不会显示。
66)问:在Canvas对象上,我们可以用怎样的方法来完成作图工作?
答:Canvas对象有一些基本做图方法:
1.首先是MoveTo方法,MoveTo将笔的当前位置设置到点(x,y)处,笔的当前位置将保存到PenPos属性中,要注意的是,改变笔的当前位置用MoveTo方法,而不要修改PenPos属性。函数形式为MoveTo(x,y)。
2.LineTo方法,用来完成从当前位置画一条直线至点(x,y),并把笔的位置移动到这一点。函数形式为LineTo(x,y)。
在画线时一般首先使用方法MoveTo移动线的起始点,然后再使用方法LineTo画直线。
3.方法Rectangle用来在画布上用当前画刷绘制矩形,其中x1,y1是矩形的左上角坐标,x2,y2是矩形的右下角坐标。因为正方形实际上就是一种特殊的矩形,所以你可以使用这个方法画出正方形。函数形式为Rectangle(x1,y1,x2,y2)。
4.方法Ellipset用来在画布上给定的矩形边界上画一个椭圆,其中x1,y1是边界矩形的左上角坐标,x2,y2是边界矩形的右下角坐标。所谓边界矩形就是圆或椭圆的外接矩形。函数形式为Ellipse(x1,y1,x2,y2)。
5.方法RoundRect用来绘制圆角矩形:其中x1,y1,x2,y2分别是圆角矩形虚拟的左上角和右下角。x3,y3是圆角的长短半径。函数形式为RoundRect(x1,y1,x2,y2)。
6.方法Polygon可以画出多边形。函数形式为Polygon(Tpoint p,int Point_Size)。
参数P是类Tpoint的一个数组,每一个Tpoint对象包含一个点的X和Y坐标。Points_Size用来指示数组的大小。这个是实心多边形,Polyline是画多边形的,二者用法基本相同。
67)问:在显示区域输出文本信息,我们是不是还象DOS下用printf函数来实现?
答:printf是一个控制台函数,不能在窗口程序中使用,一般我们TextOut输出文本,其声明:void _fastcall TextOut(int X,int Y,const AnsiString Text);其中x,y是输出字符串起始点的坐标,text是要输出的文本。当然你要用Lable也是可以的J
9.多页组件
68)问:我觉得C++Builder的组件模板的管理方式很好,在这个模板中通过单击页就可以在各个页之间进行切换,在C++Builder中我应该怎样来实现呢?
答:我们可以用多页组件来实现,它的好处就是可以在有限的空间中尽量多地存放信息,而且便于把信息分类。就拿组件模板来说吧,在那么小的屏幕中放置了上百个组件,而且并不显得凌乱,这都是多页组件的功劳。
如果窗体上要放置大量的组件,而且这些组件又是分组使用的,那么可以考虑使用多页组件。这样做可以使窗体更加简洁明了。多页组件既有多页的特性,又是一个容器组件,它就像窗体一样可以包含其他的组件。
C++Builder提供了几种功能相似的多页组件,它们是:Win32页中的TabControl,PageControl组件,Win31页中的TabSet和TabbedNoteBook组件。从它们所在的页的不同就可以知道,前两个组件是应用在32位系统中的组件,无论从性能还是风格上来看,这两个组件都更适合在Windows 95以上的板本中使用。后面两种组件是为喜爱Windows 3.1风格的用户保留的,其实我们完全可以只使用前两种组件。
69)问:TabControl组件和PageControl组件他们看起来简直是一模一样?
答:初看起来这两种组件的确差不多,我们可以通过下面的操作可以说明这一点。
(1)在窗体上放置这两个组件,通过修改Height和Width属性把它们的尺寸设置成相同的。
(2)在对象编辑器中,单击TabControl的属性Tabs中的省略号按钮,这时会弹出一个字符串编辑器。在这个编辑器中分三行输入第一页,第二页和第三页。然后点击OK按钮。
(3)在窗体中选取PageControl组件,用鼠标右键单击该组件,在弹出的快捷菜单中选择NewPage命令,PageControl组件将生成一个缺省名为“TabSheetN”的页,其中N是从1开始的整数。第一次运行New Page命令生成的页名为TabSheet1,第二次生成的名为TabSheet2,以此类推。我们使用这个命令生成三个页。
与TabControl组件不同的是,你可以直接单击页来切换页面。单击页在切换页面的同时也选中了整个PageControl组件。如果要选择某个页,可以先单击页切换到该页,然后再单击页面就可以了。
4)在对象编辑器中,选择TabSheet1,TabSheet2和TabSheet3的Caption属性,把它分别修改为第一页,第二页和第三页。
这样看起来他们就完全一样。
70)问:但要是一样,为什么有两个不同的组件呢,用一个不就行了,好象他们并不真的完全一样?
答:你说的对,千万不要被他们的外表假象所蒙蔽,实际上它们并不一样。TabControl是一种“假”多页组件,而PageControl才是“真”多页组件。
我们知道多页组件把所要显示的信息放在不同的页中,实际上每个页都是一个窗体,并可以通过页来进行页的切换。TabControl组件虽然有多个页,但是实际上只有一个页的模板,也就是说,每次用户单击页时,这个页都要根据页模板更新一次,以便对用户的选择作出响应。
很显然,如果需要在每个页上显示不同的组件,那么TabControl肯定无法胜任这种工作。因为你每次换页时,必须用代码更新页模板,这在C++Builder中实在不是一个好办法。如果每个页所显示的组件相同只是内容有变化,那么使用TabControl会更加合适,因为它消耗的系统资源比较少。
而PageControl则是每页对应一个页模板。所以,它适宜于每页都不同的情况。
71)问:TabControl组件有哪些常用的属性?
答:HotTrack属性用来确定当鼠标指向页时,页上的标签是否自动被加亮显示。缺省值为False。
MultiLine属性用来确定当页一行显示不下时是否显示到下一行。缺省值False表示在一行无法显示完时,在行的右边自动出现一个双向箭头,可以用来移动页。
TabHeight属性用来设置页的高度。缺省?表示页的高度将自动适应页上文本的高度。
TabWidth属性用来设置页的宽度。缺省值0表示页的宽度将自动适应页上文本的宽度。
TabPosition属性为tpTop时,表示将页放在TabControl组件的上面,为tpBottom时则表示放在下面。
TabControl组件上的每一个页都有自己的索引,最左边的页的索引为0,紧接着的页的索引为1,以此类推。TabIndex属性返回当前被按下的页。
Tabs属性用来设置页的个数及其标题,在设计时是使用一个字符串编辑器来实现的。
72)问:TabControl控件有哪些比较重要的事件呢?
答:TabControl有两个比较重要的事件OnChange和OnChanging。每当页被选中后就会发生OnChange事件,这时可以根据属性TabIndex判断哪个页被选中,然后再采取响应的动作。
OnChanging事件正好发生在页被选中之前,也就是正好在一个页切换到另一个页之前。这个事件使得我们可以在页切换之前采取某些行动,例如在某些条件没有设置之前不准离开这一页。
其实这两个事件以前我们已经详细的研究过,这里就不再举例说明了。
73)问:PageControl组件好像比TabControl组件功能更强大,它是怎样使用的?
答:与TabControl相比,PageControl组件的功能确实更加强大,但是却并不复杂。PageControl中的每一个页都是一个TTabSheet组件,这个组件实际上是一个容器,你可以在上面放置各种控件。每个TTabSheet组件都有自己的属性。
实际上这里有两层关系。最顶层的是PageControl组件,它负责管理多个TTabSheet组件,而每个TTabSheet组件也管理着放在它上面的组件。
74)问:PageControl组件有哪些比较重要的属性呢?
答:ActivePage这个属性显示当前被选中的页,它也可以用来切换页。
MultiLine属性用来确定当页一行显示不下时是否显示到下一行。缺省值False表示在一行无法显示完时,在行的右边自动出现一个双向箭头,可以用来移动页。缺省值为False。
TabHeight属性用来设置页的高度。缺省值0表示页的高度将自动适应页上文本的高度。
TabWidth属性用来设置页的宽度。缺省值0表示页的宽度将自动适应页上文本的宽度。
TabPosition这个属性的值为tpTop时,页将放在TabControl组件的上面,而为tpBottom时将显示在下面。
此外,还有运行时态属性:
Pages这个只读属性是PageControl组件上所有的页组成的数组。
PageCount这个属性返回PageControl组件上的页数。
75)问: PageControl的每一页都是一个TTabSheet组件,那TTabSheet有些什么属性呢?
答:TabVisible 这个属性用来屏蔽某一页的显示。也说是说,它的值为False的时候PageControl组件将不显示这个页了,但是这个页还存在,你还可以把这个属性设置为True来恢复它的显示。在程序运行期间不能删除页,只能屏蔽页的显示。
PageIndex是页的索引号,
此外,还有运行时态属性。
PageControl属性返回该页所在的PageControl组件。
TabIndex属性返回该页在所有可见页中的索引号。
因为在程序运行时是不能删除页的,所以PageIndex索引号是固定不变的。但是页可以被屏蔽,当没有页被屏蔽时,TabIndex与PageIndex完全相同;当有页被屏蔽时,TabIndex就与PageIndex就不同了。TabIndex是这一页在所有可见页中的索引号,也就是说,第一个可见页的TabIndex是0,第二个可见页的TabIndex是1,以此类推。
76)问:能设计一个例子让我更好的理解一下它们吗?
答:好的 ,首先在TabControl组件的页面上放置一个编辑框组件。
然后在PageControl组件的Tab1页中放置一个编辑框组件。
接着将PageControl组件切换到Tab2页,放置一个文本编辑框组件。
最后,编译并运行这个程序。
点击tabControl组件的tab1到tab3标签,可以看出,编辑框出现在组件的每一页中。
点击PageControl组件的tab1到tab3标签,可以看出,每一页组件都不相同。
10.菜单组件
77)问:菜单是Windows常用的用户界面。对于一个能够实际应用的程序,菜单是必不可少的组成部分。C++Builder中是怎样实现菜单的?
答:C++Builder为我们提供了两种菜单组件:一种是主菜单组件MainMenu;另一种是弹出菜单PopMenu组件。
主菜单组件的作用是在窗体上生成菜单条。菜单组件刚放到窗体上时,菜单是不可见的,当双击它打开菜单编辑器并且增加了菜单项后,菜单条才出现在窗体的顶部。设计时的菜单条与运行时的菜单条完全一样。
下面我们介绍利用菜单编辑器进行菜单设计的过程。
首先要打开菜单编辑器
从组件模板上的Standard选项卡中选择MainMenu组件,放置到窗体上。然后,用鼠标右键单击菜单组件,然后在弹出菜单中选择Menu Designer。当然,你也可以双击菜单组件打开菜单编辑器。
被加亮显示的小方框就是一个空白菜单项。
接着要添加菜单项
选中这个菜单项,这时对象编辑器中将出现这个菜单项的属性。
我们在菜单项的Caption属性中输入“文件(&File)”,其中“&F”可以生成一个快捷键,这样用户就可以通过键盘操作了,并且F下面出现了下划线(注意:由于是系统原因,”&”一定要在英文状态下输入)。当我们输入完标题并按下回车键时,C++Buildr会自动给这个菜单项赋一个名字,这里的名字是N1。如果你不满意这个名字可以修改Name属性。
添加完菜单项后会自动出现一个空白的子菜单项等待你输入。
现在菜单编辑器中的输入焦点移到了这个空白子菜单项上,同时对象编辑器将对应显示这个子菜单项的属性。你可以在Caption属性中输入子菜单项的标题“打开(&Open)”,然后按下回车键。菜单编辑器会打开一个新的子菜单项,并把输入焦点移动到这个子菜单项上。这样,就可以给菜单项增加一系列的子菜单了。
同样方法添加退出菜单。
要开始编辑下一个菜单项,可以用鼠标单击菜单项“文件”的右边的虚线框,它表示一个空白的菜单项。
78)问:当菜单项中的子菜单项属于不同类别时,有必要用分隔条把子菜单项进行分组,菜单组件是怎样实现这个功能的?
答:我们在退出和保存两个菜单项中间加入选择分隔条,在退出菜单项上点击鼠标右键,从弹出菜单中选择Insert,这时,一个空白的菜单项就插入了,将它的Caption属性设置为“–”这个减号符就可以了。
79)问:那么,我们又怎样给菜单项设置热键呢?
答:每个菜单项都有一个属性ShortCut,利用这个属性就可以给这个菜单项设置热键了。现在我们给打开文件菜单项添加热键。单击这个菜单项,选择属性ShortCut,出现一个下拉菜单,其中包括一系列的热键的组合。选择Ctrl+O,这个热键中就被赋给了退出菜单项。
80)问:快捷键与热键这两个概念我已经弄糊涂了,您能给我解释一下吗?
答:好的,快捷键与热键相同的方面是,它们都是通过键盘来访问。
一个菜单项可以同时拥有快捷键和热键。所谓快捷键通常只是一个字母,而热键通常是一个组合键。另外它们的激活方式不一样,例如一个子菜单项有一个快捷键O和一个热键Ctrl+O,那么使用热键就可以直接激活这个子菜单项,而使用快捷键你必须首先选中该子菜单项上一层的菜单项,然后按下快捷键才可以激活这个子菜单项。
81)问:您讲解的菜单到现在还只有菜单项和子菜单两级,如果我希望子菜单还有自己的子菜单,应该怎么办?
答:这种嵌套的菜单项实际上就是级联菜单。这里,我们给颜色菜单项添加子菜单项,选择颜色菜单,单击鼠标右键,在弹出的快捷菜单中选择Create Submenu命令,这时在子菜ハ钪薪鱿忠桓鲋赶蛴业募罚被共艘桓黾读说ァ?/P>
当然,你也可以用Ctrl+方向键右键。
82)问:很多菜单项的旁边有复选标记,请问菜单编辑器是否提供了这个功能呢?
答:复选标记就是在菜单项的标题的前面出现的勾号。复选标记经常用于一些状态设置的菜单项,当处于该状态时就会出现复选标记,否则复选标记消失。
现在,我们给回绕菜单项设置复选标记,在对象编辑器中把它的属性Checked改为True,这样这个菜单项就会显示复选标记。要取消复选标记可以把Checked属性再次设置为False。
83)问:我有时要设计一组互相排斥的菜单项,也就是单选菜单项,应该怎么办呢?
答:要使一组菜单项成为单选菜单项,必须满足几个条件。
首先,这一组菜单必须在同一个下拉菜单中。
其次,它们的RadioItem属性必须都设置成了True。
最后,它们的GroupIndex属性必须相同。
这一组中唯一被选中的菜单项的前面将会显示一个圆点标记。
84)问:我怎样使菜单项失效呢?
答:只要把菜单项的Enabled属性设置为False,就可以使菜单项失效,这时菜单项是灰色的。在设计时和运行时你可以任意设置菜单项的Enabled属性。
85)问:菜单项有些什么常用的事件呢?
答:菜单项只有一种事件OnClick,在实际编辑中这个事件是一定要响应的。
86)问:我们一般在主菜单之外还会提供一个快捷菜单,就像C++Builder一样,在窗体中单击右键随时都可以打开一个弹出菜单。C++Builder是怎样实现弹出菜单的?
答:弹出菜单有时候又称为快捷菜单。弹出菜单与主菜单的区别是:主菜单是固定的,而弹出菜单是活动的;主菜单有多组菜单项,而弹出菜单只有一组。弹出菜单的设计方法与主菜单基本相同,也是使用菜单编辑器来完成的。