📄 ch20.htm
字号:
the Object Inspector already contain values. These are the default values as definedby the component writer. Assigning default values makes life easier for the componentuser. All properties should have a default value, if possible. This enables the userto change only specific properties and leave the rest alone. Certain types of properties(such as string properties) do not lend themselves to default values, but most do.</P><BLOCKQUOTE> <P><HR><strong>NOTE:</strong> String properties cannot have a default value.<BR> Like the read and write methods, the default value is set when the property is declared. Let's go back to the component's FlashRate property. Declaring the FlashRate property with a default value would look like this:</BLOCKQUOTE><PRE></PRE><BLOCKQUOTE> <PRE>property FlashRate : Integer read FFlashRate write SetFlashRate default 800;</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE> <P>Now, when the FlashingLabel component is displayed in the Object Inspector, the value of 800 (milliseconds in this case) will already be displayed for the FlashRate property.<HR></P> <P><HR><strong>NOTE:</strong> Setting a default value for the property displays only the default value in the Object Inspector. It does <I>not</I> set the value of the underlying data field for the property. You must still assign the default value to the data field in the component's constructor. For example, the constructor for the FlashingLabel component would look like this: <PRE>constructor TFlashingLabel.Create(AOwner : TComponent);begin inherited; FFlashRate := 800; { Other things here. } end;</PRE></BLOCKQUOTE><PRE></PRE><BLOCKQUOTE> <P>Be sure to set the appropriate values for all class data fields that correspond to properties with default values.</BLOCKQUOTE><PRE></PRE><BLOCKQUOTE> <P>If you don't want to use a default value for a property, omit the default specifier in the property's declaration.<HR></P></BLOCKQUOTE><P><H4>Properties Can Be Published, Public, or Private</H4><P>Some properties are available at design time. These properties can be modifiedat design time through the Object Inspector and can also be modified or read at runtime.These properties are said to be <I>published</I>. Simply put, a published propertyis one that shows up in the Object Inspector at design time. Any properties locatedin the published section of the component's class declaration will be displayed inthe Object Inspector at design time.</P><P>Other properties, called <I>public properties</I>, are runtime-only. These propertiescan't be accessed at design time (they don't show up in the Object Inspector). Propertiesof this type are declared in the public section of the component's class declaration.</P><P><I>Private</I> <I>properties</I> are properties that are used internally by thecomponent and are not available to the component users. Private properties are declaredin the protected or private sections in the component's class declaration.</P><P><H3><A NAME="Heading8"></A>Writing Methods for Components</H3><P>Writing methods for components is no different than writing methods for any ObjectPascal class. Your component's methods can be private, protected, or public. It paysto keep in mind access levels as you write components.</P><P>Determining what methods to make public is easy. A <I>public</I> <I>method</I>is one that users of your component can call to cause the component to perform aspecific action. The use of private versus protected methods is more difficult todecide. After programming for a while, you will be better able to recognize the situationswhen protected access should be used instead of private access.</P><P>Generally speaking, though, use private methods for tasks that are internal tothe component and should not be accessible to derived classes. Use protected methodsfor tasks that are internal to the component, but are tasks that derived classesmight want to alter to provide additional functionality to the component.</P><BLOCKQUOTE> <P><HR><strong>NOTE:</strong> Read and write methods for properties are usually made protected. Thus, you enable classes derived from your component to modify the behavior of the read and write methods by overriding the method.<HR></BLOCKQUOTE><P>As I said earlier, methods of components are just methods and, for the most part,can be treated as regular class member functions.<PRE></PRE><H2><A NAME="Heading9"></A>Adding Functionality to TFlashingLabel</H2><P>A little later today I'll talk about events and how to write them, but for nowyou have enough information to write your first component. The FlashingLabel componenthas the following features:</P><P><UL> <LI>A property called FlashRate that controls the blink rate <P> <LI>A property called FlashEnabled that turns the flashing on or off <P> <LI>Write methods for the FlashRate and FlashEnabled properties <P> <LI>Default values for the FlashRate and FlashEnabled properties <P> <LI>A private class member (a TTimer class instance) to control the timing of the flash <P> <LI>All the characteristics of a regular Label component</UL><P>First, I'll show you the complete FlashingLabel unit. After that, I'll go overwhat is happening with the code. Listing 20.2 shows the FlashingLabel unit.</P><P><PRE><B>Listing 20.2.</B> FLASHINGLABEL.PAS.unit FlashingLabel;interfaceuses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;type TFlashingLabel = class(TCustomLabel) private { Private declarations } FFlashEnabled : Boolean; FFlashRate : Integer; Timer : TTimer; protected { Protected declarations } { Protected write methods for the properties. } procedure SetFlashEnabled(AFlashEnabled : Boolean); procedure SetFlashRate(AFlashRate : Integer); { OnTimer event handler. } procedure OnTimer(Sender : TObject); virtual; public { Public declarations } constructor Create(AOwner : TComponent); override; published { Published declarations } { The component's properties. } property FlashEnabled : Boolean read FFlashEnabled write SetFlashEnabled default True; property FlashRate : Integer read FFlashRate write SetFlashRate default 800; { All the properties of TCustomLabel redeclared. } property Align; property Alignment; property AutoSize; property BiDiMode; property Caption; property Color; property Constraints; property DragCursor; property DragKind; property DragMode; property Enabled; property FocusControl; property Font; property ParentBiDiMode; property ParentColor; property ParentFont; property ParentShowHint; property PopupMenu; property ShowAccelChar; property ShowHint; property Transparent; property Layout; property Visible; property WordWrap; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDock; property OnEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnStartDock; property OnStartDrag; end;procedure Register;implementationconstructor TFlashingLabel.Create(AOwner : TComponent);begin inherited; { Set the data fields to their default values. } FFlashEnabled := True; FFlashRate := 800; { Initialize the timer object. } Timer := TTimer.Create(Self); { Set the timer interval using the flash rate. } Timer.Interval := FFlashRate; { Assign our own OnTimer event handler to the { TTimer OnTimer event. } Timer.OnTimer := OnTimer;end;procedure TFlashingLabel.SetFlashEnabled(AFlashEnabled : Boolean);begin { Set FFlashEnabled data field. } FFlashEnabled := AFlashEnabled; { Don't start the timer if the component is on a form { in design mode. Instead, just return. } if csDesigning in ComponentState then Exit; { Start the timer. } Timer.Enabled := FFlashEnabled; { If flashing was turned off, be sure that the label { is visible. } if not FFlashEnabled then Visible := True;end;procedure TFlashingLabel.SetFlashRate(AFlashRate : Integer);begin { Set the FFlashRate data field and the timer interval. } FFlashRate := AFlashRate; Timer.Interval := AFlashRate;end;procedure TFlashingLabel.OnTimer(Sender : TObject);begin { If the component is on a form in design mode, { stop the timer and return. } if csDesigning in ComponentState then begin Timer.Enabled := False; Exit; end; { Toggle the Visible property each time the timer { event occurs. } Visible := not Visible;end;procedure Register;begin RegisterComponents(`Samples', [TFlashingLabel]);end;</PRE><PRE>END.</PRE><H3><A NAME="Heading10"></A>The Class Declaration</H3><P>First, let's take a look at the class declaration. Notice that the private sectiondeclares three class data fields. The first two, FFlashEnabled and FFlashRate, arethe data fields associated with the FlashEnabled and FlashRate properties. The thirddeclaration looks like this:</P><P><PRE>Timer : TTimer;</PRE><P>This declares a pointer to a TTimer object. The TTimer object is used to regulatethe flash rate.</P><BLOCKQUOTE> <P><HR><strong>NOTE:</strong> I haven't discussed timers yet. A <I>timer</I> is set up to fire at a specified interval (in milliseconds). When a timer fires, a WM_TIMER message is sent to the window that owns the timer and, as a result, the OnTimer event is triggered. For example, if you set the timer interval to 1,000 milliseconds, your OnTimer event will be called nearly every second. I should point out that the WM_TIMER message is a low-priority message and can be preempted if the system is busy. For this reason, you can't use a regular timer for mission-critical operations. Still, for non-critical timings, the TTimer does a good job. (For more information on timers, refer to the Delphi help under TTimer or the WM_TIMER message.)<HR></BLOCKQUOTE><P>The protected section of the class declaration contains declarations for the writemethods for the FlashRate and FlashEnabled properties. The protected section alsodeclares the OnTimer function. This function is called each time a timer event occurs.It is protected and declared as virtual, so derived classes can override it to provideother special handling when a timer event occurs. For example, a derived class mightwant to change the color of the text each time the label flashes. Making this methodvirtual enables overriding of the function to add additional behavior.</P><P><H3><A NAME="Heading11"></A>The Published Section</H3><P>Finally, notice the published section. This section contains declarations forthe FlashEnabled and FlashRate properties. In the case of the FlashEnabled property,the read specifier uses direct access, the write specifier is set to the SetFlashEnabledmethod, and the default value is set to True. The FlashRate property uses a similarconstruct.<PRE></PRE><P>Notice that following the two property declarations, I have declared all the propertiesof TCustomLabel that I want republished. If you don't perform this step, the usualproperties of a label component won't be available to the class at runtime nor laterat design time (after you install the component to the Component palette).</P><P><H3><A NAME="Heading12"></A>The Implementation Section</H3><P>Now turn your attention to the implementation section. First you see the TFlashingLabelCreate constructor. Here you see that the default values for the class data fieldsrepresenting the FlashEnabled and FlashRate properties are assigned. Remember thatyou declared default values for these properties, but that affects only the way theproperty is displayed in the Object Inspector. So you must assign the actual values
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -