⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cb199911nf_f.asp.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 4 页
字号:
component defines a couple of published properties that will appear in the     

Object Inspector: <i>MaxValue</i> and <i     

style='mso-bidi-font-style:normal'>MinValue</i>. Neither of these properties     

does anything special when they are set, so they both directly access the     

private member variables. There is also one public property, <i>Value</i>, that     

is defined to be read-only, because it only has a Get method and doesn't have     

an internal member variable. Whenever the component user accesses the <i>Value</i>     

property, the <i>GetValue</i> method is     

called to supply the random number. <i>GetValue</i>     

hides the messy details of calling the random number function. By allowing the     

user to select a range of values, we can translate them to the format required     

by the function. Then, after getting the random number in the format preferred     

by the function, we can internally convert it back to the range the user     

wanted. Also notice that the randomize function is called in the constructor of     

the component. This relieves the user from having to remember to seed the     

random number generator. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Subheads>Default     

Behavior for Buttons</p>     

     

<p class=BodyText> In     

C++Builder, every event handler is a pointer to a method, either written by the     

component writer or, more commonly, by the programmer using the component. You     

can think of event handlers as places where the user can "plug in" a routine. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> This is     

implemented in C++Builder by using method pointers. Each event handler     

typically has a private member variable that points to an event handler of a     

particular type: </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Code><span class=Code><b>class</b>     

PACKAGE TControl : <b>public</b> TComponent</span></p>     

     

