There were several other minor issues that I had to solve and I've described them in the ColorPicker.vb
source file. There was one issue, however, that I'd like to discuss here in more detail.
When the drop-down color selector is displayed by calling the IWindowsFormsEditorService.DropDownControl
method, it is expected not to return only until after the user either selects a new color or she cancels the selection. The cancellation can be performed in a variety of ways: by pressing the Esc key, by clicking outside of the drop-down box, by pressing the Ctrl+Esc system key combination or by clicking the ColorPicker
button once again.
In other words, the DropDownControl
method implementation should block while dispatching windows messages caused by the user interaction with Windows (including the drop-down color selector).
Do you remember the old pal, DoEvents
?
This time, however, it is exposed as the DoEvents
method of the System.Windows.Forms.Application
class. Calling the method causes processing of all the Windows messages in the current thread's message queue. Here is a pseudocode for my first DropDownControl
implementation, which uses the DoEvents
method:
Namespace LaMarvin.Windows.Forms
...
Public Class ColorPicker
...
Private Class EditorService
Public Sub DropDownControl(ByVal control As Control) _
Implements IWindowsFormsEditorService.DropDownControl
' Display the drop-down color selector, which
' is hosted within a _DropDownHolder Form instance.
' Wait until the drop-down is closed.
Do While _DropDownHolder.Visible
Application.DoEvents()
Loop
...
End Sub
The code shows the drop-down UI and then it enters a loop calling Application.DoEvents
until the drop-down form is closed. This way, the DropDownControl
method blocks (ignoring the possible reentrancy issues for the moment) while Windows messages are still being dispatched.
It worked fine this way until I realized that when the drop-down color selector is displayed, the process hosting the ColorPicker
control eats 100% of the CPU.
Too bad!
Once again, I've turned to the .NET Reflector tool in order to see how the DropDownControl
method is implemented within the PropertyGrid
itself (which, obviously, doesn't eat 100% CPU while displaying the color selector).
Here is what I found out:
Namespace System.Windows.Forms.PropertyGridInternal
Private Class PropertyGridView
Private Class DropDownHolder
Public Sub DoModalLoop()
Do While MyBase.Visible
Application.DoEvents
UnsafeNativeMethods.MsgWaitForMultipleObjects( _
1, 0, 1, 250, 255)
Loop
End Sub
...
The same code as above plus the MsgWaitForMultipleObjects
function call. The function is a standard part of the Win32 API. Here is the function's prototype taken from the MSDN documentation (comments mine):
DWORD MsgWaitForMultipleObjects(
DWORD nCount, // number of handles pointed to by the
// pHandles argument
const HANDLE* pHandles, // pointer to an array of object
// handles whose signaled state is checked
BOOL bWaitAll, // all object handles should be
// signaled (TRUE) or any one of them (FALSE)
DWORD dwMilliseconds, // wait timeout
DWORD dwWakeMask // which input events (messages)
// should cause the function to return (in addition
// to signaling objects pointed to by pHandles)
);
The purpose of the function is to suspend the calling thread until one or all of the object handles become signaled, OR, until a message appears in the thread's input queue according to the dwWakeMask
value.
The weird thing is that .NET framework calls the function with the number of object handles equal to ONE, while the pointer to the array of handles is ZERO (Nothing
in VB nomenclature).
This way of calling the function is not documented (AFAIK). One can only guess that such a call is used to suspend the calling thread until a message has to be processed WITHOUT checking the signaled state of any object handle. Nevertheless, because the call is used within WinForms implementation itself, I find it quite safe to use it within your own applications. I've used it with ColorPicker
and the problem with the drop-down form consuming 100% CPU cycles disappeared.
You can download the ColorPicker
control project along with the accompanying demo project here.
The ColorPicker.vb
source code is listed on the next page. Enjoy!
Comments