Click here to Skip to main content
Click here to Skip to main content
Go to top

Subclassing TextBox Using Native Callbacks

, 3 Jan 2008
Rate this:
Please Sign up or sign in to vote.
Adding Click Event on a Textbox by Subclassing using Native Callbacks

Introduction

In the previous article, I discussed adding Click event on a TextBox which is applicable in Windows Forms. Unfortunately that same code will not work on smart device applications as .NET Compact Framework does not support that overridden onClick implementation of textbox. So we need to find a new workaround for it.

Background

Compact Framework, as its name suggests, has given limited but necessary functionality and limited support of events in comparison to .NET Framework to decrease its size as it has to be available on devices which have limited resources. At the same time, they have provided few classes which you can use to implement events which you need; In our case, Click Event on TextBox. These classes capture mouse Click Messages which are handled by the machine itself. Processes of capturing an event is when the mouse button is clicked, a message is generated which is passed to the device and in return the OS returns a handle to the object which has raised an event with few other parameters. There are different structures and methods defined for the available list of window messages in the provided classes which, using a handle of object you can implement event on Control. This method of implementing events on controls is called “SubClassing a Control Using Native Callbacks”.

Using the Code

I have used the following classes to create a smart device control library project:

  • WndProcHooker.cs
  • Win32.cs

You can find these classes here.

Code in classes is properly commented which gives a good understanding of what is going on in the methods. (Both classes are included in the sample project.)