<p class=Code><span class=Code>{</span></p>     

     

<p class=Code><span class=Code><b>&nbsp;&nbsp;private</b>:</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;TNotifyEvent *FOnClick; </span></p>     

     

<p class=Code><span class=Code><b>&nbsp;&nbsp;protected</b>:</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> virtual</b>     

<b>void</b> <b>__fastcall</b> Click();</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;<b> __property</b>     

OnClick = { read=FOnClick, write=FOnClick };</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;... </span></p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> The     

private member variable holds the pointer to the routine that the programmer,     

who is using this component, will write. The <i>Click</i> method is defined as     

follows: </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Code><span class=Code><b>void</b> <b     

style='mso-bidi-font-weight:normal'>__fastcall</b> TControl::Click()</span></p>     

     

<p class=Code><span class=Code>{</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;<b> if</b>     

(Assigned(FOnClick)) </span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;{ </span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;&nbsp;&nbsp;FOnClick(<b>this</b>);</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;} </span></p>     

     

<p class=Code><span class=Code>}</span></p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> The <i     

style='mso-bidi-font-style:normal'>Assigned</i> function returns <b>true</b> if     

the argument points to a routine, and <b>false</b> if the argument is null.     

This routine checks to see if the user has attached any code to the event     

handler, and executes it if the code has been defined. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> Events     

are implemented this way in C++Builder to make them as flexible as possible. A     

rule of thumb for component writing in C++Builder is to make the components as     

robust as possible. In other words, you don't want to write a component that     

requires the user to attach some code to it for it to work. Components should     

ignore event handlers that are empty, and the previous mechanism allows this. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Subheads><i>CustomButton</i> </p>     

     

<p class=BodyText> We're     

going to create a button that has some default behavior attached to it. When     

the user clicks the button, the default code will execute. The component class     

is shown in <a href="#ListingTwo">Listing     

Two</a>. This class overrides the protected <i>Click</i> routine from     

the parent (<i>TButton</i>), and provides a     

component writer-defined routine. When this button is dropped on a form and the     

user clicks on the button at run time, the user gets the <i>ShowMessage</i> dialog box (see Figure 2). </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Captions><img width=231 height=103     

 src="images/cb199911nf_f_image003.gif" tppabs="http://www.cbuilderzine.com/features/1999/11/cb199911nf_f/cb199911nf_f_image003.gif" align=left> <br clear=all>     

<b>Figure 2:</b> Custom-defined button. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

     

     

<p class=BodyText> This     

demonstrates how to attach some default behavior to a control by manipulating     

the protected method that actually dispatches the component user's code.     

However, look what happens if users add their own code to the <i>OnClick</i>     

event of the button: </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Code><span class=Code><b>void</b> <b     

style='mso-bidi-font-weight:normal'>__fastcall</b>     

TForm1::CustomButton1Click(TObject *Sender) </span></p>     

     

<p class=Code><span class=Code>{</span></p>     

     

<p class=Code><span class=Code>&nbsp;&nbsp;ShowMessage(&quot;From the component user's     

code&quot;); </span></p>     

     

<p class=Code><span class=Code>}</span></p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> When the     

application is run, the user gets both message dialog boxes. So, the component     

writer's code and the component user's code are being executed. In fact, the     

location in the component's <i>Click</i>     

method determines which code is executed first. Notice that the inherited     

class's <i>Click</i> method appears first in     

the <i>Click</i> routine for the custom     

button, but the user's code is executed first. It's more common to call the     

inherited routine first, then supply your own code. However, it doesn't matter     

in which order you perform these actions, as long as you call the inherited     

routine to accommodate any code that the component user has added. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Subheads>An Example     

Unfolds </p>     

     

<p class=BodyText> A common     

Windows interface technique is to create dialog boxes that "unfold." This way,     

simple options can be placed on the main part of the form and more complex or     

seldom-used operations can be placed "out of the way" on the folded portion of     

the dialog box. Of course, it would be easy enough to create this type of form     

in C++Builder, placing a button on the form to automatically resize the form     

and disable itself when pressed. With a component, however, you can create it     

once and never have to worry about it again. We're going to create such a     

control next. This illustrates how to create a button that already has some     

default behavior associated with it. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> The <i     

style='mso-bidi-font-style:normal'>UnfoldButton</i> control is a button that     

automatically implements this unfolding behavior. When you drop the button on     

the form, it notes the current size of the form. When the application runs, the     

form resizes itself (before it is shown), to extend to the bottom of the unfold     

button plus a little extra distance. When the button is clicked, the dialog box     

unfolds to show the rest of the form. The "folded" version of the dialog box is     

shown in Figure 3, and the "unfolded" version is shown in Figure 4. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Captions><img width=333 height=156     

 src="images/cb199911nf_f_image005.gif" tppabs="http://www.cbuilderzine.com/features/1999/11/cb199911nf_f/cb199911nf_f_image005.gif" align=left> <br clear=all>     

<b>Figure 3: </b>The "folded" version of the dialog     

box. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

     

     

<p class=Captions><img width=333 height=248     

 src="images/cb199911nf_f_image007.gif" tppabs="http://www.cbuilderzine.com/features/1999/11/cb199911nf_f/cb199911nf_f_image007.gif" align=left> <br clear=all>     

<b>Figure 4:</b> The "unfolded" version of the     

dialog box. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

     

     

<p class=BodyText> The     

button automatically unfolds the dialog box and disables itself (because you     

obviously won't be expanding the same form twice). However, this control     

doesn't affect your application in the Designer. Essentially, everything under     

the button will be part of the "folding" portion, and everything above the     

button will always appear. The source for <i>UnfoldButton</i> is shown in <a     

href="#ListingThree">Listing     

Three</a>.</p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> We're     

creating some private fields to hold the additional space (i.e. the space     

between the bottom of the button and the bottom of the form) and the initial     

form height. We're also overriding the constructor and the <i>Click</i> method. Notice that we are also overriding the <i     

style='mso-bidi-font-style:normal'>Loaded</i> method. The <i>Loaded</i> method is called for each component after the form has     

loaded its values from the DFM file. We then set the default values. Remember     

that the <b>default</b> directive only signifies whether the value is streamed     

to the DFM file. You must still provide the default values in the constructor.     

We're also blindly setting the form's <i>AutoScroll</i>     

property to <b>false</b>. This is generally not a good idea from an     

encapsulation standpoint to blindly assign a value like this, but in this case,     

the button can't possibly work if this property is set to <b>true</b>. The <i     

style='mso-bidi-font-style:normal'>Click</i> method calls the <i>inherited</i>     

method and resets the form to the original size captured in the <i     

style='mso-bidi-font-style:normal'>Loaded</i> method. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> The most     

complex method for this component is the <i>Loaded</i>     

method. There are several points to note here. The first order of business is     

to call the inherited <i>Loaded</i> method.     

You should always do this any time you override a method, not just for the     

constructor and destructor. Next, we'll save the initial height of the form in     

a property, so we can restore it in the <i>Click</i> routine. Next, we'll     

obtain the <i>ClientOrigin</i> value for the     

button. <i>ClientOrigin</i> is a wrapper for     

a Windows API call that tells you the absolute position on the screen of the     

component. We'll then set the form's height (after casting <i>Owner</i>, which     

is of type <i>TComponent</i>) to the base     

class (which includes the <i>height</i> property), to the position of the     

button on the screen minus the position of the form on the screen. This     

calculation will tell how much to resize the form to be just a little taller     

than the button position. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> Why go     

to the trouble of getting screen coordinates from Windows? Why not just look at     

the height property of the button? That will work as long as there are no     

containers on the form (e.g. panels, groupboxes, etc.) underneath the <i     

style='mso-bidi-font-style:normal'>UnfoldButton</i> control. A component's position on the form is relative to the     

container, not the form itself. So, if you have a panel on the form, and a     

button on the panel, the <i>height</i> property of the button is relative to     

the panel, not the form. It's possible to loop through all the containers on     

the form, adding the heights as you go (in fact, a previous version of this     

control did just that), but it's much cleaner and more efficient to let Windows     

tell you the relative position. This is a common theme in Windows: Don't build     

it if it already exists. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> Why not     

do this in the constructor, rather than overriding the <i>Loaded </i>method?     

Notice that the form creates all the controls that it owns during its     

constructor. The problem lies in the fact that we must refer to, and change,     

one of the form's properties as the button is created. However, the form's     

properties aren't yet available in its constructor. In other words, you can't     

refer to the form's height, and change it, in a constructor for one of the     

controls that the form owns. The form doesn't exist yet! Thus, the <i     

style='mso-bidi-font-style:normal'>Loaded</i> method is perfect in this     

situation. In fact, the rule of thumb is that any time you must refer to the     

form or one of its controls, you must do so in <i>Loaded</i>, rather than the constructor. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=Subheads>Conclusion</p>     

     

<p class=BodyText> This     

article showed two more of the architectural underpinnings of components in     

C++Builder: properties, and how the <i>Click</i>     

methods of buttons work. The event model will be examined more closely in a     

future article, including how to create custom events and event handlers. In     

the next installment, we'll look at component containership and creating dialog     

box components. </p>     

     

<p class=BodyText> &nbsp; </p>     

     

<p class=BodyText> <i>The     

files referenced in this article are available for <a href="download/cb199911nf_f.zip" tppabs="http://www.cbuilderzine.com/features/1999/11/cb199911nf_f/cb199911nf_d.asp">download</a>. </i></p>    

    

<p class=BodyText> &nbsp; </p>    

    

<p class=Biotext>Neal is the     

Vice President of Technology at The DSW Group. He is also the designer and developer     

of applications, instructional materials, magazine articles, video     

presentations, and author of the book<i> JBuilder 3 Unleashed</i>. He can be     

reached at <a href="mailto:nford@thedswgroup.com">mailto:nford@thedswgroup.com</a>,     

or by calling The DSW Group at (800) 356-9644. </p>     

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -