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

(Ebook - Pdf) Kick Ass Delphi Programming

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

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!

-----------

Sharing Event Handlers

In an untidy little room across town, an ominous figure was bent over a book, reading intently.

Casebook No. 16, March 29: Among the bills in today’s mail, I found a wedding invitation from Blade Porter and Cinder Holte-Mullingham-LaFong, a couple Helen and I had gotten to know while we were attending Art School. The announcement read “Come and share our joyous event with us.” We had always assumed they would get married. Now it was official.

As I tossed the card on my desk, I got to thinking about ways it’s possible to “share events” in an entirely different way—by using common handlers to deal with events that require similar actions.

I decided to create a simple application that would let me explore this concept. After a little pondering, I settled on a little “To Do List” program that would be a spin-off of the Drag/Drop Demo I had written a few days ago.

As in the earlier demo, the new program would feature an edit box in which the user could type a comment. Instead of dragging it to a calendar, this time I would provide three separate string grids—one for morning tasks, another for afternoon duties, and a final one for evening activities. The three grids would be placed on separate pages of a tabbed notebook. I created a design model of the form, which can be seen in Figure 14.1.

FIGURE 14.1 The shared events demo.

Taking a First Run

The scenario would be to select the proper page and then drag the task description string from the edit box to the grid on that page. The operation would be virtually identical for each of the three pages, the only difference being which grid would receive the string. There should therefore be a way for the three string grids to share their OnDragOver and OnDragDrop event handlers in common.

I suppose this is a personal preference, but I would like shared event handlers to have a more generic name than what is automatically generated by Delphi. I decided to call the two handlers

GridDragOver and GridDragDrop.

Before proceeding any further, I want to document the 8-step method I used to create generic-sounding routine names for the handlers:

1.Double-click on the name of the event to be handled, as it appears in the Object Inspector.

2.Delphi automatically names the routine, combining the names of the component and the event to produce a unique identifier. More importantly, it automatically generates the call parameter list for the specific event. (Being lazy, I would much rather have Delphi do this for me than create it from scratch.)

3.Type anything (a semicolon does nicely) between the begin and end of the empty routine created by Delphi. This will prevent Delphi from automatically deleting the routine if you try to save it.

4.Edit the name of the routine, then double-click to highlight it, and press Ctrl-C to copy the new name to the clipboard.

5.Jump to the beginning of the file, and find the original handler declaration within the form’s declaration. Highlight the original name there, and tap Ctrl-V to replace it with the new name.

6.At some point in the process, Delphi will complain that the name it originally assigned (still present in the Object Inspector) can’t be found. Give Delphi the okay to remove it.

7.Move the cursor to the event slot in the Object Inspector, where this all began. Use the

Ctrl-V combination to paste in the new name.

8. Save the file.

The routines I wrote appear in Listing 14.1.

Listing 14.1 Common handlers for OnDragOver and OnDragDrop events

{ Common event handler for all the grids' OnDragOver event. } procedure TShareEventDemoForm.GridDragOver(Sender, Source: TObject;

X, Y: Integer; State: TDragState; var Accept: Boolean); begin

{ We can accept anything, but only from the EditBox. } Accept := Source is TEdit;

end;

{ Common event handler for all the grids' OnDragDrop event. } procedure TShareEventDemoForm.GridDragDrop(Sender, Source : TObject;

X, Y : Integer); begin

{ Drop the load on the currently selected grid. } DropEditString(CurrentGrid);

end;

{Common event handler support routine that drops the string

in the EditBox onto the specified grid. Also clears the EditBox. }

procedure TShareEventDemoForm.DropEditString(AGrid : TStringGrid); begin

if AGrid <> nil then with AGrid do begin

Cells[0, RowCount - 1] := EditBox.Text; RowCount := RowCount + 1;

EditBox.Text := '';

end; { with } end;

{ Return a pointer to the grid on the currenly selected tab sheet. } function TShareEventDemoForm.CurrentGrid : TStringGrid;

begin

Result := nil;

if PageControl.ActivePage = MorningSheet then Result := MorningGrid else

if PageControl.ActivePage = AfternoonSheet then Result := AfternoonGrid else

if PageControl.ActivePage = EveningSheet then Result := EveningGrid;

end;

The OnDragOver routine is very straightforward. As long as the source object for the drag is the edit box, it can be accepted by any of the grids. I chose to split the OnDragDrop handling into two routines: a main handler and a support method. The handler merely calls the support routine, using a function which returns a pointer to the grid on the currently selected tabsheet. Delphi’s ability to mask the use of pointers really comes into play here, making it very straightforward to pull some shenanigans with pointers, while keeping the program exceedingly readable. Given the pointer, the DropEditString routine adds the string from the edit box to the appropriate string grid, updating the grid’s row counter and clearing out the edit box.

The handlers worked as expected for the grid on the first tabsheet (MorningGrid). I went back to the Object Inspector and selected the same handlers for the other two grids. Again, everything worked perfectly.

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!

-----------

Down a Crooked Path

