Message Propagation

posted @ Wednesday, May 9, 2007 11:55 AM

 

Today I ran into an issue where multiple user controls within a scrollable container (Windows Forms) were not always scrolling properly within the container. When the outer user control (specific controls actually) within the container had focus, scrolling worked fine, however when other controls had focus, scrolling with the mouse wheel doesn't occur at all - time to start digging. I noticed this occurred with the TextBox class, and it behaves differently depending on the Multiline state of the control. This is actually a valid situation, we want the TextBox class to handle the WM_MOUSEWHEEL message when it's in multiline mode because we want it to be able to scroll through its own contents. The client requirements are that scroll occur within the container as well.

Using Spy++, I was able to monitor the messages that were being generated on a TextBox with the multiline state set to true. I was in fact receiving the WM_MOUSEWHEEL message, however it didn't seem to propagate, which is again a valid operation. The WM_MOUSEWHEEL semantics state that the message will continue to propagate up the parent chain until a window processes it. When the Multiline state is set to false, the WM_MOUSEWHEEL message is sent to the parent user control who can then send it to the scrollable container, thus causing the scroll effect to occur. The TextBox class appears to capture the WM_MOUSEWHEEL message when the multiline state is set to true and stops the propagation. I put the following together as a replacement which will cause the message to always be sent up, regardless of the Multiline state.

    public class TextBoxEx : System.Windows.Forms.TextBox
{
public TextBoxEx(){}
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=false)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const UInt32 WM_MOUSEWHEEL = 0x020A;
protected override void WndProc(ref Message m)
{    
if(m.Msg == WM_MOUSEWHEEL)
{
SendMessage(this.Parent.Handle, WM_MOUSEWHEEL, m.WParam, m.LParam);
}
base.WndProc (ref m);
}
}