Interuptable listbox
Fredrik Gunne -- fge@ada.agema.se
Thursday, January 04, 1996
Consider this hypothetical situation: A dialog with one "Fill Listbox"-button and
one user-drawn listbox. When the user presses the button, the
listbox is filled with filenames in the current directory (using
CListBox::Dir).
In CMyListBox::DrawItem the kind of the file is determined, and if a BMP,
the BMP is drawn in the listbox.
This works fine.
Now, suppose the user presses the button again. (Perhaps the files in the current
directory has changed) Well, he just have to sit back and wait until all the files
in the visible part of the listbox have been processed (i.e. painted). With a
not-so-powerful PC, this may take some time. The reason is simply that the the
LBUTTON-messages that go to the button won't be processed until the listbox is
finished.
To solve this (in my case, which is a much more complicated dialog) I've done the
following:
In CMyListBox::DrawItem, I save the DRAWITEMSTRUCT in an array
(which takes almost no time)
In CMyListBox::OnPaint, I empty the array, calls CListBox::OnPaint and
start a timer (Yuck!)
In CMyListBox::OnTimer a take the first entry in the array an process it. Then
I delete that entry.
What I have achieved is that the DrawItem-messages that stops the LBUTTON-messages
are processed rapidly. Then, when a timer-message comes along, I do the job.
Since both WM_TIMER and WM_LBUTTON... messages are queued, they have equal chance
of being processed.
It works, but PLEASE tell me there's a better way to do this! For instance, what
happens if I can't get a timer (there are only 5 in Windows 3.x, aren't there?).
Well, I have to keep the old code to cover that case.
Any ideas?
Fredrik
David W. Gillett -- DGILLETT@expertedge.com
Friday, January 05, 1996
> What I have achieved is that the DrawItem-messages that stops the
> LBUTTON-messages are processed rapidly. Then, when a timer-message
> comes along, I do the job. Since both WM_TIMER and WM_LBUTTON...
> messages are queued, they have equal chance of being processed.
Actually, WM_TIMER messages are not -- they're like WM_PAINT
messages in that you will not receive one until your message queue is
empty, and there can never be more than one pending to you.
> It works, but PLEASE tell me there's a better way to do this! For
> instance, what happens if I can't get a timer (there are only 5 in
> Windows 3.x, aren't there?).
16, actually. 5 is the number of cached DCs.
Instead of using a timer to send yourself a message (it's a
WM_TIMER, but all you really need is "WM_OK_TO_DO_NEXT_ITEM"), why
not just, at the end of your "draw one item" code, use PostMessage to
queue a message to yourself to draw the next, if there is one. Any
messages that have accumulated in the queue while you were drawing
the item will get dispatched first, but as soon as they clear the
queue you'll get it without waiting for a timer.
Another possibility -- the canonical one -- is to insert a
PeekMessage loop in your code....
Dave
David Stidolph -- stidolph@magnet.com
Friday, January 05, 1996
[Mini-digest: 3 responses]
It sounds like what you want is to stop the list box from being drawn =
until you are finished making changes, then redraw the new listbox. Is =
that correct? If it is, then you want to use the following CWnd call =
(remember the CListBox is derived from CWnd and thus has this function):
void SetRedraw( BOOL bRedraw =3D TRUE );
=20
Parameters
bRedraw Specifies the state of the redraw flag. If this parameter is =
TRUE, the redraw flag is set; if FALSE, the flag is cleared.
Remarks
An application calls SetRedraw to allow changes to be redrawn or to =
prevent changes from being redrawn.=20
This member function sets or clears the redraw flag. While the redraw =
flag is cleared, the contents will not be updated after each change and =
will not be repainted until the redraw flag is set. For example, an =
application that needs to add several items to a list box can clear the =
redraw flag, add the items, and then set the redraw flag. Finally, the =
application can call the Invalidate or InvalidateRect member function to =
cause the list box to be repainted.
I have used this function for exactly this reason -- I was creating a =
tree control a couple of years ago (before Windows 95 and the tree =
control) and I had to insert a lot of items (when a folder was opened). =
I called SetRedraw(FALSE) at the beginning of the insertion and =
SetRedraw(TRUE) after I was done.
Good luck.
-----From: bibhas@hermes (Bibhas Bhattacharya)
> Now, suppose the user presses the button again. (Perhaps the files in the current
> directory has changed) Well, he just have to sit back and wait until all the files
> in the visible part of the listbox have been processed (i.e. painted). With a
> not-so-powerful PC, this may take some time. The reason is simply that the the
> LBUTTON-messages that go to the button won't be processed until the listbox is
> finished.
Try loading the BMP's one after another from the application idle function.
Bibhas.
-----From: Tim Irwin
You can implement a message processing function such as the following, which
will allow the system to check the messages in the queue.
BOOL CClassWithLongProcess::PassMessages(CDialog *dlg)
{
MSG msg;
BOOL retVal;
while (retVal = PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
{
if (!IsWindow(dlg->m_hWnd) || !dlg->IsDialogMessage(&msg))
{ // if the dialog didn't handle the message, pass it on through the
system
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return retVal;
}
Note: there is another function similar to PeekMessage called GetMessage
which waits for a message to appear in the queue before returning, this can
really slow down the processing unless, say, the mouse is moving (i.e. "For
best performance, move mouse vigorously.").
Hope this helps.
Tim Irwin
timi@interlinq.com
| Вернуться в корень Архива
|