It must be a quirk in my personality (or maybe I’m just a nerd at heart), but this was just too easy. I wanted to take the process a step further, where I could drop a string from the edit box onto any of the string grids, simply by dropping it onto the appropriate tab. In a way, I wish I had never started down this road.

I first learned that although PageControls appear similar to the TabSet and Notebook components provided with Delphi 1.0, they have some significant differences, not the least of which concerns the tabs. TabSets have a method that will tell you the index of a tab, given an x-y position. The wrapper-relationship of PageControls gives most of the power to the TabSheets, leaving little to the PageControl, which is pretty well limited to knowing which is the currently selected sheet.

I would need to calculate where the tabs were, so I could know which tab the mouse was pointing to. From that information, I could easily determine the associated tabsheet. But there was another problem: PageControls automatically size each tab’s width individually, based on the length of its caption string. What to do?

I decided to support drops onto tabs only if the tabs had been manually sized by setting the PageControl’s TabHeight and TabWidth properties to some value other than zero. I should have stopped there. But I decided I also wanted to provide a feature that would automatically size the tabs at equal width, based on the length of the longest caption. That eventually led me to a much longer version of the code, the complete version of which is shown in Listing 14.2.

Listing 14.2 Complete code for the common event handler demo application

{———————————————————————————————————————————————————}

{

Event Handler Sharing Demonstration

}

{

SHARMAIN.PAS : Main Unit

}

{

By Ace Breakpoint, N.T.P.

}

{

Assisted by Don Taylor

}

{

 

}

{ Application that demonstrates the sharing of

}

{ event handlers within an application, as part

}

{ of a drag/drop operation.

}

{

 

}

{ Written for *Kick-Ass Delphi Programming*

}

{ Copyright (c) 1996 The Coriolis Group, Inc.

}

{

Last Updated 3/29/96

}

{———————————————————————————————————————————————————} unit SharMain;

interface

uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls, ExtCtrls, ComCtrls;

type

TShareEventDemoForm = class(TForm) EditBox: TEdit;

Label1: TLabel;

QuitBtn: TButton;

Panel1: TPanel; PageControl: TPageControl; MorningSheet: TTabSheet; AfternoonSheet: TTabSheet; EveningSheet: TTabSheet; MorningGrid: TStringGrid; AfternoonGrid: TStringGrid; EveningGrid: TStringGrid;

procedure FormCreate(Sender: TObject); procedure QuitBtnClick(Sender: TObject);

procedure EditBoxMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

procedure GridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

procedure GridDragOver(Sender, Source: TObject;

X, Y: Integer; State: TDragState; var Accept: Boolean); procedure GridDragDrop(Sender, Source : TObject;

X, Y : Integer);

procedure PageControlDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);

procedure PageControlDragDrop(Sender, Source: TObject; X, Y: Integer);

private

CopyDrag : Boolean;

function ManualTabsSet : Boolean; function CurrentGrid : TStringGrid;

function TabGrid(X : Integer) : TStringGrid; procedure SetTabSizes;

procedure DropEditString(AGrid : TStringGrid); procedure DropGridString(TargetGrid : TStringGrid);

public

{ Public declarations } end;

var

ShareEventDemoForm: TShareEventDemoForm;

implementation

{$R *.DFM}

{ Return the length in pixels of a string for display, given a handle to the window in which it will appear, along with a handle to the window’s font. }

function StringWidth

(WinHnd : HWND; FntHnd : HWND; Text : String) : Integer; var

DCHnd : HWND;

StrSize : TSize;

TextArr : array[0..127] of char; begin

Result := -1;

DCHnd := GetDC(WinHnd);

if GetMapMode(DCHnd) = MM_TEXT then begin

SelectObject(DCHnd, FntHnd); StrPCopy(TextArr, Text);

if GetTextExtentPoint32(DCHnd, @TextArr, Length(Text), StrSize) then Result := StrSize.Cx

end; ReleaseDC(WinHnd, DCHnd); end;

{Return the height in pixels of a font for display, given a handle to the window in which it will appear, along with a handle to the window’s font. The height must include the ascent, descent and inner leading. }

function FontHeight(WinHnd : HWND; FntHnd : HWND) : Integer; var

DCHnd : HWND;

TextMex : TTextMetric; begin

Result := -1;

DCHnd := GetDC(WinHnd);

if GetMapMode(DCHnd) = MM_TEXT then begin

SelectObject(DCHnd, FntHnd); GetTextMetrics(DCHnd, TextMex); Result := TextMex.tmHeight; end;

ReleaseDC(WinHnd, DCHnd); end;

procedure TShareEventDemoForm.FormCreate(Sender: TObject); begin

PageControl.ActivePage := MorningSheet; SetTabSizes;

CopyDrag := False; end;

procedure TShareEventDemoForm.QuitBtnClick(Sender: TObject); begin

Close;

end;

procedure TShareEventDemoForm.EditBoxMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

{ Before initiating a drag, make sure the left button

is pressed, the EditBox has text in it, and that we’re not in a double-click situation. }

if (Button = mbLeft)

and (EditBox.Text <> '') and not (ssDouble in Shift)