The steps required to create the CF control library project are listed below:

  1. Open your Visual Studio 2005 IDE and Create New Project > In Visual C# > Smart device > Pocket PC 2003 > Select Control Library from available Templates. Name it as ClickableTextBox or whatever you like.
  2. Add a new *.cs class, name it as WndProcHooker and copy the contents of the downloaded WndProcHooker class into it.
  3. Add a new *.cs class, name it Win32 and copy the contents of the downloaded Win32 class into it.
  4. Add a new Component Class and name it as ClickableTextBox.
    In its source code, change the base class to TextBox. Remember to change the base class to TextBox in the designer.cs class too.
  5. Following is the code you need to write in the Component class:.

    public partial class ClickableTextBox : TextBox
    {
        public delegate void onClick(object sender, EventArgs e);
        public event onClick ClickableTextBox_Clicked;
    
        public ClickableTextBox()   
        {
            InitializeComponent();
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_LButtonDown_Handler),
                Win32.WM_LBUTTONDOWN);
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_LButtonUp_Handler),
                Win32.WM_LBUTTONUP); 
                
        }         
             
        #region OnClick
    
        /// <span class="code-SummaryComment"><summary></span>
        /// The method that gets called when a WM_NOTIFY message is received by the
        /// TextBox's parent.
        /// <span class="code-SummaryComment"></summary></span>
        /// <span class="code-SummaryComment"><param name="hwnd">The handle of the window that received the message</span>
        /// <span class="code-SummaryComment"></param></span>
        /// <span class="code-SummaryComment"><param name="msg">The message received</param></span>
        /// <span class="code-SummaryComment"><param name="wParam">The wParam arguments for the message</param></span>
        /// <span class="code-SummaryComment"><param name="lParam">The lParam arguments for the message</param></span>
        /// <span class="code-SummaryComment"><param name="handled">Set to true to indicate that </span>
        /// this message was handled<span class="code-SummaryComment"></param></span>
        /// <span class="code-SummaryComment"><returns>An appropriate return code for the message handled (see MSDN)</span>
        /// <span class="code-SummaryComment"></returns></span>
        int WM_Notify_Handler(
        IntPtr hwnd, uint msg, uint wParam, int lParam,
        ref bool handled)
        {
            Win32.NMHDR nmHdr = new Win32.NMHDR();
            System.Runtime.InteropServices.Marshal.PtrToStructure
                ((IntPtr)lParam, nmHdr);
            switch (nmHdr.code)
            {
                case Win32.WM_LBUTTONDOWN:
                case Win32.WM_LBUTTONUP:
                    // get the cursor coordinates on the client
                    Point msgPos = Win32.LParamToPoint((int)Win32.GetMessagePos());
                    msgPos = this.PointToClient(msgPos);
                    RaiseMouseClickEvent(MouseButtons.Left, msgPos);
                    break;
                default:
                    break;
            }
            return 0;
        }
        public void OnMouseClick(MouseEventArgs e)
        {
            this.OnClick(new EventArgs());
        }
    
        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);
        }
    
        /// <span class="code-SummaryComment"><summary></span>
        /// Raises the MouseClick event for the TextBox with the specified handle.
        /// <span class="code-SummaryComment"></summary></span>
        /// <span class="code-SummaryComment"><param name="hNode">The handle of the node for which the event is raised</span>
        /// <span class="code-SummaryComment"></param></span>
        /// <span class="code-SummaryComment"><param name="button">The [mouse] buttons that were pressed </span>
        /// to raise the event<span class="code-SummaryComment"></param></span>
        /// <span class="code-SummaryComment"><param name="coords">The [client] cursor coordinates </span>
        /// at the time of the event<span class="code-SummaryComment"></param></span>
        public void RaiseMouseClickEvent(MouseButtons button, Point coords)
        {
            MouseEventArgs e = new MouseEventArgs(button,
            1, coords.X, coords.Y, 0);
    
            OnMouseClick(e);
        }
    
        // The callback called when the window receives a WM_LBUTTONDOWN
        // message. We capture the mouse and draw the button in the "pushed"
        // state.
        // hwnd - The handle to the window that received the
        // message.
        // wParam - Indicates whether various virtual keys are
        // down.
        // lParam - The coordinates of the cursor.
        // handled - Set to true if we don't want to pass this
        // message on to the original window procedure.
        // Returns zero if we process this message.
        int WM_LButtonDown_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            ref bool handled)
        {
            // Start capturing the mouse input.
            this.Capture = true;
            // someone clicked on us so grab the focus
            this.Focus();
    
            // Fire the MouseDown event
    
            ClickableTextBox_Clicked(this, null);
    
            // We have handled this windows message and we don't want the
            // sub-classed window to do anything else.
            handled = true;
            return 0;
        }
        // The callback called when the window receives a WM_LBUTTONUP
        // message. We release capture on the mouse, draw the button in the
        // "un-pushed" state and fire the  OnMouseUp event if the cursor was
        // let go of inside our client area.
        // hwnd - The handle to the window that received the
        // message
        // wParam - Indicates whether various virtual keys are
        // down.
        // lParam - The coordinates of the cursor
        // handled - Set to true if we don't want to pass this
        // message
        // on to the original window procedure
        // Returns zero if we process this message.
        int WM_LButtonUp_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            ref bool handled)
        {
            this.Capture = false;
            // TODO : implement your login on mouse key up event
            handled = true;
            return 0;
        }
        #endregion
    } 

    Let’s examine the code in the ClickableTextBox class.

    1. In ClickableTextBox constructor, Mouse LEFT button up and Mouse LEFT Button Down events messages are hooked.
    2. WM_Notify_Handler is the method which gets called to notify when some event is raised by mouse. This method checks for Left mouse up and down events and calls win32 function which gets the cursor position and converts them into respective client points and raises MouseClickEvent which raises OnMouseClick and finally onClick event is fired on TextBox.
  6. Add a delegate with public access modifier which is something like this with its handler:

    public delegate void onClick(object sender, EventArgs e);
    
    public event onClick Clicked;  
  7. WM_LButtonDown_Handler is the handler for Mouse Left button Down which I have left unimplemented.
  8. The last function is WM_LButtonUp_Handler here. I have added handler ClickableTextBox_Clicked.
    That’s it. Compile the project. And you might get errors ! Smile | :)
    If you have used MSDN downloaded WndProcHooker and Win32 classes, they don’t have the necessary libraries added at the top. Add them in the respective classes and now you will not get errors (inshallah).
  9. Create a Test Pocket PC Application and add the ClickableTextBox.dll by right clicking on tools bar, choose Items and browse to the specified DLL. The control will be added as a drag and drop control.
  10. [optional] Add a reference of ClickableTextBox.dll if it is not added in project references by drag and drop.
  11. Drag ClickableTextBox control from the Toolbar and check its events list. A new event “Clicked” will be added to the list. Add event and test it with some alert message, and it is done.

Happy programming.

Points of Interest

This Click event is not a true Click event as it has something missing. I have raised ClickEvent on LButton up event so it will fire the Click event even if you pressed LButton inside Textbox and released outside Textbox [i.e. drag event]. So you need to keep the old cursor position and compare it with the coordinates of Left button UP event position. This is missing, so if anybody likes, s/he can add this functionality.

History

  • 3rd January, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

imran afzal
Software Developer Adaptive solutions Inc.
Pakistan Pakistan
BS(CS) From SZABIST (Pakistan)
I have been working as
"Web Application Developer"
with Adaptive Solutions Inc.
 

Comments and Discussions

 
GeneralMy vote of 3 PinmembereRRaTuM19-Jun-14 9:17 
GeneralMy vote of 5 Pinmembervirtualdj7-May-11 0:43 
GeneralThanks! Pinmemberf.vanvugt2-Dec-09 11:43 
GeneralSmall change PinmemberAchim Bohmann26-Mar-09 1:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 4 Jan 2008
Article Copyright 2008 by imran afzal
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid