Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

(Ebook - Pdf) Kick Ass Delphi Programming

.pdf
Скачиваний:
284
Добавлен:
17.08.2013
Размер:
5.02 Mб
Скачать

then begin

MessageDlg('You can only adjust one element at a time.' + #13#10 +

'Please unselect the current element before continuing.', mtWarning, [mbOK], 0);

Exit;

end;

Adjust.Checked := True;

ComponentBeingAdjusted := PopupMenu1.PopupComponent; With TWinControl(PopupMenu1.PopupComponent) do

begin

SizingRect1.Top := Top; SizingRect1.Left := Left; SizingRect1.Width := Width; SizingRect1.Height := Height;

end;

SizingRect1.Visible := True; MiniInspector1.ShowThisComponent(ComponentBeingAdjusted);

end;

end;

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

Once various checks are performed to be sure the operation is legal, the TSizingRect is moved to the same position as the control being adjusted. (The variable ComponentBeingAdjusted exists for the benefit of additional routines that may not be able to rely on PopupMenu1.PopupComponent.) It’s done like this:

ComponentBeingAdjusted := PopupMenu1.PopupComponent; With TWinControl(PopupMenu1.PopupComponent) do

begin

SizingRect1.Top := Top; SizingRect1.Left := Left; SizingRect1.Width := Width; SizingRect1.Height := Height;

end;

SizingRect1.Visible := True; MiniInspector1.ShowThisComponent(ComponentBeingAdjusted);

At this point, the SizingRect component is active and can be moved and re-sized by the mouse, as shown in Figure 10.5.

FIGURE 10.5 The SizingRect’s rectangle.

When the user has finished adjusting the control, they can again right click the control to save or discard their changes, as shown in Figure 10.6.

FIGURE 10.6 Saving or discarding changes.

If the user does wish to move ahead with the adjustment, and thus selects the second option, the control being adjusted is moved and re-sized to the new specifications, after which the SizingRect is again hidden. (This code is also part of TFrmMain.AdjustClick).

With TWinControl(ComponentBeingAdjusted) do begin

Top := SizingRect1.Top;

Left := SizingRect1.Left; Width := SizingRect1.Width; Height := SizingRect1.Height;

end;

SizingRect1.Cursor := crDefault; SizingRect1.Visible := False; SizingRect1.Top := -40; SizingRect1.Left := -40;

{…}

Abandoning Changes

If the user doesn’t want to keep any changes, and thus chooses the first popup menu option, the TSizingRect is hidden and the selected control remains unchanged. This is done in TFrmMain.Escape1Click, as shown in Listing 10.5.

Listing 10.5 The Escape/No Changes OnClick Handler

procedure TFrmMain.Escape1Click(Sender: TObject); begin

{if we were just now resizing a component, then...} if (Adjust.Checked = True) then

begin

Adjust.Checked := False; SizingRect1.Cursor := crDefault; SizingRect1.Visible := False; SizingRect1.Top := -40; SizingRect1.Left := -40; SizingRect1.Width := 40; SizingRect1.Height := 40;

ComponentBeingAdjusted := Self; {i.e., no control is }

end;

{ currently selected. }

end;

 

NOTE:

In the Starter.Dpr project, the SizingRect component is hidden off to the top right of the main form to prevent it from being accidentally shown at the wrong time. When using the project as a starting point for your own apps, be sure to locate the SizingRect component and, from Delphi’s main menu, select Edit|Bring To Front after you have added all of your own controls. Also, be sure that all of the controls’ PopupMenu properties point to PopupMenu1.

Changing the Tab Order at Runtime

If users are going to move controls around, chances are they’ll want to be able to adjust the tab order as well. After all, our do-it-yourself design would completely fail the ease-of-use test if users were locked into the original tab order. Navigating from one control to another would be a very confusing task.

In Delphi, you set the tab order of the controls via the Tab Order dialog box, which consists primarily of a ListBox component, an up-arrow button, and a down-arrow button. Since this seems to work well for Delphi, we’ll use the same kind of dialog box in our system. Figure 10.7 shows our FrmTabOrder component at runtime.

FIGURE 10.7 The FrmTabOrder component at runtime.

FrmTabOrder itself, however, is just a friendly interface. What actually manages the tab order is the code that shows FrmTabOrder. This is the TFrmMain.TabOrder1Click method, shown in Listing 10.6. I’ll discuss this handler in detail next.

Listing 10.6 The tab order menu item’s OnClick Handler

procedure TFrmMain.TabOrder1Click(Sender: TObject); var

i : Integer; begin

FrmTabOrder.LBControls.Items.Clear; for i := 0 to ComponentCount -1 do begin

if ((Components[i] is TWinControl) and

not (Components[i] is TSizingRect)) then FrmTabOrder.LBControls.Items.Add(Components[i].Name);

end;

FrmTabOrder.ShowModal;

if FrmTabOrder.ModalResult = mrOK then begin

for i := 0 to FrmTabOrder.LbControls.Items.Count -1 do TWinControl(FindComponent(

FrmTabOrder.LbControls.Items[i])).TabOrder := i;

end;

end;

Now, in detail. First, the routine clears the list box. Then it iterates through all of the form’s controls, adding every control that’s a TWinControl to the list box, except for the SizingRect.

FrmTabOrder.LBControls.Items.Clear; for I := 0 to ComponentCount -1 do

begin

if ((Components[I] is TWinControl) and not (Components[I] is TSizingRect)) then

FrmTabOrder.LBControls.Items.Add(Components[I].Name);

end;

Next, the routine shows the form. (FrmTabOrder.LBControls handles the re-ordering of the items itself.) If the user clicks the OK button, our program goes through FrmTabOrder.LBControls.Items, extracting the index of each item, assigning that value to the related control’s TabOrder property.

FrmTabOrder.ShowModal;

if FrmTabOrder.ModalResult = mrOK then begin

for I := 0 to FrmTabOrder.LbControls.Items.Count -1 do TWinControl(FindComponent(

FrmTabOrder.LbControls.Items[I])).TabOrder := I;

end;

Pretty simple, isn’t it? That’s all there is to runtime control of component tab order.

Changing Other Properties

Now we’ve got to tackle the issue of changing some of the other properties of the controls on the form. For instance, what if a user wanted to change the font or color of some DBEdit components to indicate that they were required fields? Well, there are a few simple things that we can do for some of these additional properties. As we’ve just discovered, it’s relatively easy to change the tab order for all of the controls. It’s also quite simple to change other individual properties of a control.

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

Changing Control Fonts at Runtime

For instance, in our sample app, we offer the user the ability to change every control’s character font via a main menu item, Adjust All Fonts. The code to do this is relatively simple, as shown in Listing 10.7.

Listing 10.7 Changing fonts of all controls on a form

procedure TFrmMain.AdjustMenu2Click(Sender: TObject); var

i : Integer; begin

{multiple Font changes}

if FontDialog1.Execute then begin

for i := 0 to ComponentCount - 1 do begin

try

if ((Components[i] is TWinControl) or (Components[i] is TGraphicControl)) and not ((Components[i] is TMenu) and (Components[i] is TMenuItem)) then

TMagic(Components[i]).Font := FontDialog1.Font;

except Continue;

end;

end;

end;

end;

Something interesting is going on here. Note the TMagic typecast in the actual assignment statement. TMagic is a control class defined in the TSizingRect unit that does absolutely nothing from a code action standpoint. The only reason it exists at all is to publish certain protected properties; namely, the Font property. Since the Font property of most controls is protected, we can’t change it at runtime without performing just such a typecast. Therefore,

TMagic makes it possible to change a control’s font at runtime.

Our sample application also offers users the ability to change a single control’s font via the Change Font option of the popup menu. This, too, is a relatively simple process, as shown in Listing 10.8.

Listing 10.8 Changing the font for a single control at runtime

procedure TFrmMain.ChangeFont1Click(Sender: TObject); begin

if FontDialog1.Execute then try

TMagic(PopupMenu1.PopupComponent).Font := FontDialog1.Font; except

Exit;

end;

end;

NOTE:

Even with the help of TMagic, there are some control classes—for instance, TMenu—that will raise an exception if you try to change their fonts, “magic” typecast or not. Therefore, it’s usually best to test the type of the control before attempting to change its font. In the above example, though, there was no need to filter out the “no-font” controls. This is because the code is launched via a popup menu. The controls that have a popup menu property do not object if you change their fonts—even if they don’t display any text. (An example of this would be a ScrollBar component.)

Changing Properties in an Object Inspector

We must now provide the users with a way to edit the additional properties Caption, CharCase, or Color, for instance. It’s not unreasonable for a user to expect the ability to change any of these items, considering that we’re letting them change everything else.

How does Delphi allow us to change these properties at design-time? Through the Object Inspector. So, for our project, we’ll use an Object Inspector of our own.

NOTE:

Because the Object Inspector presented in this chapter has been previously available as a commercial product, only a demo version (that is, without source code) is provided on the CD-ROM. Basically, this version limits the type of properties and controls available. However, it is otherwise fully functional, and there are no “nag screens”. Please consult the licensing agreement for more details. Information about the full version of the TMiniInspector class, which includes all source code, can be found on the CD-ROM accompanying this book, or by clicking the MiniInspector component’s About_This_Component property at design-time.

To install TMiniInspector to your component palette, choose Components|Install and add MiniOI.DCU. You will also want to ensure that the following three files are in the same directory as MiniOI.DCU:

OICompDemo.DCU

OICompDemo.DFM

MiniOI.DCR

The Object Inspector in our project works exactly like the Object Inspector supplied with Delphi. The user selects a component via the combo box at the top. He or she can then edit the properties by either typing in a value or by clicking on a button to select a separate property editing form, where one is available. Figure 10.8 shows TMiniInspector at runtime.

