📄 ch04.htm
字号:
{
MessageDlg("The delegation model says hello.", mtInformation,
TMsgDlgButtons() << mbOK, 0);
}
void __fastcall TForm1::FormKeyPress(TObject
*Sender, char &Key)
{
AnsiString S("OnKeyPress: " + AnsiString(Key));
MessageDlg(S, mtInformation, TMsgDlgButtons() << mbOK, 0);
}
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
MessageDlg(Key, mtInformation, TMsgDlgButtons() << mbOK, 0);
}
</FONT></PRE>
<P>After this introduction to event-oriented programming, it's time to step back
and see some of the theory behind the code. After explaining something of how
the
system works, this chapter goes on to give examples of how to take full advantage
of Windows event-oriented code base.
<H4><A NAME="Heading10"></A><FONT COLOR="#000077">Understanding Events</FONT></H4>
<P>Event-oriented programming isn't unique to
Windows, nor is it a chore that can
be handled only by an operating system. For instance, any DOS program could be based
around a simple loop that keeps running the entire time the program is in memory.
Here is a hypothetical example of how such code
might look:</P>
<PRE><FONT COLOR="#0066FF">do
{
CheckForMouseEvent(Events);
CheckForKeyPress(Events)
HandleEvents(Events);
} while (!Events.Done);
</FONT></PRE>
<P>This code represents a typical event-oriented loop. A simple
<TT>do..while</TT>
statement checks for keyboard and mouse events and then calls <TT>HandleEvents</TT>
to give the program a chance to respond to the events that are generated by the user
or the operating system.</P>
<P>The variable called
<TT>Events</TT> might be a record with a fairly simple structure:</P>
<PRE><FONT COLOR="#0066FF">struct TEvent {
int X, Y;
TButton MouseButton;
int Key;
bool Done;
};
</FONT></PRE>
<P><TT>X</TT> and <TT>Y</TT> give the current location
of the cursor, and <TT>Key</TT>
contains the value of the top event in the key buffer. The <TT>TButton</TT> type
might have a declaration that looks like this:</P>
<PRE><FONT COLOR="#0066FF">enum TButton {ButtonLeft, ButtonRight};
</FONT></PRE>
<P>These structures permit you to track where the mouse is, what state its buttons
are in, and what keys the user has pressed. Admittedly, this is a simple type of
event structure, but the principles involved mirror what is going on inside Windows
or
inside other event-oriented systems such as Turbo Vision. If the program being
written was an editor, pseudo-code for the <TT>HandleEvent</TT> for the program might
look like this:</P>
<PRE><FONT COLOR="#0066FF">void HandleEvent(TEvent: Events)
{
switch(Events.Key)
{
case "A..z":
WriteXY(Events.X, Events.Y, Events.Key);
break;
case EnterKey:
Write(CarriageReturn);
break;
case EscapeKey:
Events.Done = TRUE;
break;
}
}
</FONT></PRE>
<P>Given the preceding code, the program would go to location <TT>X,Y</TT> and write
the letter most recently pressed by the user. If the Enter key was pressed, a carriage
return would be written to the screen. A press on the Esc key
would cause the program
to terminate. All other keypresses would be ignored.</P>
<P>Code like this can be very powerful, particularly if you're writing a program
that requires animation. For instance, if you need to move a series of bitmaps across
the
screen, you want to move the bitmap a few pixels and then check to see whether
the user has pressed a button or hit a keystroke. If an event has occurred, you want
to handle it. If nothing occurred, you want to continue moving the bitmap.</P>
<P>I
hope the short code samples shown here give you some feeling for the way event-oriented
systems work. The only piece that's missing is an understanding of why Windows is
event- oriented.</P>
<P>Microsoft made Windows event-oriented in part because
multiple programs run under
the environment at the same time. In multitasking systems, the operating system needs
to know whether the user has clicked in a program or whether the click was in the
desktop window. If the mouse click occurred in a window
that was partially hidden
behind another window, it is up to the operating system to recognize the event and
bring that window to the foreground. Clearly, it wouldn't be appropriate for the
window itself to have to be in charge of that task. To ask
that much would place
an impossible burden on the programmer who created the window. As a result, it's
best for the operating system to handle all the keystrokes and mouse clicks, and
to then pass them on to the various programs in the form of events.
Any other system
would force every programmer to handle all the events that occurred when his or her
program had the focus, and to manipulate the entire operating system in response
to certain mouse events or keystrokes, such as Alt+Tab.</P>
<P>In
short, Windows programmers almost never directly monitor the hardware or hardware
interrupts. Instead, the operating system handles that task. It passes all external
events on to individual programs in the form of messages. In a typical event-oriented
system, the operating system continually polls the hardware in a loop and then sends
each event off to its programs in the form of some kind of event structure or event
variables. This is the same kind of activity you saw in the brief code snippets
shown
earlier.</P>
<P>You have seen that Windows handles mouse and keyboard events, and passes them
on to the appropriate window. The message that is generated in these cases gets sent
to the default window function, which, as you know, is called
<TT>DefWindowProc</TT>.
<TT>DefWindowProc</TT> is analogous to the <TT>HandleEvent</TT> function shown earlier.</P>
<P>The important point to understand is that Windows messages contain the information
that drives the entire operating environment.
Almost everything that happens inside
Windows is a message; if you really want to tap into the power of BCB, you need to
understand how these messages work.</P>
<P>One of the tricky parts of Windows event-oriented programming is extracting information
from the <TT>WPARAM</TT> and <TT>LPARAM</TT> variables passed to the window function.
In most cases, BCB frees you from the necessity of performing this task. For instance,
if you create an event for the <TT>OnMouseDown</TT> property, BCB directly
tells
you the <TT>X</TT> value and <TT>Y</TT> value where the event occurred. As a programmer,
you don't have to struggle to get the event and its associated values. As you will
see in the next section, everything about the event is shown to you in a
simple and
straightforward manner.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B><TT>WPARAM</TT> and <TT>LPARAM</TT>
are the types of the parameters passed to a standard <TT>WndProc</TT> as is
declared
in <TT>WinUser.h</TT>:</P>
<PRE><FONT COLOR="#0066FF">typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);</FONT></PRE>
</BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"></FONT></PRE>
<BLOCKQUOTE>
<P>These variables are usually
given names such as <TT>wParam</TT> and <TT>lParam</TT>
or <TT>WParam</TT> and <TT>LParam</TT>:</P>
<PRE><FONT COLOR="#0066FF">WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);</FONT></PRE>
</BLOCKQUOTE>
<PRE><FONT
COLOR="#0066FF"></FONT></PRE>
<BLOCKQUOTE>
<P>You will find that I sometimes refer to these parameters by type in all caps,
and sometimes as variable identifiers with mixed caps and small letters. In all cases,
I am referring to the same
variables passed to the user from the system. Even in
the discussion of the <TT>TMessage</TT> type, which occurs later in the chapter,
I am still referring to these same parameters, only in a slightly different context.
Specifically,
<TT>TMessage</TT> is a VCL type that contains exact copies of these
parameters. The <TT>X</TT> and <TT>Y</TT> parameters passed to an <TT>OnMouseDown</TT>
event are not exact copies of <TT>WPARAM</TT> and <TT>LPARAM</TT>, but variables
designed to
display information originally contained in <TT>WPARAM</TT> or <TT>LPARAM</TT>
after they have been parsed by the VCL.
<HR>
</BLOCKQUOTE>
<H3><A NAME="Heading12"></A><FONT COLOR="#000077">Using Sets to Track Messages</FONT></H3>
<P>Rather than
ask you to parse the <TT>LPARAM</TT> and <TT>WPARAM</TT> parameters,
BCB performs this chore for you and then passes the information on in the form of
parameters:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::FormMouseDown(
TObject
*Sender,
TMouseButton Button,
TShiftState Shift,
int X,
int Y)
</FONT></PRE>
<P>This is by far the most convenient way for you to handle events. BCB can also
give direct access to the values sent to you by the operating system. That is,
you
can handle <TT>WPARAM</TT> and <TT>LPARAM</TT> directly if you want. After you have
studied the EVENTS2 program and learned more about sets, I'll show you exactly how
to get at that raw data.</P>
<P>Take a moment to consider the <TT>Shift</TT>
parameter shown in the <TT>FormMouseDown</TT>
header. <TT>Shift</TT> is declared to be of type <TT>TShiftState</TT>:</P>
<PRE><FONT COLOR="#0066FF">enum Classes_1 { ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble };
typedef
Set<Classes_1, ssShift, ssDouble> TShiftState;
</FONT></PRE>
<P><TT>TShiftState</TT> is a set, that is, it's an instance of the <TT>Set</TT> template
class from <TT>SYSDEFS.H</TT>. To find out whether a particular element is a member
of the
set passed to you by BCB, you can perform simple tests using the <TT>Contains</TT>
method:</P>
<PRE><FONT COLOR="#0066FF">ShiftKey->Checked = Shift.Contains(ssShift);
ControlKey->Checked = Shift.Contains(ssCtrl);
LeftButton->Checked =
Shift.Contains(ssLeft);
RightButton->Checked = Shift.Contains(ssRight);
</FONT></PRE>
<P>This code asks whether the element <TT>ssRight</TT> is in the set passed to you
via the <TT>Shift</TT> variable. If it is, the code sets the state of a
<TT>TCheckBox</TT>
component.</P>
<P>Here is how you can declare a set at runtime:</P>
<PRE><FONT COLOR="#0066FF">TShiftState LeftShift;
LeftShift << ssLeft << ssShift;
</FONT></PRE>
<P>Given this set, the <TT>Contains</TT> operator
returns <TT>True</TT> if you ask
about <TT>ssLeft</TT> or <TT>ssShift</TT>.</P>
<P>Besides the important <TT>Contains</TT> method, there are three key operators
you can use with the <TT>Set</TT> class:</P>
<PRE><FONT COLOR="#0066FF">+ Union
-
Difference
* Intersection
</FONT></PRE>
<P>All three of these operators return a set, whereas <TT>Contains</TT> returns a
Boolean value. The SETSEXP program, shown in Listing 4.2, shows how to work with
these key elements of the <TT>Set</TT>
template class.<BR>
<BR>
<A NAME="Heading13"></A><FONT COLOR="#000077"><B>Listing 4.2. The SETEXP program
shows how to use operators to track the members of sets such as TShiftState.</B></FONT></P>
<PRE><FONT
COLOR="#0066FF">///////////////////////////////////////
// Copyright (c) 1997 by Charlie Calvert
//
#include <vcl.h>
#pragma hdrstop
#include "Main.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall
TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void TForm1::CheckState(TShiftState Shift)
{
ShiftKey->Checked = Shift.Contains(ssShift);
ControlKey->Checked = Shift.Contains(ssCtrl);
LeftButton->Checked =
Shift.Contains(ssLeft);
RightButton->Checked = Shift.Contains(ssRight);
}
void __fastcall TForm1::UnionClick(TObject *Sender)
{
AnsiString Operators[3] = {"+", "*", "-"};
TShiftState FinalSet;
TShiftState LeftShift;
TShiftState LeftCtrl;
LeftShift << ssLeft << ssShift;
LeftCtrl << ssLeft << ssCtrl;
switch (TOptType(dynamic_cast <TBitBtn*>(Sender)->Tag))
{
case otUnion:
FinalSet =
LeftShift + LeftCtrl;
break;
case otIntersection:
FinalSet = LeftShift * LeftCtrl;
break;
case otDifference:
FinalSet = LeftShift - LeftCtrl;
break;
}
CheckState(FinalSet);
Label2->Caption =
Operators[dynamic_cast<TBitBtn *>(Sender)->Tag];
}
void __fastcall TForm1::Label4MouseDown(TObject *Sender, TMouseButton Button,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -