(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

TextBox OnClick
James S. Jan
hashbrown
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
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