Combo box API trickery

F

Frank Rizzo

I have a Combo Box, with DropDownStyle=DropDownList. I have a
requirement that a user not be able to click certain items in the
dropdown, e.g. the dropped part of the combo box should act like nothing
happened.

To that end, I inherited the from IMessageFilter interface and
implemented PreFilterMessage method to look at messages. The plan was
simple:

1. check to make sure that the message has the same Hwnd as that of the
combo box

2. Catch the WM_LBUTTONDOWN event and if the mouse if over the
forbidden item, return true (e.g. ignore the event).

Except that I ran into an unexpected problem. The dropped down portion
of the combobox has a different Hwnd.

The documentation (
http://msdn.microsoft.com/library/d...oxreference/comboboxmessages/cbn_dropdown.asp
) states that a message CBN_DROPDOWN is sent down with the WM_COMMAND.

For whatever reason, this data does not come down via the
IMessageFilter.PreFilterMessage method. It does come down via the
overridden WndProc method.

However, the WndProc method does not seem to catch the WM_LBUTTONDOWN
event for the dropped portion of the combobox. The
IMessageFilter.PreFilterMessage does catch that message, but only if I
don't override the Form's WndProc method.

So I am caught between a rock and a hard place. Why do these methods
catch only selected messages? Am I missing something simple here?

Thanks.
 
M

Mattias Sjögren

Frank,
I have a Combo Box, with DropDownStyle=DropDownList. I have a
requirement that a user not be able to click certain items in the
dropdown, e.g. the dropped part of the combo box should act like nothing
happened.

Oh great, controls that don't behave the way you're used to. I'm sure
your user's will love that.

The documentation (
http://msdn.microsoft.com/library/d...oxreference/comboboxmessages/cbn_dropdown.asp
) states that a message CBN_DROPDOWN is sent down with the WM_COMMAND.

Right, and that maps to the managed DropDown and DropDownClosed
events.

So I am caught between a rock and a hard place. Why do these methods
catch only selected messages? Am I missing something simple here?

Only a certain subset of messages are routed through message filters.
Unfortunately I can never remember exactly which messages are.

You don't get mouse messages for the dropdown list in your form's
WndProc because the list isn't a child control of the form (it's a
toplevel window created directly on the desktop IIRC).

I guess you could get the list hwnd with GetComboBoxInfo and then
subclass it with the NativeWindow class.

But eating WM_MOUSEDOWN will only get you half way. You also need to
handle the various ways to select the item with the keyboard.


Mattias
 
L

Linda Liu [MSFT]

Hi Frank,

Based on my understanding, you'd like to prevent the user from selecting
certain items in a ComboBox control. If I'm off base, please feel free to
let me know.

I think your basic thinking of solving this problem is right. We could
implement IMessageFilter interface and add the implementation class to the
application's message pump to filter out a message.
The dropped down portion of the combobox has a different Hwnd.

Yes, you're right. The dropped down portion of a ComboBox control is
separate window, which has a different handle from the ComboBox control.
The documentation (
http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/shellcc/platform/commctls/comboboxes/comboboxreference/comboboxmessages/c
bn_dropdown.asp ) states that a message CBN_DROPDOWN is sent down with the
WM_COMMAND.
For whatever reason, this data does not come down via the
IMessageFilter.PreFilterMessage method. It does come down via the
overridden WndProc method.

Since the drop down portion of a ComboBox control is a separate window, the
CBN_DROPDOWN notification won't go through IMessageFilter.PreFilterMessage
method. Actually, .NET class ComboBox has exposed the DropDown event which
is a wrapper of the CBN_DROPDOWN notification.

What's more, I don't think we need to catch the CBN_DROPDOWN notification
in this case.

I have tried to implement the function you want. The following is the
sample code. The sample prevents the user from selecting the item whose
index is 1 by clicking it or pressing up/down key.

public partial class Form1 : Form
{
IMessageFilter myfilter = new MessageFilter();
private int previousSelectIndex = -1;

public Form1()
{
InitializeComponent();

Application.AddMessageFilter(myfilter);
this.Load +=new EventHandler(Form1_Load);
this.comboBox1.DropDownClosed += new
EventHandler(comboBox1_DropDownClosed);
this.comboBox1.SelectedIndexChanged += new
EventHandler(comboBox1_SelectedIndexChanged);
}

void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.comboBox1.SelectedIndex != 1)
{
this.previousSelectIndex = this.comboBox1.SelectedIndex;
}
else
{
this.comboBox1.SelectedIndex = this.previousSelectIndex;
}
}

void comboBox1_DropDownClosed(object sender, EventArgs e)
{
if (this.comboBox1.SelectedIndex == 1)
{
this.comboBox1.SelectedIndex = this.previousSelectIndex ;
}
}

private void Form1_Load(object sender, EventArgs e)
{
(myfilter as MessageFilter).RelatdCombobox = this.comboBox1;
}
}

class MessageFilter : IMessageFilter
{
private ComboBox m_combobox;
public ComboBox RelatdCombobox
{
get { return m_combobox; }
set { m_combobox = value; }
}

#region IMessageFilter Members

private int WM_LBUTTONDOWN = 0x0201;
private int WM_LBUTTONDBLCLK = 0x0203;

bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN || m.Msg == WM_LBUTTONDBLCLK)
{
if (m_combobox.SelectedIndex == 1 && m_combobox.DroppedDown)
{
return true;
}
else
{
return false;
}
}
return false;
}

#endregion
}

Hope this helps.
If my sample code is not what you want, please feel free to let me know.


Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
B

Ben Newsam

You don't get mouse messages for the dropdown list in your form's
WndProc because the list isn't a child control of the form (it's a
toplevel window created directly on the desktop IIRC).

Yes, and one of the reason for this is so that if the list is required
to drop down over the edge of the containing window or form, it can do
so with no worries about being clipped.
 
L

Linda Liu [MSFT]

Hi Frank,

How about the problem now?

If the problem has not been resolved, please feel free to let me know.

Thank you for using our MSDN Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
F

Frank Rizzo

Linda said:
Hi Frank,

How about the problem now?

If the problem has not been resolved, please feel free to let me know.


Linda, thank you very much - sorry it took so long to respond. The code
did the trick. I focused too much on the Windows API, while missing the
bigger picture.

Again, thank you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top