FIGURE 10.8 The MiniInspector component at runtime.

In our sample app, when the user selects either the Show Properties option from the main menu, or the View Properties option from the popup menu, we display the Object Inspector with a simple call to its Show method.

MiniInspector1.Show;

Then, if the user activated the Object Inspector via the popup menu, we’ll tell the TMiniInspector component to show the control that was right-clicked in the first place.

if PopupMenu1.PopupComponent <> nil then MiniInspector1.ShowThisComponent(PopupMenu1.PopupComponent);

The ShowThisComponenent method is a function that takes a TComponent parameter and returns a boolean value. If the component type passed to the function can be found in the combo box, it will be displayed and the function will return a value of True. If the component can’t be found, or if the MiniInspector is not visible, the function will return a value of False.

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

Go!

Keyword

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

Saving Component Changes Made at Runtime

Now that we have methods for changing just about all of the elements of a user interface, we’d better come up with a way to save the changes so that they can become “persistent” across sessions. Users will not be amused if they’re forced to alter the UI each time they run the application! While we might be tempted to just plunge in and use .INI files—or maybe the Windows 95 Registry if we’re brave enough—there are some serious drawbacks to both of these approaches. The problem is that since each component has numerous properties of different types, there’s no straightforward way to write a generic “Save_This_Component” method.

If we tried, we’d probably end up testing each component’s type, and then saving certain properties based on that type. This isn’t very efficient, you’ll agree. On the other hand, we could decide to save only those properties which are common to all components. Since the TComponent type—the ancestor of all other component types—has only nine properties (not including the Left, Top, Width, and Height properties), this, too, would be useless.

However, all is not lost! There are several very good mechanisms for saving and loading the properties of components. All you have to do is dig through Borland’s documentation and experiment a little bit.

In this expedition into the documentation, our final destination is the TFiler / TWriter / TReader family of objects. According to the Delphi 2 help files, TFiler is “the abstract base object for the reader objects and writer objects that Delphi uses for saving (and loading) forms and components in form files.”

That definition says something very important to us; namely, that the TWriter and TReader objects can be used to save and load a component’s properties from a file. By associating an instance of the TWriter or TReader class with an instance of TFileStream, we can, in fact, use the

WriteRootComponent and ReadRootComponent methods to do exactly what we want.

Snag: Components with Components as Properties

The only limitation of these methods is that there are a few types of components that cannot be saved directly. The components in question are those that have other components as properties.

The problem occurs when trying to load these component properties from the file. Since these components are saved on their own, loading them as properties of another component raises an exception with a message to the effect of “A component named Widget1 already exists.”

Thankfully, this behavior seems to be limited to only four types of components: TMainMenu,

TMenuItem, TPopupMenu, and TForm.

For our purposes, the first three don’t really matter. However, we probably should give users the power to save some of the form’s properties. Since there aren’t likely to be that many properties we really need to be concerned with, it’s simpler to save the ones we do care about.

In Listing 10.9 is the code to save a form’s properties, as executed in the FormCloseQuery event. As is my style, I’ll discuss pertinent portions of the event handler in detail shortly.

Listing 10.9 The event handler for the FormCloseQuery event

procedure TFrmMain.FormCloseQuery(Sender: TObject;

var CanClose: Boolean);

var

Writer : TWriter; FileStream : TFileStream; i : Integer;

TempRect : TRect;

begin

{Just to be on the safe side, we delete the old *.KAD file:} DeleteFile(ExtractFilePath(Application.ExeName) +

TObject(Self).ClassName + '.KAD'); {Then, go ahead & re-write it:}

FileStream := TFileStream.Create(ExtractFilePath(Application.ExeName)

+ TObject(Self).ClassName + '.KAD',fmOpenWrite or fmCreate); for i := 0 to ComponentCount-1 do

begin

{There are some controls that should not (or cannot) be

saved in this manner. Luckily, they're ones you're not likely to want to save anyhow:}

if ((Components[i] is TSizingRect) or (Components[i] is TMenu) or (Components[i] is TMenuItem) or (Components[i] is TPopupMenu) or (not(Components[i] is TControl))) then

Continue;

Writer := TWriter.Create(FileStream, SizeOf(Components[i])); Writer.WriteRootComponent(Components[i]);

Writer.Free;

end;

{Save & Load form properties here:} TempRect.Top := Self.Top; TempRect.Left := Self.Left;

TempRect.Bottom := TempRect.Top + Self.Height;

TempRect.Right := TempRect.Left + Self.Width; FileStream.Write(TempRect, SizeOf(TRect)); FileStream.Write(Self.Color, SizeOf(TColor)); FileStream.Free;

{Don't forget to 'OK' the closing of the form!} CanClose := True;

end;

Now, let’s take a detailed look at how all this works. First, just to be on the safe side, we delete the old *.KAD file. Next, we re-create it.

FileStream := TFileStream.Create(ExtractFilePath

(Application.ExeName) +