Overriding WndProc


The Windows operating system sends all sorts of messages to applications that tell them about changes in the Windows environment. Messages tell forms to draw, move, resize, hide, minimize, close, respond to changes in the Windows environment, and do just about everything else related to Windows.

All Windows applications have a subroutine tucked away somewhere that responds to those messages. That routine is traditionally called a WindowProc. A Visual Basic .NET form processes these messages in a routine named WndProc. You can override that routine to take special actions when the form receives certain messages.

For example, the following code shows how a program can ensure that a form always keeps the same aspect ration (ratio of height to width). The program overrides the form’s WndProc subroutine and looks for the WM_SIZING message. This message receives as parameters a flag indicating the edge that the user is dragging to resize the form and a Rect structure giving the form’s new size and location.

This code begins by defining the Rect structure. It then declares the overridden WndProc routine. That routine defines some constants and a static variable to hold the form’s original aspect ratio.

Next, WndProc determines the type of message it is processing. If the message is WM_SIZING, then the routine uses the Marshal.PtrToStructure function to copy the m.LParam parameter into a Rect structure. It uses this structure to calculate the form’s new width and height, and its aspect ratio.

If this is the first time the WndProc has executed, the static value fixed_aspect_ratio is zero. If the code sees that the value is zero, it saves the form’s current aspect ratio in the variable fixed_aspect_ratio.

Next WndProc decides whether the form’s aspect ratio is different from its original value. If the ratio has changed, then it determines which dimension (width or height) it should save. If the user is dragging one of the form’s corners, the routine saves whichever of the width and height is bigger, and calculates a value for the other dimension that gives the desired aspect ratio.

If the user is dragging one of the form’s sides, the program keeps the new width and calculates an appropriate height. Similarly, if the user is dragging the form’s top or bottom, the program keeps the height and calculates an appropriate width.

Next, the program determines whether it should move the form’s left or right side and its top or bottom edge. The program moves whichever edges the user is dragging. For example, if the user is dragging the form’s lower-left corner, the program adjusts the form’s left and bottom values so the upper-right corner remains stationary.

WndProc then calls Marshal.StructureToPtr to copy the Rect structure back into the m.LParam parameter.

Finally, WndProc calls MyBase.WndProc to make the parent class’s original version of WndProc process the message. If the code changed the values in the Rect structure, the parent’s WndProc uses the new values to resize the form.

