📄 ch04.htm
字号:
<P>You can test to see which virtual key has been pressed by writing code that looks
like this:</P>
<PRE><FONT COLOR="#0066FF">if (Key == VK_CANCEL)
DoSomething();
</FONT></PRE>
<P>This code
simply tests to see whether a particular key has been pressed. If it
has, the code calls the <TT>DoSomething</TT> function.</P>
<P>To help you understand virtual keys, the <TT>GetKey</TT> method from the <TT>VKEYS</TT>
unit returns a string stating
exactly what key has been pressed:</P>
<PRE><FONT COLOR="#0066FF">AnsiString GetKey(WORD K)
{
AnsiString S;
switch (K)
{
case VK_LBUTTON: S = "VK_LButton"; break;
case VK_RBUTTON : S = "VK_RBUTTON"; break;
case VK_CANCEL : S = "VK_CANCEL"; break;
case VK_MBUTTON : S = "VK_MBUTTON"; break;
case VK_BACK : S = "VK_BACK"; break;
case VK_TAB : S = "VK_TAB"; break;
case VK_CLEAR :
S = "VK_CLEAR"; break;
case VK_RETURN : S = "VK_RETURN"; break;
case VK_SHIFT : S = "VK_SHIFT"; break;
case VK_CONTROL : S = "VK_CONTROL"; break;
case VK_MENU : S =
"VK_MENU"; break;
case VK_PAUSE : S = "VK_PAUSE"; break;
case VK_CAPITAL : S = "VK_CAPITAL"; break;
case VK_ESCAPE : S = "VK_ESCAPE"; break;
case VK_SPACE : S =
"VK_SPACE"; break;
case VK_PRIOR : S = "VK_PRIOR"; break;
case VK_NEXT : S = "VK_NEXT"; break;
case VK_END : S = "VK_END"; break;
case VK_HOME : S = "VK_HOME";
break;
case VK_LEFT : S = "VK_LEFT"; break;
case VK_UP : S = "VK_UP"; break;
case VK_RIGHT : S = "VK_RIGHT"; break;
case VK_DOWN : S = "VK_DOWN"; break;
case
VK_SELECT : S = "VK_SELECT"; break;
case VK_PRINT : S = "VK_PRINT"; break;
case VK_EXECUTE : S = "VK_EXECUTE"; break;
case VK_SNAPSHOT : S = "VK_SNAPSHOT"; break;
case VK_INSERT
: S = "VK_INSERT"; break;
case VK_DELETE : S = "VK_DELETE"; break;
case VK_HELP : S = "VK_HELP"; break;
case VK_NUMPAD0 : S = "VK_NUMPAD0"; break;
case VK_NUMPAD1 : S =
"VK_NUMPAD1"; break;
case VK_NUMPAD2 : S = "VK_NUMPAD2"; break;
case VK_NUMPAD3 : S = "VK_NUMPAD3"; break;
case VK_NUMPAD4 : S = "VK_NUMPAD4"; break;
case VK_NUMPAD5 : S =
"VK_NUMPAD5"; break;
case VK_NUMPAD6 : S = "VK_NUMPAD6"; break;
case VK_NUMPAD7 : S = "VK_NUMPAD7"; break;
case VK_NUMPAD8 : S = "VK_NUMPAD8"; break;
case VK_NUMPAD9 : S =
"VK_NUMPAD9"; break;
case VK_MULTIPLY : S = "VK_MULTIPLY"; break;
case VK_ADD : S = "VK_vkADD"; break;
case VK_SEPARATOR : S = "VK_SEPARATOR"; break;
case VK_SUBTRACT : S =
"VK_SUBTRACT"; break;
case VK_DECIMAL : S = "VK_DECIMAL"; break;
case VK_DIVIDE : S = "VK_DIVIDE"; break;
case VK_F1 : S = "VK_F1"; break;
case VK_F2 : S =
"VK_F2"; break;
case VK_F3 : S = "VK_F3"; break;
case VK_F4 : S = "VK_F4"; break;
case VK_F5 : S = "VK_F5"; break;
case VK_F6 : S = "VK_F6"; break;
case VK_F7 : S = "VK_F7"; break;
case VK_F8 : S = "VK_F8"; break;
case VK_F9 : S = "VK_F9"; break;
case VK_F10 : S = "VK_F10"; break;
case VK_F11 : S =
"VK_F11"; break;
case VK_F12 : S = "VK_F12"; break;
case VK_F13 : S = "VK_F13"; break;
case VK_F14 : S = "VK_F14"; break;
case VK_F15 : S = "VK_F15"; break;
case VK_F16 : S = "VK_F16"; break;
case VK_F17 : S = "VK_F17"; break;
case VK_F18 : S = "VK_F18"; break;
case VK_F19 : S = "VK_F19"; break;
case VK_F20 : S =
"VK_F20"; break;
case VK_F21 : S = "VK_F21"; break;
case VK_F22 : S = "VK_F22"; break;
case VK_F23 : S = "VK_F23"; break;
case VK_F24 : S = "VK_F24"; break;
case VK_NUMLOCK : S = "VK_NUMLOCK"; break;
case VK_SCROLL : S = "VK_SCROLL"; break;
default:
S = char(K);
}
return S;
}
</FONT></PRE>
<P>This function is really just a giant <TT>case</TT> statement that
checks to see
whether the <TT>Key</TT> variable is equal to any of the virtual keys. If it is not,
the code assumes that it must be one of the standard keys between <TT>A</TT> and
<TT>Z</TT>. (See the <TT>else</TT> clause in the code to see how these
standard keys
are handled.)</P>
<P>As explained in the last paragraph, the virtual key codes do not cover normal
letters such as <TT>A</TT>, <TT>B</TT>, and <TT>C</TT>. In other words, there is
no value <TT>VK_A</TT> or <TT>VK_B</TT>. To test for
these letters, just use the
standard ASCII values. In other words, test whether <TT>Key</TT> is equal to <TT>65</TT>,
or whether <TT>char(key) = `A'</TT>. The point here is that these letters already
have key codes. That is, the key codes for these
letters are the literal values <TT>A</TT>,
<TT>B</TT>, <TT>C</TT>, and so on. Because these are perfectly serviceable values,
there is no need to create virtual key codes for the standard letters of the alphabet,
or for numbers.</P>
<P>To see the
value as a number, make the default section of the code look like this:</P>
<PRE><FONT COLOR="#0066FF">S = K;
</FONT></PRE>
<P>Then if you press the A key, you get back <TT>65</TT>. If you want to see the
letter <TT>`A'</TT> instead, write the
following:</P>
<PRE><FONT COLOR="#0066FF">S = char(K);
</FONT></PRE>
<P>You probably won't have much use for the <TT>GetKey</TT> routine in a standard
BCB program. However, it is useful when you are trying to understand virtual keys
and the
<TT>OnKeyDown</TT> event. As a result, I have included it in this program.
<H4><A NAME="Heading21"></A><FONT COLOR="#000077">Handling Events Directly</FONT></H4>
<P>If you look at the bottom of the <TT>EVENTS2</TT> form, you see that there is
a
special event that tracks the position of the mouse. The EVENTS2 program tracks
the mouse movements in two different ways because I wanted to show you that you can
get information about the mouse either by responding to <TT>OnMouseMove</TT> events
or
by directly tracking <TT>WM_MOUSEMOVE</TT> messages.</P>
<P>Here is how you declare a function that is going to directly capture a message:</P>
<PRE><FONT COLOR="#0066FF">class TForm1 : public TForm
{
__published:
... // Declarations omitted
private:
MESSAGE void MyMouseMove(TWMMouse &Message);
public:
virtual __fastcall TForm1(TComponent* Owner);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_MOUSEMOVE, TWMMouse, MyMouseMove);
END_MESSAGE_MAP(TForm);
};
</FONT></PRE>
<P>The
declaration shown here tells BCB that you want to respond directly when the
operating system informs your program that the mouse has moved. In other words, you
don't want the BCB VCL to trap the message first and then pass it on to you in an
<TT>OnMouseMove</TT> event. Instead, you just want the message sent straight to you
by the operating system, as if you were working with one of the Windows API programs
shown earlier in the book. In short, you're telling the VCL: "Yes, I know you
can make this task very simple and can automate nearly the entire process by using
visual tools. That's nice of you, but right now I want to get the real event itself.
I have some reason of my own for wanting to get very close to the metal. As a
result,
I'm going to grab the message before you ever get a chance to look at it!"</P>
<P>Here's the code for the <TT>MyMouseMove</TT> function:</P>
<PRE><FONT COLOR="#0066FF">void TForm1::MyMouseMove(TWMMouse &Message)
{
TForm::Dispatch(&Message);
LSpecialMouse->Caption = "X " + IntToStr(Message.XPos) +
" Y " + IntToStr(Message.YPos);
}
</FONT></PRE>
<P>You can see that the code begins by calling the
<TT>Dispatch</TT> method inherited
from <TT>TObject</TT>. If you didn't make this call, the program would still run,
but the <TT>OnMouseMove</TT> event would never be sent to the <TT>FormMouseMove</TT>
function. It isn't an error if you don't pass the
message back to BCB. You can either
keep the message for yourself or pass it on, as you prefer.</P>
<P>If you omit the call to <TT>Dispatch</TT> from the <TT>MySpecialMouse</TT> function,
the <TT>FormMouseMove</TT> method in the EVENTS2 program is no
longer called. In
other words, you are directly trapping <TT>WM_MOUSEMOVE</TT> messages and not passing
them on to the VCL. As a result, the VCL does not know that the event occurred, and
<TT>FormMouseMove</TT> is not called.</P>
<P>The explanation in
the last paragraph might not be easy to grasp unless you actually
experiment with the EVENTS2 program. You should run the program once with the default
version of the <TT>MyMouseMove</TT> method, and once with the call to <TT>Inherited</TT>
commented
out:</P>
<PRE><FONT COLOR="#0066FF">void TForm1::MyMouseMove(TWMMouse &Message)
{
// TForm::Dispatch(&Message);
LSpecialMouse->Caption = "X " + IntToStr(Message.XPos) +
" Y " +
IntToStr(Message.YPos);
}
</FONT></PRE>
<P>Notice that when you run the program this way, the <TT>OnMouseMove</TT> message
at the top of the form is left blank.</P>
<P>If you look at the header for the <TT>MyMouseMove</TT> function, you can see that
it is passed a parameter of type <TT>TWMMouse</TT>. As you recall, the <TT>TWMMouse</TT>
record, found in <TT>MESSAGES.PAS</TT>, looks like this:</P>
<PRE><FONT COLOR="#0066FF">struct TWMMouse
{
unsigned int Msg;
long Keys;
union
{
struct
{
Windows::TSmallPoint Pos;
long Result;
};
struct
{
short XPos;
short YPos;
};
};
};
</FONT></PRE>
<P>If you break out both of the options shown in this variant record, you can further
simplify this record by writing</P>
<PRE><FONT COLOR="#0066FF">struct TWMMouse
{
unsigned int Msg;
long Keys;
Windows::TSmallPoint Pos;
long Result;
};
</FONT></PRE>
<P>or</P>
<PRE><FONT COLOR="#0066FF">struct TWMMouse
{
unsigned
int Msg;
long Keys;
short XPos;
short YPos;
};
</FONT></PRE>
<P>For most users, one of these two views will be the most useful way to picture
the record.</P>
<P>The same information is present in a <TT>TWMMouse</TT> record that you would
find
if you responded to an <TT>OnMouseMove</TT> or <TT>OnMouseDown</TT> event. If appropriate,
you can find out the row and column where the mouse is located, what key is pressed,
and what state the Shift, Alt, and Ctrl keys are in. To pursue this
matter further,
you should look up <TT>WM_MOUSEMOVE</TT> and <TT>WM_MOUSEDOWN</TT> messages in the
online help.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -