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:
- 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.
- Add a new *.cs class, name it as
WndProcHooker
and copy the contents of the downloaded WndProcHooker
class into it.
- Add a new *.cs class, name it
Win32
and copy the contents of the downloaded Win32
class into it.
- 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.
-
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
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:
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);
}
public void RaiseMouseClickEvent(MouseButtons button, Point coords)
{
MouseEventArgs e = new MouseEventArgs(button,
1, coords.X, coords.Y, 0);
OnMouseClick(e);
}
int WM_LButtonDown_Handler(
IntPtr hwnd, uint msg, uint wParam, int lParam,
ref bool handled)
{
this.Capture = true;
this.Focus();
ClickableTextBox_Clicked(this, null);
handled = true;
return 0;
}
int WM_LButtonUp_Handler(
IntPtr hwnd, uint msg, uint wParam, int lParam,
ref bool handled)
{
this.Capture = false;
handled = true;
return 0;
}
#endregion
}
Let’s examine the code in the ClickableTextBox
class.
- In
ClickableTextBox
constructor, Mouse LEFT button up and Mouse LEFT Button Down events messages are hooked.
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
.
-
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;
WM_LButtonDown_Handler
is the handler for Mouse Left button Down which I have left unimplemented.
- 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 ! :)
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).
- 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.
- [optional] Add a reference of ClickableTextBox.dll if it is not added in project references by drag and drop.
- 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