Calling the parent class’s WndProc routine is extremely important. If the program doesn’t do this for every message that it does not completely handle itself, the message will not be processed. In that case, the window will not handle all of its Windows messages. It may not repaint, move, load, unload, display menus, and perform all sorts of other Windows tasks correctly.

  Imports System.Runtime.InteropServices Public Class Form1     Public Structure Rect         Public left As Integer         Public top As Integer         Public right As Integer         Public bottom As Integer     End Structure     Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)         Const WM_SIZING As Long = &H214         Const WMSZ_LEFT As Integer = 1         Const WMSZ_RIGHT As Integer = 2         Const WMSZ_TOP As Integer = 3         Const WMSZ_TOPLEFT As Integer = 4         Const WMSZ_TOPRIGHT As Integer = 5         Const WMSZ_BOTTOM As Integer = 6         Const WMSZ_BOTTOMLEFT As Integer = 7 Const WMSZ_BOTTOMRIGHT As Integer = 8 Static fixed_aspect_ratio As Double = 0 Dim new_aspect_ratio As Double If m.Msg = WM_SIZING And m.HWnd.Equals(Me.Handle) Then     ' Turn the message's lParam into a Rect     Dim r As Rect     r = DirectCast( _         Marshal.PtrToStructure(m.LParam, GetType(Rect)), _         Rect)     ' Get the current dimensions.     Dim wid As Double = r.right - r.left     Dim hgt As Double = r.bottom - r.top     ' Get the new aspect ratio.     new_aspect_ratio = hgt / wid     ' The first time, save the form's aspect     If fixed_aspect_ratio = 0 Then         fixed_aspect_ratio = new_aspect_ratio     End If     ' See if the aspect ratio is changing.     If fixed_aspect_ratio <> new_aspect_ratio Then         ' To decide which dimension we should preserve,         ' see what border the user is dragging.         If m.WParam.ToInt32 = WMSZ_TOPLEFT Or _            m.WParam.ToInt32 = WMSZ_TOPRIGHT Or _            m.WParam.ToInt32 = WMSZ_BOTTOMLEFT Or _            m.WParam.ToInt32 = WMSZ_BOTTOMRIGHT _         Then             ' The user is dragging a corner.              ' Preserve the bigger dimension.             If new_aspect_ratio > fixed_aspect_ratio Then                ' It's too tall and thin.                 wid = hgt / fixed_aspect_ratio             Else                ' It's too short and wide.                 hgt = wid * fixed_aspect_ratio             End If         ElseIf m.WParam.ToInt32 = WMSZ_LEFT Or _                m.WParam.ToInt32 = WMSZ_RIGHT _         Then             ' The user is dragging a side.              ' Preserve the width.             hgt = wid * fixed_aspect_ratio         ElseIf m.WParam.ToInt32 = WMSZ_TOP Or _                m.WParam.ToInt32 = WMSZ_BOTTOM _         Then             ' The user is dragging the top or bottom.              ' Preserve the height.             wid = hgt / fixed_aspect_ratio         End If                ' Figure out whether to reset the top/bottom                ' and left/right.                ' See if the user is dragging the top edge.                If m.WParam.ToInt32 = WMSZ_TOP Or _                   m.WParam.ToInt32 = WMSZ_TOPLEFT Or _                   m.WParam.ToInt32 = WMSZ_TOPRIGHT _                Then                    ' Reset the top.                    r.top = r.bottom - CInt(hgt)                Else                    ' Reset the bottom.                    r.bottom = r.top + CInt(hgt)                End If                ' See if the user is dragging the left edge.                If m.WParam.ToInt32 = WMSZ_LEFT Or _                   m.WParam.ToInt32 = WMSZ_TOPLEFT Or _                   m.WParam.ToInt32 = WMSZ_BOTTOMLEFT _                Then                    ' Reset the left.                    r.left = r.right - CInt(wid)                Else                    ' Reset the right.                    r.right = r.left + CInt(wid)                End If                ' Update the Message object's                Marshal.StructureToPtr(r, m.LParam, True)             End If         End If         MyBase.WndProc(m)     End Sub End Class  

In Visual Basic 6 and earlier versions, a program could install a custom WindowProc to perform roughly the same operations. This process was called subclassing, an unfortunate choice of name given that object-oriented languages use the term to mean “deriving one class from another” as Visual Basic does with its Inherits statement.

Overriding WndProc in Visual Basic .NET is much easier and safer than subclassing in Visual Basic 6. As you can see from the example, however, it still requires some tricks to convert the IntPtr stored in m.LParam to and from the appropriate structure. You must also figure out what messages to intercept, what m.LParam and m.WParam parameters they take, and what you can do to safely affect them.

One way to learn about messages is to insert the following WndProc and then perform the action that you want to study (resizing the form, in this example):

  Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)     Debug.Print(m.ToString)     MyBase.WndProc(m) End Sub 

The following statement shows the result for the WM_SIZING message sent to the form while the user resizes it. It at least shows the message name (WM_SIZING) and its numeric value (hexadecimal 0x214).

  msg=0x214 (WM_SIZING) hwnd=0x30b8c wparam=0x2 lparam=0x590e29c result=0x0 

Searching for the message name on the Microsoft web site and on other programming sites usually gives you the other information you need to know (such as what m.WParam and m.LParam mean).

Note also that the Form class inherits the WndProc subroutine from the Control class, so all other Windows forms controls inherit it as well. That means you can override their WndProc routines to change their behaviors.

For example, the following code shows how the NoCtxMnuTextBox class works. This control is derived from the TextBox control. Its WndProc subroutine checks for WM_CONTEXTMENU messages and calls the base class’s WndProc for all other messages. By failing to process the WM_CONTEXTMENU message, the control prevents itself from displaying the TextBox control’s normal Copy/Cut/Paste context menu when you right-click on it.

  Public Class NoCtxMnuTextBox     Inherits System.Windows.Forms.TextBox     Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)         Const WM_CONTEXTMENU As Integer = &H7B         If m.Msg <> WM_CONTEXTMENU Then             MyBase.WndProc(m)         End If     End Sub End Class  




Visual Basic 2005 with  .NET 3.0 Programmer's Reference
Visual Basic 2005 with .NET 3.0 Programmer's Reference
ISBN: 470137053
EAN: N/A
Year: 2007
Pages: 417

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net