then TEdit(Sender).BeginDrag(False); end;

{ Common event handler for all the grids' OnMouseDown event. } procedure TShareEventDemoForm.GridMouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var

TheGrid : TStringGrid; begin

{We’re initiating a drag from whichever string grid is currently selected. Set the CopyDrag flag if the control key is down. Before starting the drag, make sure the left mouse button is pressed, there is text in the selected row of the grid, and we’re not in

a double-click situation. }

TheGrid := CurrentGrid; CopyDrag := ssCtrl in Shift; if (Button = mbLeft)

and (TheGrid.Cells[0, TheGrid.Row] <> '') and not (ssDouble in Shift)

then TStringGrid(Sender).BeginDrag(False); end;

{ Common event handler for all the grids' OnDragOver event. } procedure TShareEventDemoForm.GridDragOver(Sender, Source: TObject;

X, Y: Integer; State: TDragState; var Accept: Boolean); begin

{ We can accept anything, but only from the EditBox. } Accept := Source is TEdit;

end;

{ Common event handler for all the grids' OnDragDrop event. } procedure TShareEventDemoForm.GridDragDrop(Sender, Source : TObject;

X, Y : Integer); begin

{ Drop the load on the currently selected grid. } DropEditString(CurrentGrid);

end;

procedure TShareEventDemoForm.PageControlDragOver(Sender, Source: TObject; X, Y: Integer;

State: TDragState; var Accept: Boolean);

begin

{ A drop on a tab is acceptable if it comes fron the EditBox, or if it comes from a grid -- as long as the tab isn't the

one for the grid originating the drag. In either case, tabs must have been manually set. }

Accept := ManualTabsSet and

(

(Source is TEdit) or

((Source is TStringGrid) and (CurrentGrid <> TabGrid(X))) );

end;

procedure TShareEventDemoForm.PageControlDragDrop(Sender, Source: TObject; X, Y: Integer);

begin

{Get the string from the proper source and drop it on the grid associated with the tab at position X. }

if (Source is TEdit) then DropEditString(TabGrid(X));

if (Source is TStringGrid) then DropGridString(TabGrid(X));

end;

{Return True only if both tab height and width have been set manually. } function TShareEventDemoForm.ManualTabsSet : Boolean;

begin

Result := (PageControl.TabHeight > 0) and (PageControl.TabWidth > 0); end;

{Return a pointer to the grid on the currenly selected tab sheet.} function TShareEventDemoForm.CurrentGrid : TStringGrid;

begin

Result := nil;

if PageControl.ActivePage = MorningSheet then Result := MorningGrid else

if PageControl.ActivePage = AfternoonSheet then Result := AfternoonGrid else

if PageControl.ActivePage = EveningSheet then Result := EveningGrid;

end;

{Return a pointer to the grid on the tab sheet associated with the tab located at position X. }

function TShareEventDemoForm.TabGrid(X : Integer) : TStringGrid; var

Idx : Integer; begin

Result := nil; with PageControl do begin

Idx := X div TabWidth; case Idx of

0 : Result := MorningGrid;

1 : Result := AfternoonGrid;

2 : Result := EveningGrid; end; { case }

end; { with } end;

{Adjust tab height and width as required to give a pleasing appearance, and to ensure tabs each have the same height and width. }

procedure TShareEventDemoForm.SetTabSizes; var

i : Integer;

Len : Integer; MaxWidth : Integer; s : String;

begin

with PageControl do begin

if TabWidth > 0 then begin

MaxWidth := -1;

for i := 0 to PageCount - 1 do begin

s := Pages[i].Caption;

Len := StringWidth(Handle, Font.Handle, s); if Len > MaxWidth then MaxWidth := Len; end;

if MaxWidth > 0 then TabWidth := MaxWidth + 10; end;

if TabHeight > 0

then PageControl.TabHeight := FontHeight(Handle, Font.Handle) + 5;

end; { with } end;

{Common event handler support routine that drops the string

in the EditBox onto the specified grid. Also clears the EditBox. }

procedure TShareEventDemoForm.DropEditString(AGrid : TStringGrid); begin

if AGrid <> nil then with AGrid do

begin

Cells[0, RowCount - 1] := EditBox.Text; RowCount := RowCount + 1;

EditBox.Text := ''; end; { with }

end;

{ Common event handler support routine that drops the string in the selected row of the currently selected grid onto

the specified grid. If it is a "move" operation, the current grid is purged of the string and the grid is compressed. }

procedure TShareEventDemoForm.DropGridString(TargetGrid : TStringGrid); var

i : Integer; begin

if TargetGrid <> nil then begin

with TargetGrid do begin

Cells[0, RowCount - 1] := CurrentGrid.Cells[0, CurrentGrid.Row];

RowCount := RowCount + 1; end; { with }

if not CopyDrag

then with CurrentGrid do begin

Cells[0, Row] := ''; if Row < RowCount - 1

then for i := Row to RowCount - 1 do Cells[0, i] := Cells[0, i + 1];

RowCount := RowCount - 1; end; { with }

end;

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.