📄 ch22.htm
字号:
class TSmallEdit : public TEdit
{
private:
protected:
public:
virtual __fastcall TSmallEdit(TComponent* Owner);
__published:
};
</FONT></PRE>
<P><FONT COLOR="#0066FF"><TT>#endif</TT></FONT>
<H4><FONT COLOR="#000077">Listing 22.5. The implementation of TSmallEdit as produced
by the standard boilerplate output of the Component Expert.</FONT></H4>
<PRE><FONT
COLOR="#0066FF">
//--------------------------------------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop
#include "Unleash1.h"
//--------------------------------------------------------------------------
static inline TSmallEdit *ValidCtrCheck()
{
return new TSmallEdit(NULL);
}
//--------------------------------------------------------------------------
__fastcall TSmallEdit::TSmallEdit(TComponent* Owner)
: TEdit(Owner)
{
}
//--------------------------------------------------------------------------
namespace Unleash1
{
void __fastcall Register()
{
TComponentClass classes[1] = {__classid(TSmallEdit)};
RegisterComponents("Unleash", classes, 0);
}
}
</FONT></PRE>
<P><FONT COLOR="#0066FF"><TT>//--------------------------------------------------------------------------</TT>
</FONT><BR>
<BR>
The Component Expert starts by giving you <TT>#include</TT> directives designed to
cover most of the
bases you are likely to touch in a standard component:</P>
<PRE><FONT COLOR="#0066FF">
#include <vcl\sysutils.hpp>
#include <vcl\controls.hpp>
#include <vcl\classes.hpp>
#include <vcl\forms.hpp>
#include
<vcl\StdCtrls.hpp>
</FONT></PRE>
<P>The next step is to give you a basic class declaration, in which the name and
parent are filled in with the choices you specified in the Component Expert dialog.
All this business about the scoping directives
is just for your convenience, and
you can delete any portion of it that you don't think you'll need.</P>
<PRE><FONT COLOR="#0066FF">class TSmallEdit : public TEdit
{
private:
// Private declarations
protected:
// Protected declarations
public:
// Public declarations
virtual __fastcall TSmallEdit(TComponent* Owner);
__published:
// Published declarations
};
</FONT></PRE>
<P>Before you can place a component on the Component Palette, you first must register
it with the
system:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall Register()
{
TComponentClass classes[1] = {__classid(TSmallEdit)};
RegisterComponents("Unleash", classes, 0);
}
</FONT></PRE>
<P>Registering a class makes it known to the BCB
Component Palette when the unit
is compiled into the BCB component library. The <TT>Register</TT> procedure has no
impact on programs compiled with this unit. Unless your program calls the <TT>Register</TT>
procedure (which it should never do), the
code for the <TT>Register</TT> procedure
will never execute.</P>
<P>This method first creates an array consisting of the types of components you want
to register. Recall that <TT>__classid</TT> is one of the new pieces of syntax added
to the language.
It is dis- cussed in Chapter 2, "Basic Facts About C++Builder."
After creating the array, you call <TT>RegisterComponents</TT>, which takes the name
of the page you want to use in the Component Palette in the first parameter, the
array of
metaclasses in the second parameter, and the size of the array in the third
parameter. If the page does not exist, it will be created automatically. Here is
the declaration for <TT>RegisterComponents</TT>:</P>
<PRE><FONT COLOR="#0066FF">extern void
__fastcall RegisterComponents(const System::AnsiString Page,
System::TMetaClass* const * ComponentClasses, const int ComponentClasses_Size);
</FONT></PRE>
<P>Recall that <TT>TMetaClass</TT> is declared in <TT>SysDefs.h</TT> and that it
represents
a subset of <TT>TObject</TT>. You can get the metaclass of a VCL object
by calling the object's <TT>ClassType</TT> method.</P>
<P>If you look back at the source for the whole project, you will see that the <TT>Register</TT>
method is placed in its own
namespace. This happens because multiple declarations
for methods with that name will occur throughout your projects and, indeed, inside
BCB itself. As a result, the compiler's need for order must be satiated with a few
discreetly placed namespaces.
This whole subject will come up again in just a moment,
when I talk about recompiling <TT>CmpLib32.ccl</TT>.</P>
<P>After using the Component Expert, you should save the project. Proceed as you
normally would by creating a directory for the project
and saving <TT>Main.pas</TT>
and <TT>TestUnleash1.cpp</TT> inside it. You should not save the new unit that you
created into the same directory, however, but you should place it in the <TT>Utils</TT>
directory where you store files such as
<TT>CodeBox</TT>. This code is now going
to come into play as part of your system, and as such you want a single path that
leads to all related files of this type. If you have all your components in different
subdirectories, you will end up with a
source path that is long and unwieldy.
<DL>
<DD>
<HR>
<FONT COLOR="#000077"><B>NOTE: </B></FONT><TT>ValidCtrCheck()</TT> verifies that
creating an instance of the object at compile time is possible. If you forget to
override a pure virtual
function, the compilation of the component will fail because
the statement <TT>"new foo(0)"</TT> is now invalid. You can test this by
creating a component derived from <TT>TCustomGrid</TT> (an abstract class) and installing
it without
doing any work to change the object.<BR>
<BR>
Because <TT>ValidCtrCheck</TT> is inline and never called, no code will be generated--so
there is no runtime cost.
<HR>
</DL>
<P><BR>
The goal of this project is to give a component of type
<TT>TEdit</TT> a new set
of default behaviors so that it starts out with certain colors and certain fonts.
To do so, you need to override the <TT>Create</TT> method and change the fonts inside
it. The method declaration itself appears
automatically:</P>
<PRE><FONT COLOR="#0066FF">class TSmallEdit : public TEdit
{
public:
virtual __fastcall TSmallEdit(TComponent* Owner);
};
</FONT></PRE>
<P>Notice that in the preceding declaration I have removed the <TT>private</TT>,
__<TT>published</TT>, and <TT>protected</TT> directives created by the Component
Expert. Making this change is neither here nor there; I do it just to keep the amount
of code you need to look at as small as possible.</P>
<P>The <TT>Create</TT> method
for <TT>TSmallEdit</TT> is declared as <TT>public</TT>.
If you think about the process of creating a component dynamically, you will see
that the <TT>Create</TT> method has to be <TT>public</TT>. This is one method that
must be exposed. Like all VCL
constructors, <TT>TSmallEdit</TT> is declared <TT>virtual</TT>
and <TT>__fastcall</TT>.</P>
<P><TT>Create</TT> is passed a single parameter of type <TT>TComponent</TT>, which
is a base class that encapsulates the minimum functionality needed to be an
owner
of another component and to place a component on the Component Palette. In particular,
whatever form you place a component on usually will be the owner of that component.</P>
<P>The implementation of the <TT>Create</TT> method is simple:</P>
<PRE><FONT COLOR="#0066FF">__fastcall TSmallEdit::TSmallEdit(TComponent* Owner)
: TEdit(Owner)
{
Color = clBlue;
Font->Color = clYellow;
Font->Name = "Times New Roman";
Font->Size = 12;
Font->Style =
TFontStyles() << fsBold << fsItalic;
}
</FONT></PRE>
<P>The code first calls <TT>Create</TT>, passing in the variable <TT>AOwner</TT>.
As I stated previously, the owner of a component will often, though not always, be
the form on which
the component is to be displayed. In other words, the user will
drop the component onto a form, and that form will become the owner of the component.
In such a case, <TT>AOwner</TT> is a variable that points to the form. The VCL uses
it to initialize
the <TT>Owner</TT> property, which is one of the fields of all components.</P>
<P>The next step is to define the color and font that you want to use, with <TT>Font->Style</TT>
defined as follows:</P>
<PRE><FONT COLOR="#0066FF">enum TFontStyle {
fsBold, fsItalic, fsUnderline, fsStrikeOut };
typedef Set<TFontStyle, fsBold, fsStrikeOut> TFontStyles;
</FONT></PRE>
<DL>
<DD>
<HR>
<FONT COLOR="#000077"><B>NOTE: </B></FONT>Don't let all this gobbledygook confuse
you! All that occurs in
this declaration is that a simple <TT>enum</TT> type is declared,
and then you see a <TT>Set</TT> declaration for the type that ranges from a low value
of <TT>fsBold</TT> to a high value of <TT>fsStrikeOut</TT>. Piece of cake!
<HR>
</DL>
<P><BR>
If you want to add the underline and bold style to the text in the edit control,
write the following:</P>
<PRE><FONT COLOR="#0066FF">Font->Style = TFontStyles << fsBold << fsUnderline;
</FONT></PRE>
<P>If you then want to add the
italic style at runtime, write this:</P>
<PRE><FONT COLOR="#0066FF">Font->Style = Font->Style << fsItalic;
</FONT></PRE>
<P>To remove the style, write this line:</P>
<PRE><FONT COLOR="#0066FF">Font->Style = Font->Style >>
fsItalic;
</FONT></PRE>
<P>At this stage, the code is ready to go on the Component Palette. However, most
of the time when you write components, you should test them first to see whether
they work.</P>
<P>Even on a fast machine, the process of
recompiling <TT>CmpLib32.ccl</TT> and adding
your component to the Component Palette takes between 10 seconds and 2 minutes. Perhaps
I'm a bit impatient, but that's a little too long a wait for me if all I need to
tweak is a few aspects of my code. As
a result, I test things first in a small program
and then add the component to the IDE.</P>
<P>To test the new class, drop a button on the program's main form, and create an
<TT>OnClick</TT> handler:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TSmallEdit *MyEdit = new TSmallEdit(this);
MyEdit->Parent = this;
MyEdit->Show();
}
</FONT></PRE>
<P>Don't forget to use View | Project Manager and add the unit to the project. This
ensures
that the <TT>USEUNIT</TT> macro is placed in your project source.</P>
<P>This code creates the component and shows it on the main form. <TT>this</TT>,
of course, is the way that <TT>TForm1</TT> refers to itself from inside one of its
own methods. The
owner of the new component is <TT>Form1</TT>, which will be responsible
for disposing of the component when finished with it. This process happens automatically.
You never need to worry about disposing of a visible component shown on a form.</P>
<P>The parent of the component is also <TT>Form1</TT>. The <TT>Parent</TT> variable
is used by Windows when it is trying to decide how to display the form on the screen.
If you place a panel on a form and drop a button on the panel, the owner of that
button is the form, but the parent is the panel. Ownership determines when and how
the component is deallocated, and parental relationships determine where and how
the component is displayed. Ownership is fundamentally a BCB issue, whereas parental
relationships are primarily concerns of Windows.
<DL>
<DD>
<HR>
<FONT COLOR="#000077"><B>NOTE: </B></FONT>The next paragraph in this section explains
how to recompile <TT>CmpLib32.ccl</TT>. Remember that you should always keep a backup
copy of
<TT>CmpLib32.ccl</TT> on your hard drive. <TT>CmpLib32.ccl</TT> is stored
in the <TT>..\CBUILDER\BIN</TT> directory. Beneath this directory, I have created
another directory called <TT>BACK</TT> where I keep a backup copy of <TT>CmpLib32.ccl</TT>.
If worse comes to worst, you can always copy the version of <TT>CmpLib32.ccl</TT>
on your installation CD back into the <TT>BIN</TT> subdirectory.<BR>
<BR>
My personal experience is that everyone, sooner or later, makes a tasty Mulligan's
Stew out
of a copy of <TT>CmpLib32.ccl</TT>. The worst-case scenario is that you're
on the road when this mess happens, and that you don't have access to your installation
disk. Don't let this situation happen to you. Keep a backup copy of
<TT>CmpLib32.ccl</TT>
on your hard drive at all times!<BR>
?FIGURE 17.16. Creating SQL selection criteria in ReportSmith. CCL stands for C++Builder
Component Library.
<HR>
</DL>
<P><BR>
After you run the program and test the component, the next
step is to put it up on
the Component Palette. To do so, choose Components | Install and then click the Add
button. Browse through the directories until you find <TT>Unleash1.Cpp</TT>, select
OK, and then close the Install Components dialog by
clicking OK. At this point, a
project called <TT>CmpLib32.cpp</TT> is compiled. This project creates a huge DLL
called <TT>CmpLib32.ccl</TT>, which contains all the components in the Component
Palette, all the component and property editors associated
with those components,
the form designer part of the IDE, the experts, and other support modules.</P>
<P>After <TT>CmpLib32.ccl</TT> finishes recompiling, you can start a new project,
turn to the newly created Unleash page, and drop your new component
onto a form.
It will have a blue background, default to Times New Roman, and have its font style
set to bold and its font color to yellow. Notice that all the properties of <TT>TEdit</TT>
have been inherited by <TT>TSmallEdit</TT>. That's OOP in
action. You can see an
example of the form that uses this control in Figure 22.3.</P>
<P><A HREF="22ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/22/22ebu03.jpg">Figure 22.3.</A><I>Using the <TT>TSmallEdit</TT>
control on a standard VCL form.</I>
<H2><FONT COLOR="#000077">Understanding the
Project Source for CmpLib32</FONT></H2>
<P>You can save the <TT>CmpLib32.cpp</TT> file used during compilation of <TT>CmpLib32.ccl</TT>
to disk if you choose Options | Environment | Library | Save Library Source Code.
After choosing this option and
recompiling <TT>CmpLib32.ccl</TT>, you can go to the
<TT>\CBUILDER\BIN</TT> directory and view your copy of <TT>CmpLib32.cpp</TT>.</P>
<P>Here is an example of the source code produced by the IDE for use when compiling
<TT>CmpLib32.ccl</TT>:</P>
<PRE><FONT COLOR="#0066FF">
//--------------------------------------------------------------------------
// Component Palette
// Copyright (c) 1996, 1996 by Borland International, All Rights Reserved
//
// Module generated by C++Builder to
rebuild the Component
// Palette Library (CmpLib32.ccl).
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// In order to be totally Delphi
compatible, the Component palette must be
// built with a namespaced version of the VCL library. Using namespace allows
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -