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

(Ebook - Pdf) Kick Ass Delphi Programming

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

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!

-----------

CHAPTER 10

Dynamic User Interfaces

TERENCE GOGGIN

Moving Controls At Runtime

Re-sizing Controls At Runtime

Changing Tab Order At Runtime

Changing Control Fonts At Runtime

Building In A Runtime Object Inspector

Saving Runtime Component Changes

If your users don’t like the user interface you hand them, well, let them eat icons! No, wait, let them cook up an interface they can swallow....

Let’s face it: No two people look at data in the same way. After all, if

everybody had the same idea of how data should be presented, there would only be one “Personal Information Manager” program. But that’s not the case; the world is virtually littered with PIMs of all shapes and sizes.

Some PIMs are lucky accidents in interface design and receive immediate widespread acceptance. Others are difficult to use and probably only intuitive to their creators. The problem seems to be that there’s no in-between.

Occasionally, users will find one of these difficult-to-use programs so beneficial that they will force themselves to use it, no matter how difficult a task learning it might be. However, you can’t count on this when you’re designing a program. Mostly, you can count on complaints.

A perfect example of this is the Word 6.0 toolbar. Maybe the purpose of those curvy arrow buttons has always been clear to you. On the other hand, you might think the whole thing is far too cluttered and confusing. Again, there’s really no middle ground: It’s either intuitive or it isn’t.

Since the goal of any software company is, in the end, to sell as many units as possible, the designers of graphical interfaces can’t ignore these “everything is in the wrong place” customers—yet they can’t alter the entire design to suit the quirks of individual end users.

Until now, this problem hasn’t really been properly addressed. No one has attempted a do-it-yourself design for end users. But, with Delphi 2.0, a little bit of creative coding, and a runtime Object Inspector, you can!

First, we’ll take a look at what a simple database application with dynamic design capabilities might look like. Then, we can examine some of the mechanics that make this on-the-fly user interface design possible in the first place.

An Example “UI-It-Yourself” Application

Figure 10.1 is a “composite” screen shot of a sample database application that shows all of the options you can offer to the end user.

FIGURE 10.1 Custom UI options.

The selected item from the form’s main menu offers three options:

