TextBox OnClick

(Compact .Net 1.0)

TextBox does not have a OnClick EventHandler to use. So I created my own TextBox class and subclassed TextBox and I override OnClick. This compiles but when clicking on the TextBox the method never gets called.

Is there any way to get notified when the TextBox is clicked or selected I tried catching the OnGotFocus event, that works great except if the focus is already on the TextBox and the user click on the box again. I need something to capture this case and OnGotFOcus does not get fired.

Anu suggestions

Thanks



Answer this question

TextBox OnClick

  • James S. Jan

    C#
  • hashbrown

    I i tried it by my own, but it doesn't work. I don't know why. I get no errors or somthing else.
    Please help.

    The win32 i copied:

        /// <summary>
        /// Contains managed wrappers or implementations of Win32 structs, delegates,
        /// constants and PInvokes that are useful for this sample.
        ///
        /// See the documentation on MSDN for more information on the elements provided
        /// in this file.
        /// </summary>
        public sealed class Win32
        {
            /// <summary>
            /// A callback to a Win32 window procedure (wndproc)
            /// </summary>
            /// <param name="hwnd">The handle of the window receiving a message</param>
            /// <param name="msg">The message</param>
            /// <param name="wParam">The message's parameters (part 1)</param>
            /// <param name="lParam">The message's parameters (part 2)</param>
            /// <returns>A integer as described for the given message in MSDN</returns>
            public delegate int WndProc(IntPtr hwnd, uint msg, uint wParam, int lParam);

            public const uint WM_NOTIFY = 0x4E;
            public const uint WM_LBUTTONDOWN = 0x0201;
            public const uint WM_LBUTTONUP = 0x0202;

            //public enum WM
            //{
            //    LBUTTONDOWN = 0x0201,
            //    LBUTTONUP = 0x0202
            //}

            [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
            public class NMHDR
            {
                public IntPtr hwndFrom;
                public uint idFrom;
                public uint code;
            }



            /// <summary>
            /// Helper function to convert a Windows lParam into a Point
            /// </summary>
            /// <param name="lParam">The parameter to convert</param>
            /// <returns>A Point where X is the low 16 bits and Y is the
            /// high 16 bits of the value passed in</returns>
            public static Point LParamToPoint(int lParam)
            {
                uint ulParam = (uint)lParam;
                return new Point(
                    (int)(ulParam & 0x0000ffff),
                    (int)((ulParam & 0xffff0000) >> 16));
            }

    #if DESKTOP
        [DllImport("user32.dll")]
    #else
            [DllImport("coredll.dll")]
    #endif
            public extern static int DefWindowProc(
                IntPtr hwnd, uint msg, uint wParam, int lParam);

    #if DESKTOP
        [DllImport("user32.dll")]
    #else
            [DllImport("coredll.dll")]
    #endif
            public extern static IntPtr SetWindowLong(
                IntPtr hwnd, int nIndex, IntPtr dwNewLong);

            public const int GWL_WNDPROC = -4;

    #if DESKTOP
        [DllImport("user32.dll")]
    #else
            [DllImport("coredll.dll")]
    #endif
            public extern static int CallWindowProc(
                IntPtr lpPrevWndFunc, IntPtr hwnd, uint msg, uint wParam, int lParam);


            #if DESKTOP
        [DllImport("user32.dll")]
    #else
        [DllImport("coredll.dll")]
    #endif
        public extern static uint GetMessagePos();

    #if DESKTOP
        [DllImport("user32.dll")]
    #else
        [DllImport("coredll.dll")]
    #endif
        public extern static int SendMessage(
            IntPtr hwnd, uint msg, uint wParam);
        }


    the Hooker class i copied too:

    public class WndProcHooker
    {
    /// <summary>
    /// The callback used when a hooked window's message map contains the
    /// hooked message
    /// </summary>
    /// <param name="hwnd">The handle to the window for which the message
    /// was received</param>
    /// <param name="wParam">The message's parameters (part 1)</param>
    /// <param name="lParam">The message's parameters (part 2)</param>
    /// <param name="handled">The invoked function sets this to true if it
    /// handled the message. If the value is false when the callback
    /// returns, the next window procedure in the wndproc chain is
    /// called</param>
    /// <returns>A value specified for the given message in the MSDN
    /// documentation</returns>
    public delegate int WndProcCallback(
    IntPtr hwnd, uint msg, uint wParam, int lParam, ref bool handled);

    /// <summary>
    /// This is the global list of all the window procedures we have
    /// hooked. The key is an hwnd. The value is a HookedProcInformation
    /// object which contains a pointer to the old wndproc and a map of
    /// messages/callbacks for the window specified. Controls whose handles
    /// have been created go into this dictionary.
    /// </summary>
    private static Dictionary<IntPtr, HookedProcInformation> hwndDict =
    new Dictionary<IntPtr, HookedProcInformation>();

    /// <summary>
    /// See <see>hwndDict</see>. The key is a control and the value is a
    /// HookedProcInformation. Controls whose handles have not been created
    /// go into this dictionary. When the HandleCreated event for the
    /// control is fired the control is moved into <see>hwndDict</see>.
    /// </summary>
    private static Dictionary<Control, HookedProcInformation> ctlDict =
    new Dictionary<Control, HookedProcInformation>();

    /// <summary>
    /// Makes a connection between a message on a specified window handle
    /// and the callback to be called when that message is received. If the
    /// window was not previously hooked it is added to the global list of
    /// all the window procedures hooked.
    /// </summary>
    /// <param name="ctl">The control whose wndproc we are hooking</param>
    /// <param name="callback">The method to call when the specified
    /// message is received for the specified window</param>
    /// <param name="msg">The message we are hooking.</param>
    public static void HookWndProc(
    Control ctl, WndProcCallback callback, uint msg)
    {
    HookedProcInformation hpi = null;
    if (ctlDict.ContainsKey(ctl))
    hpi = ctlDict[ctl];
    else if (hwndDict.ContainsKey(ctl.Handle))
    hpi = hwndDict[ctl.Handle];
    if (hpi == null)
    {
    // We havne't seen this control before. Create a new
    // HookedProcInformation for it
    hpi = new HookedProcInformation(ctl,
    new Win32.WndProc(WndProcHooker.WindowProc));
    ctl.HandleCreated += new EventHandler(ctl_HandleCreated);
    ctl.HandleDestroyed += new EventHandler(ctl_HandleDestroyed);
    ctl.Disposed += new EventHandler(ctl_Disposed);

    // If the handle has already been created set the hook. If it
    // hasn't been created yet, the hook will get set in the
    // ctl_HandleCreated event handler
    if (ctl.Handle != IntPtr.Zero)
    hpi.SetHook();
    }

    // stick hpi into the correct dictionary
    if (ctl.Handle == IntPtr.Zero)
    ctlDict[ctl] = hpi;
    else
    hwndDict[ctl.Handle] = hpi;

    // add the message/callback into the message map
    hpi.messageMap[msg] = callback;
    }

    /// <summary>
    /// The event handler called when a control is disposed.
    /// </summary>
    /// <param name="sender">The object that raised this event</param>
    /// <param name="e">The arguments for this event</param>
    static void ctl_Disposed(object sender, EventArgs e)
    {
    Control ctl = sender as Control;
    if (ctlDict.ContainsKey(ctl))
    ctlDict.Remove(ctl);
    else
    System.Diagnostics.Debug.Assert(false);
    }

    /// <summary>
    /// The event handler called when a control's handle is destroyed.
    /// We remove the HookedProcInformation from <see>hwndDict</see> and
    /// put it back into <see>ctlDict</see> in case the control get re-
    /// created and we still want to hook its messages.
    /// </summary>
    /// <param name="sender">The object that raised this event</param>
    /// <param name="e">The arguments for this event</param>
    static void ctl_HandleDestroyed(object sender, EventArgs e)
    {
    // When the handle for a control is destroyed, we want to
    // unhook its wndproc and update our lists
    Control ctl = sender as Control;
    if (hwndDict.ContainsKey(ctl.Handle))
    {
    HookedProcInformation hpi = hwndDict[ctl.Handle];
    UnhookWndProc(ctl, false);
    }
    else
    System.Diagnostics.Debug.Assert(false);
    }

    /// <summary>
    /// The event handler called when a control's handle is created. We
    /// call SetHook() on the associated HookedProcInformation object and
    /// move it from <see>ctlDict</see> to <see>hwndDict</see>.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    static void ctl_HandleCreated(object sender, EventArgs e)
    {
    Control ctl = sender as Control;
    if (ctlDict.ContainsKey(ctl))
    {
    HookedProcInformation hpi = ctlDict[ctl];
    hwndDict[ctl.Handle] = hpi;
    ctlDict.Remove(ctl);
    hpi.SetHook();
    }
    else
    System.Diagnostics.Debug.Assert(false);
    }

    /// <summary>
    /// This is a generic wndproc. It is the callback for all hooked
    /// windows. If we get into this function, we look up the hwnd in the
    /// global list of all hooked windows to get its message map. If the
    /// message received is present in the message map, its callback is
    /// invoked with the parameters listed here.
    /// </summary>
    /// <param name="hwnd">The handle to the window that received the
    /// message</param>
    /// <param name="msg">The message</param>
    /// <param name="wParam">The message's parameters (part 1)</param>
    /// <param name="lParam">The messages's parameters (part 2)</param>
    /// <returns>If the callback handled the message, the callback's return
    /// value is returned form this function. If the callback didn't handle
    /// the message, the message is forwarded on to the previous wndproc.
    /// </returns>
    private static int WindowProc(
    IntPtr hwnd, uint msg, uint wParam, int lParam)
    {
    if (hwndDict.ContainsKey(hwnd))
    {
    HookedProcInformation hpi = hwndDict[hwnd];
    if (hpi.messageMap.ContainsKey(msg))
    {
    WndProcCallback callback = hpi.messageMap[msg];
    bool handled = false;
    int retval = callback(hwnd, msg, wParam, lParam, ref handled);
    if (handled)
    return retval;
    }

    // if we didn't hook the message passed or we did, but the
    // callback didn't set the handled property to true, call
    // the original window procedure
    return hpi.CallOldWindowProc(hwnd, msg, wParam, lParam);
    }

    System.Diagnostics.Debug.Assert(
    false, "WindowProc called for hwnd we don't know about");
    return Win32.DefWindowProc(hwnd, msg, wParam, lParam);
    }

    /// <summary>
    /// This method removes the specified message from the message map for
    /// the specified hwnd.
    /// </summary>
    /// <param name="ctl">The control whose message we are unhooking
    /// </param>
    /// <param name="msg">The message no longer want to hook</param>
    public static void UnhookWndProc(Control ctl, uint msg)
    {
    // look for the HookedProcInformation in the control and hwnd
    // dictionaries
    HookedProcInformation hpi = null;
    if (ctlDict.ContainsKey(ctl))
    hpi = ctlDict[ctl];
    else if (hwndDict.ContainsKey(ctl.Handle))
    hpi = hwndDict[ctl.Handle];

    // if we couldn't find a HookedProcInformation, throw
    if (hpi == null)
    throw new ArgumentException("No hook exists for this control");

    // look for the message we are removing in the messageMap
    if (hpi.messageMap.ContainsKey(msg))
    hpi.messageMap.Remove(msg);
    else
    // if we couldn't find the message, throw
    throw new ArgumentException(
    string.Format(
    "No hook exists for message ({0}) on this control",
    msg));
    }

    /// <summary>
    /// Restores the previous wndproc for the specified window.
    /// </summary>
    /// <param name="ctl">The control whose wndproc we no longer want to
    /// hook</param>
    /// <param name="disposing">if true we remove don't readd the
    /// HookedProcInformation
    /// back into ctlDict</param>
    public static void UnhookWndProc(Control ctl, bool disposing)
    {
    HookedProcInformation hpi = null;
    if (ctlDict.ContainsKey(ctl))
    hpi = ctlDict[ctl];
    else if (hwndDict.ContainsKey(ctl.Handle))
    hpi = hwndDict[ctl.Handle];

    if (hpi == null)
    throw new ArgumentException("No hook exists for this control");

    // If we found our HookedProcInformation in ctlDict and we are
    // disposing remove it from ctlDict
    if (ctlDict.ContainsKey(ctl) && disposing)
    ctlDict.Remove(ctl);

    // If we found our HookedProcInformation in hwndDict, remove it
    // and if we are not disposing stick it in ctlDict
    if (hwndDict.ContainsKey(ctl.Handle))
    {
    hpi.Unhook();
    hwndDict.Remove(ctl.Handle);
    if (!disposing)
    ctlDict[ctl] = hpi;
    }
    }

    /// <summary>
    /// This class remembers the old window procedure for the specified
    /// window handle and also provides the message map for the messages
    /// hooked on that window.
    /// </summary>
    class HookedProcInformation
    {
    /// <summary>
    /// The message map for the window
    /// </summary>
    public Dictionary<uint, WndProcCallback> messageMap;

    /// <summary>
    /// The old window procedure for the window
    /// </summary>
    private IntPtr oldWndProc;

    /// <summary>
    /// The delegate that gets called in place of this window's
    /// wndproc.
    /// </summary>
    private Win32.WndProc newWndProc;

    /// <summary>
    /// Control whose wndproc we are hooking
    /// </summary>
    private Control control;

    /// <summary>
    /// Constructs a new HookedProcInformation object
    /// </summary>
    /// <param name="ctl">The handle to the window being hooked</param>
    /// <param name="wndproc">The window procedure to replace the
    /// original one for the control</param>
    public HookedProcInformation(Control ctl, Win32.WndProc wndproc)
    {
    control = ctl;
    newWndProc = wndproc;
    messageMap = new Dictionary<uint, WndProcCallback>();
    }

    /// <summary>
    /// Replaces the windows procedure for <see>control</see> with the
    /// one specified in the constructor.
    /// </summary>
    public void SetHook()
    {
    IntPtr hwnd = control.Handle;
    if (hwnd == IntPtr.Zero)
    throw new InvalidOperationException(
    "Handle for control has not been created");

    oldWndProc = Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC,
    Marshal.GetFunctionPointerForDelegate(newWndProc));
    }

    /// <summary>
    /// Restores the original window procedure for the control.
    /// </summary>
    public void Unhook()
    {
    IntPtr hwnd = control.Handle;
    if (hwnd == IntPtr.Zero)
    throw new InvalidOperationException(
    "Handle for control has not been created");

    Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC, oldWndProc);
    }

    /// <summary>
    /// Calls the original window procedure of the control with the
    /// arguments provided.
    /// </summary>
    /// <param name="hwnd">The handle of the window that received the
    /// message</param>
    /// <param name="msg">The message</param>
    /// <param name="wParam">The message's arguments (part 1)</param>
    /// <param name="lParam">The message's arguments (part 2)</param>
    /// <returns>The value returned by the control's original wndproc
    /// </returns>
    public int CallOldWindowProc(
    IntPtr hwnd, uint msg, uint wParam, int lParam)
    {
    return Win32.CallWindowProc(
    oldWndProc, hwnd, msg, wParam, lParam);
    }
    }
    }



    and the TextBox Class I adept from the Treeview class:



    public partial class XactTextBox : TextBox
    {

    private Control prevParent = null;

    public XactTextBox()
    {
    InitializeComponent();
    }

    #region OnClick
    protected override void OnParentChanged(EventArgs e)
    {
    // unhook the old parent
    if (this.prevParent != null)
    {
    WndProcHooker.UnhookWndProc(prevParent, Win32.WM_NOTIFY);
    }
    // update the previous parent
    prevParent = this.Parent;

    // hook up the new parent
    if (this.Parent != null)
    {
    WndProcHooker.HookWndProc(this.Parent,
    new WndProcHooker.WndProcCallback(this.WM_Notify_Handler),
    Win32.WM_NOTIFY);
    }

    base.OnParentChanged(e);
    }


    /// <summary>
    /// The method that gets called when a WM_NOTIFY message is received by the
    /// TreeView's parent.
    /// </summary>
    /// <param name="hwnd">The handle of the window that received the message</param>
    /// <param name="msg">The message received</param>
    /// <param name="wParam">The wParam arguments for the message</param>
    /// <param name="lParam">The lParam arguments for the message</param>
    /// <param name="handled">Set to true to indicate that this message was handled</param>
    /// <returns>An appropriate returen code for the message handled (see MSDN)</returns>
    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;
    }
    protected void OnMouseClick(MouseEventArgs e)
    {
    this.OnClick(new EventArgs());
    }

    protected override void OnClick(EventArgs e)
    {
    base.OnClick(e);
    }



    /// <summary>
    /// Raises the TreeNodeMouseClick event for the TreeNode with the specified handle.
    /// </summary>
    /// <param name="hNode">The handle of the node for which the event is raised</param>
    /// <param name="button">The [mouse] buttons that were pressed to raise the event</param>
    /// <param name="coords">The [client] cursor coordinates at the time of the event</param>
    void RaiseMouseClickEvent(MouseButtons button, Point coords)
    {

    MouseEventArgs e = new MouseEventArgs(button,
    1, coords.X, coords.Y,0);

    OnMouseClick(e);
    }
    #endregion

  • Carl Daniel

    Hello,

    is a complete TextBox sample available which has the Click event implemented
    It's very important for me because the project have to be finished till monday and I need a TextBox with a Click Event.

    Please show me the sources.

    PLEASE HELP

  • AndrewLuiHK

    You can hook the underlying WM_LBUTTONDOWN or WM_LBUTTONUP message and do with it what you want.

    See this article for more information: "Subclassing controls in .NETCF 2.0"

    part 1: http://blogs.msdn.com/netcfteam/archive/2005/05/20/420551.aspx

    part 2: http://blogs.msdn.com/netcfteam/archive/2005/05/23/421143.aspx



  • Frances83

    r u using VBnet or
  • TextBox OnClick