2008 November 13th Thursday (十一月 十三日 木曜日)

   The WM_TIMER messages are similar to WM_PAINT messages in another respect. Windows does not keep loading up the message queue with multiple
WM_TIMER messages. Instead, Windows combines multiple WM_TIMER messages in the message queue into a single message. Therefore, the application
won't get a bunch of them at once, although it might get two WM_TIMER messages in quick succession. An application cannot determine the number
of "missing" WM_TIMER messages that result from this process.

  If you need a timer for the entire duration of your program, you'll probably call SetTimer from the WinMain function or while processing the
WM_CREATE message, and KillTimer on exiting WinMain or in response to a WM_DESTROY message. You can use a timer in one of three ways, depending
on the arguments to the SetTimer call.

Method One

  This method, the easiest, causes Windows to send WM_TIMER messages to the normal window procedure of the application. The SetTimer call looks
like this:

SetTimer (hwnd, 1, uiMsecInterval, NULL) ;

  The first argument is a handle to the window whose window procedure will receive the WM_TIMER messages. The second argument is the timer ID,
which should be a nonzero number. I have arbitrarily set it to 1 in this example. The third argument is a 32-bit unsigned integer that specifies
an interval in milliseconds. A value of 60,000 will deliver a WM_TIMER message once a minute.

  You can stop the WM_TIMER messages at any time (even while processing a WM_TIMER message) by calling

KillTimer (hwnd, 1) ;

  The second argument is the same timer ID used in the SetTimer call. It's considered good form to kill any active timers in response to a WM_DESTROY
message before your program terminates.

  When your window procedure receives a WM_TIMER message, wParam is equal to the timer ID (which in the above case is simply 1) and lParam is 0.
If you need to set more than one timer, use a different timer ID for each. The value of wParam will differentiate the WM_TIMER message passed to
your window procedure.

Method Two

  The first method for setting the timer causes WM_TIMER messages to be sent to the normal window procedure. With this second method, you can direct
Windows to send the timer messages to another function within your program.

  The function that receives these timer messages is termed a "call-back" function. This is a function in your program that is called from Windows.
You tell Windows the address of this function, and Windows later calls the function. This should sound familiar, because a program's window procedure
is really just a type of call-back function. You tell Windows the address of the window procedure when registering the window class, and then Windows
calls the function when sending messages to the program.

  SetTimer is not the only Windows function that uses a call-back. The CreateDialog and DialogBox functions (discussed in Chapter 11) use call-back
functions to process messages in a dialog box; several Windows functions (EnumChildWindow, EnumFonts, EnumObjects, EnumProps, and EnumWindow) pass
enumerated information to call-back functions; and several less commonly used functions (GrayString, LineDDA, and SetWindowHookEx) also require call-back
functions.

  Like a window procedure, a call-back function must be defined as CALLBACK because it is called by Windows from outside the code space of the program.
The parameters to the call-back function and the value returned from the call-back function depend on the purpose of the function. In the case of the
call-back function associated with the timer, the parameters are actually the same as the parameters to a window procedure although they are defined
differently. However, the timer call-back function does not return a value to Windows.

  Let's name the call-back function TimerProc. (You can choose any name that doesn't conflict with something else.) This function will process only
WM_TIMER messages:


VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
     [process WM_TIMER messages]
}

  The hwnd parameter to TimerProc is the handle to the window specified when you call SetTimer. Windows will send only WM_TIMER messages to TimerProc,
so the message parameter will always equal WM_TIMER. The iTimerID value is the timer ID, and dwTimer is a value compatible with the return value from the
GetTickCount function. This is the number of milliseconds that has elapsed since Windows was started.

  As we saw in BEEPER1, the first method for setting a timer requires a SetTimer call that looks like this:

SetTimer (hwnd, iTimerID, iMsecInterval, NULL) ;

  When you use a call-back function to process WM_TIMER messages, the fourth argument to SetTimer is instead the address of the call-back function,
like so:

SetTimer (hwnd, iTimerID, iMsecInterval, TimerProc) ;


Method Three

  The third method of setting the timer is similar to the second method, except that the hwnd parameter to SetTimer is set to NULL and the second
parameter (normally the timer ID) is ignored. Instead, the function returns a timer ID:

iTimerID = SetTimer (NULL, 0, wMsecInterval, TimerProc) ;

  The iTimerID returned from SetTimer will be 0 in the rare event that no timer is available.

  The first parameter to KillTimer (usually the window handle) must also be NULL. The timer ID must be the value returned from SetTimer:

KillTimer (NULL, iTimerID) ;

  The hwnd parameter passed to the TimerProc timer function will also be NULL. This method for setting a timer is rarely used. It might come in handy
if you do a lot of SetTimer calls at different times in your program and don't want to keep track of which timer IDs you've already used.

Buttons and Input Focus

  Windows switches the input focus from one window (such as a parent) to another (such as a child window control), it first sends a WM_KILLFOCUS
message to the window losing the input focus. The wParam parameter is the handle of the window that is to receive the input focus. Windows then
sends a WM_SETFOCUS message to the window receiving the input focus, with wParam specifying the handle of the window losing the input focus.
(In both cases, wParam might be NULL, which indicates that no window has or is receiving the input focus.)

  A parent window can prevent a child window control from getting the input focus by processing WM_KILLFOCUS messages. Assume that the array
hwndChild contains the window handles of all child windows. (These were saved in the array during the CreateWindow calls that created the windows.)
NUM is the number of child windows.

case WM_KILLFOCUS :

     for (i = 0 ; i < NUM ; i++)
          if (hwndChild [i] == (HWND) wParam)
          {
               SetFocus (hwnd) ;
               break ;
          }
     return 0 ;

  In this code, when the parent window detects that it's losing the input focus to one of its child window controls, it calls SetFocus to restore
the input focus to itself.

  Here's a simpler (but less obvious) way of doing it:

case WM_KILLFOCUS :
     if (hwnd == GetParent ((HWND) wParam))
          SetFocus (hwnd) ;
     return 0 ;

  When you display text using TextOut, Windows uses values defined in the device context for the text background color (which erases the background
behind the text) and the text color. The default values are white (background) and black (text), regardless of either the system colors or the
hbrBackground field of the window class structure. So you need to use SetTextColor and SetBkColor to change your text and text background colors
to the system colors. You do this after you obtain the handle to a device context:

SetBkColor (hdc, GetSysColor (COLOR_BTNFACE)) ;
SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;

  Now the client-area background, text background, and text color are all consistent with the button colors. However, if the user changes the system
colors while your program is running, you'll want to change the text background color and text color. You can do this using the following code:

case WM_SYSCOLORCHANGE:
     InvalidateRect (hwnd, NULL, TRUE) ;
     break ;

The WM_CTLCOLORBTN Message

  This is a message that button controls send to the parent window procedure when the child window is about to paint its client area. The parent
window can use this opportunity to alter the colors that the child window procedure will use for painting. (In 16-bit versions of Windows, a message
named WM_CTLCOLOR was used for all controls. This has been replaced with separate messages for each type of standard control.)

  When the parent window procedure receives a WM_CTLCOLORBTN message, the wParam message parameter is the handle to the button's device context and
lParam is the button's window handle. When the parent window procedure gets this message, the button control has already obtained its device context.
When processing a WM_CTLCOLORBTN message in your window procedure, you:

  * Optionally set a text color using SetTextColor

  * Optionally set a text background color using SetBkColor

  * Return a brush handle to the child window
 
  In theory, the child window uses the brush for coloring a background. It is your responsibility to destroy the brush when it is no longer needed.
Here's the problem with WM_CTLCOLORBTN: Only the push buttons and owner-draw buttons send WM_CTLCOLORBTN to their parent windows, and only owner-draw
buttons respond to the parent window processing of the message using the brush for coloring the background. This is fairly useless because the parent
window is responsible for drawing owner-draw buttons anyway. Later on in this chapter, we'll examine cases in which messages similar to WM_CTLCOLORBTN
but applying to other types of controls are more useful.

The Listbox Class

Selecting and Extracting Entries

  The SendMessage calls that carry out the tasks shown below usually return a value. If an error occurs, this value is set to LB_ERR (defined as -1).
After you've put some items into a list box, you can find out how many items are in the list box:

iCount = SendMessage (hwndList, LB_GETCOUNT, 0, 0) ;

  Some of the other calls are different for single-selection and multiple-selection list boxes. Let's first look at single-selection list boxes.
Normally, you'll let a user select from a list box. But if you want to highlight a default selection, you can use

SendMessage (hwndList, LB_SETCURSEL, iIndex, 0) ;

  Setting iParam to -1 in this call deselects all items.

  You can also select an item based on its initial characters:

iIndex = SendMessage (hwndList, LB_SELECTSTRING, iIndex, (LPARAM) szSearchString) ;

  The iIndex given as the iParam parameter to the SendMessage call is the index following which the search begins for an item with initial characters
that match szSearchString. An iIndex value of -1 starts the search from the top. SendMessage returns the index of the selected item, or LB_ERR if no
initial characters match szSearchString.

  When you get a WM_COMMAND message from the list box (or at any other time), you can determine the index of the current selection using LB_GETCURSEL:


iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0) ;

  The iIndex value returned from the call is LB_ERR if no item is selected.

  You can determine the length of any string in the list box:


iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0) ;

  and copy the item into the text buffer:

iLength = SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) szBuffer) ;

  In both cases, the iLength value returned from the call is the length of the string. The szBuffer array must be large enough for the length of the string
and a terminating NULL. You may want to use LB_GETTEXTLEN to first allocate some memory to hold the string.

  For a multiple-selection list box, you cannot use LB_SETCURSEL, LB_GETCURSEL, or LB_SELECTSTRING. Instead, you use LB_SETSEL to set the selection state of
a particular item without affecting other items that might also be selected:

SendMessage (hwndList, LB_SETSEL, wParam, iIndex) ;

  The wParam parameter is nonzero to select and highlight the item and 0 to deselect it. If the lParam parameter is -1, all items are either selected or
deselected. You can also determine the selection state of a particular item using

iSelect = SendMessage (hwndList, LB_GETSEL, iIndex, 0) ;

where iSelect is set to nonzero if the item indexed by iIndex is selected and 0 if it is not.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值