Adjust All Fonts (pick a new font for all of the controls;

Tab Order (set the Tab Orders of the controls); and

Show Properties (show the Object Inspector).

There’s also a popup menu that offers the ability to change the form’s background color.

In addition, there is another popup menu that offers four choices:

Escape/No changes (cancels possible changes);

Adjust Size & Position (re-size and move the control);

Change Font (change the individual control’s font); and

View Properties (show the Object Inspector).

Every control’s PopupMenu property points to this popup menu.

On the left side of the screen is a runtime Object Inspector that allows users to view and change some of the additional properties of the controls.

The greatest part about this dynamic, do-it-yourself interface is that there’s a simple “jumping-off point” boilerplate project included on the CD-ROM (Starter.Dpr). You can add this project to your Object Repository and simply

draw on it when you need to. It’s that easy!

As you can see from this first example UI, we’ll end up capturing many of Delphi’s design-time features and making them available to the end user at runtime.

Building-in a “Delphi” for Your Users

When you design a program using Delphi, you need a few tools to define the look and feel you want. Similarly, users will need the following tools to help them define the look and feel they want:

A way to move controls at runtime;

A way to resize controls at runtime;

A way to change the Tab Order of the controls as they move them around;

A way to change additional properties of controls, such as color or border style, and

A way to automatically save & load the changes they’ve made.

Of course, it’s vital that we make each of these tools fast, simple, and easy-to-use. What we want is for our users to have much (if not all) of the same flexibility that we programmers have at design-time. In a sense, we’re giving them their own, somewhat limited version of Delphi in our apps, to be used at runtime. Explaining how to accomplish each of the points listed above is what this chapter is about.

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!

-----------

Moving Controls

While there are several ways to move controls at runtime, the best method for our purposes involves an almost undocumented WM_SYSCOMMAND trick. In order to move a

TWinControl, you call ReleaseCapture and send the control a WM_SYSCOMMAND message, specifying the literal $F012 as the wParam parameter. Or, in code terms:

ReleaseCapture;

SendMessage(SomeWinControl.Handle, WM_SYSCOMMAND, $F012, 0);

The results of this code, as seen, on the screen, are shown in Figure 10.2.

FIGURE 10.2 Moving a Windows button.

Visually, the effect is the same as moving a modal dialog box—a thin, dotted outline of the control follows the cursor until the mouse button is released.

As you may have noticed, however, this method does have a limitation: It requires a window handle to work. WinControl components have a window handle, GraphicControl components do not. Therefore, components of type TGraphicControl (such as TLabel) are not supported. Obviously, for our dynamic forms solution to be truly useful and complete, we must find a way to support GraphicControl components.

In order to get around this, then, we must enhance the WM_SYS COMMAND mechanism just described. Of course, WM_SYSCOMMAND can’t be used on GraphicControl components directly, but there is a way to fake it by creating a transparent TWinControl that will sit above the control being moved.

When a user selects the Adjust Size & Position popup menu option in the sample application, we’ll place our transparent TWinControl above the selected control. The user will be able to drag the transparent control around (via WM_SYSCOMMAND using the

$F012 parameter) as if it were the “selected” control. In other words, when the user clicks the “selected” control and begins to drag it somewhere, they’ll really be dragging our transparent TWinControl. Then, when the user opts to keep the changes they’ve made (again by selecting the Adjust Size & Position option), we’ll hide the transparent TWinControl and programmatically move the “selected” control to the new location.

In fact, this is exactly what Delphi does at design-time. If you look closely, you can see that when you drag a control around in Delphi, you’re really dragging a thick-bordered transparent rectangle. This is shown in Figure 10.3.

FIGURE 10.3 Dragging a control within Delphi at design-time.

In essence, this transparent rectangle appears above the control you wish to move. From the time you click the “selected” control until you let the mouse button up, the transparent rectangle follows your cursor. As soon as you let the mouse button up, though, the transparent rectangle disappears, and the control you intended to move jumps to the new location.

We will create our own transparent TWinControl derivative, called the SizingRect component, of class TSizingRect. Again, the TSizingRect class will serve as a control’s stand-in while that control is being dragged around.

The important methods of class TSizingRect are CreateParams and Paint. The CreateParams method is used to set up certain behavioral aspects of the control before it’s actually created. We’ll use the CreateParams method to make our control transparent, as shown in Listing 10.1.

Listing 10.1 The TSizingRect.CreateParams Method

procedure TSizingRect.CreateParams(var Params: TCreateParams); begin

inherited CreateParams(Params);

Params.ExStyle := Params.ExStyle + WS_EX_TRANSPARENT;

end;

The Paint method, shown in Listing 10.2, draws the thick rectangle border our users will see when a SizingRect is being used. When drawing this 3-pixel wide rectangle, we set the canvas’s Pen.Mode property to pmNot. This ensures that, just as with Delphi’s re-sizing mechanism, the rectangle drawn will always be a different color than the form.

Listing 10.2 The TSizingRect.Paint Method

procedure TSizingRect.Paint; begin

inherited Paint;

if fVisible = False then Exit;

Canvas.Pen.Mode := pmNot; Canvas.Pen.Width := 3; Canvas.Brush.Style := bsClear; Canvas.Rectangle(0, 0, Width, Height);

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!

-----------

Re-sizing Controls

Re-sizing controls is even easier than moving them. Again, we’ll use Delphi’s design-time mechanisms as a guide. Delphi allows you to re-size a selected control by clicking on one of the six black handles at the control’s edges, and dragging that handle until the altered size of the control suits you.

We’ll take the same approach to our runtime re-sizing. The only difference is that for the sake of simplicity (and to reduce the amount of coding needed) we’ll limit ourselves to just one of the six possible re-sizing handles.

Since we’re already using the TSizingRect class to manage the movement of the control, we’ll use it to manage the re-sizing of controls as well. We’ll simply designate the lower right-hand corner of TSizingRect to be a “hot spot” where users can click to re-size the control.

Also, to make this design easier to use, we’ll indicate which corner is “hot” by drawing a small white box there, and by changing the cursor every time the mouse passes over the white box. The action happens in the handler for the MouseMove event, which is shown in its entirety in Listing 10.3. I’ll be discussing the handler piece by piece in the text that follows.

Listing 10.3 SizingRect’s MouseMove Event Handler

procedure TFrmMain.SizingRect1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);

begin

{ControlDC and ControlRect are globally declared variables used by several procedures. These two lines, then, are just setup.} ControlDC := GetDC(TWinControl(Sender).Handle); GetWindowRect(TWinControl(Sender).Handle, ControlRect);

if ((X > TControl(Sender).Width -SizeVal) and (Y > TControl(Sender).Height -SizeVal)) then

begin

TWinControl(Sender).Cursor := crSizeNWSE; Rectangle(ControlDC, TWinControl(Sender).Width - SizeVal,

TControl(Sender).Height -SizeVal,

TControl(Sender).Width, TControl(Sender).Height);

end else

begin

TWinControl(Sender).Cursor := crDefault; end;

if ((TWinControl(Sender).Cursor = crSizeNWSE) and (ssLeft in Shift)) then

begin

TWinControl(Sender).Width := X;

TWinControl(Sender).Height := Y; end;

end;

After some initial setup, the handler tests to see if the mouse is within the sizing area. The SizeVal constant, which determines the area of the white sizing box, is defined in the TSizingRect unit. If the mouse is in the sizing area, the handler changes the cursor to the appropriate “sizing” cursor, and, of course, draws the rectangle.

if ((X > TControl(Sender).Width -SizeVal) and

(Y > TControl(Sender).Height -SizeVal)) then begin

TWinControl(Sender).Cursor := crSizeNWSE; Rectangle(ControlDC,

TWinControl(Sender).Width - SizeVal,

TControl(Sender).Height - SizeVal, TControl(Sender).Width, Control(Sender).Height);

end

If the mouse is outside the sizing area, the routine simply changes the cursor back to the default style.

else begin

TWinControl(Sender).Cursor := crDefault; end;

Finally, we check to see if the user is still re-sizing the control: If the cursor is equal to crSizeNWSE and the left mouse button is depressed, then the user is still re-sizing. The routine then sets the control’s bottom right corner to the mouse position.

if ((TWinControl(Sender).Cursor = crSizeNWSE) and (ssLeft in Shift)) then

begin

TWinControl(Sender).Width := X;

TWinControl(Sender).Height := Y; end;

end;

The overall effect here is that as long as the mouse is depressed and the cursor is in the hot spot, the sizing corner will follow the mouse’s movements.

Responding to the Popup Menu

In our sample app, the TSizingRect component is activated by PopupMenu1, which is set as the PopupMenu for each of the controls on the form. Figure 10.4 shows PopupMenu1 at runtime, after a

user has right-clicked the DBImage.

FIGURE 10.4 The popup menu for a right mouse click.

At this point, the user can make the following choices;

Do nothing (by selecting the Escape/No changes item);

Re-size or move the control (the Adjust Size & Position item);

Change the control’s font (Change Font); or

Display the MiniInspector (View Properties).

Selecting the Adjust Size & Position item invokes the TFrmMain.AdjustClick procedure, shown in Listing 10.4.

Listing 10.4 The Adjust Size & Position OnClick Handler

procedure TFrmMain.AdjustClick(Sender: TObject); begin

if (Adjust.Checked = True) then begin

if ((PopupMenu1.PopupComponent <> ComponentBeingAdjusted) and (PopupMenu1.PopupComponent <> SizingRect1)) 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 := False;

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; SizingRect1.Width := 40; SizingRect1.Height := 40;

MiniInspector1.ShowThisComponent(ComponentBeingAdjusted); ComponentBeingAdjusted := Self; { i.e., no control is }

end

{ currently selected. )

else

 

begin

if ((ComponentBeingAdjusted <> Self) and (PopupMenu1.PopupComponent <> ComponentBeingAdjusted))