您的位置:首页 > 移动开发

Windows Mobile Development Making a quality platform one application at a time(zhuan)

2009-09-07 13:17 621 查看

Windows Mobile Development

Making a quality platform one application at a time

« Creating non full screen forms and custom MessageBoxes

Will the following C# compile?
»

Add Cut/Copy/Paste functionality to a Textbox

Earlier
I discussed how you could manually get the software keyboard (SIP) to
display whenever a TextBox control gained focus. There was potentially
a lot of event handlers to write, two for every control on a form.
Today I will show you an alternative approach that utilises less code
but also has some additional benefits.

A common thing I do while testing new Windows Mobile applications,
is to tap-and-hold on a text field. Very well behaved applications
should popup a context sensitive menu containing cut/copy/paste style
options for the current control. Surprisingly, very few applications
actually pass this test, even though it is a feature that has been
built into the operating system for a while.

Within Windows CE the SIPPREF
control can be used to automatically implement default input panel behavior for a dialog. It provides the following features:

The Input Panel is automatically shown/hidden as controls gain and loose focus.

Edit controls have an automatic context menu with Cut, Copy, Paste type options.

The SIP state is remembered if the user switches to another application and later returns to this form.

In my mind this has three advantages over the process I previously discussed
.

You add the SIPPREF control once and it automatically hooks up
event handlers for each of your controls. With the manual event handler
approach it’s easy to add a new control and forget to hook up the
events required to handle the SIP.

You get free localisation. Although you could create a custom
context menu for cut/copy/paste, you would need to localise the text
into multiple languages yourself (if you are concerned with true
internalisation that is) and it’s another thing thing to hook up for
each control.

You get standardised behavior. By using functionality provided by
the operating system you are ensuring that your application has a
natural and expected behavior to it. If the platform ever changes the
conventions of SIP usage, your application will automatically be
updated.

For the rest of this blog entry I will discuss how to go about
utilising the SIPPREF control within your application. I have split my
discussion into two sections. The first section will be of interest to
native developers developing in C or C++, while the second section is
intended for .NET Compact Framework developers.

Each section contains a small example application which demonstrates
the behaviour of the SIPPREF control within the respective environment.
When you run the sample applications you will notice that the SIP does
not popup up when you click within a text box. And a tap-and-hold
operation yields nothing. This behaviour changes when a SIPPREF control
is added to the dialog, which can be achieved by clicking the sole
button.

Native Developers

[Download sipprefcontrolnativeexample.zip 16KB
]

In order to use the SIPPREF control we must first request the
operating system to register the SIPPREF window class. We do this by
calling the SHInitExtraControls
function. This step only needs to be done once, so is typically done
during your application’s start up code. It is very easy to call, as
the following example demonstrates:

#include <aygshell.h>

SHInitExtraControls(
)
;


Since SHInitExtraControls lives within aygshell.dll, we also need to modify our project settings to link with aygshell.lib
, otherwise the linker will complain that it can not find the SHInitExtraControls function.

Once we have registered the SIPPREF window class, we simply create a
SIPPREF control as a child of our dialog. When the SIPPREF control is
created it will enumerate all sibling controls and subclass
them in order to provide the default SIP handling behaviour. The
SIPPREF control must be the last control added to your dialog, as any
controls added after the SIPPREF control will not be present when the
SIPPREF control enumerates its siblings, and hence will not be
subclassed to provide the proper SIP handling.

If dynamically creating the SIPPREF control, a good place to do this
is within the WM_CREATE or WM_INITDIALOG message handler, as the
following code sample demonstrates:

case
WM_INITDIALOG:

// Create a SIPPREF control to handle the SIP. This

// assumes 'hDlg' is the HWND of the dialog.

CreateWindow(
WC_SIPPREF,
L""
,
WS_CHILD,

0
,
0
,
0
,
0
,
hDlg,
NULL,
NULL,
NULL)
;


As an alternative to dynamically creating the SIPPREF control, we
can place the control within our dialog resource by adding the
following control definition to the end of a dialog within the
project’s *.rc file.

CONTROL  ""
,-
1
,
WC_SIPPREF,
NOT WS_VISIBLE,-
10
,-
10
,
5
,
5


Depending upon your developer environment you may even be able to do
this entirely from within the Resource Editor GUI. For example within
Visual Studio 2005 you could drag the “State of Input Panel Control”
from the Toolbox onto your form to cause a SIPPREF control to be added.



.NET Compact Framework Developers

[Download sipprefcontrolexample.zip 16KB
]

The process of using the SIPPREF control for a .NET Compact
Framework application is fairly similar to that of a Native
application. Since the .NET Compact Framework does not natively support
the use of dialog templates, we must use the CreateWindow approach to
create a SIPPREF control dynamically at runtime.

The first step is to declare a number of PInvoke method declarations for the various operating system APIs we need to call.

using
System.Runtime.InteropServices
;

[
DllImport(
"aygshell.dll"
)
]

private
static
extern
int
SHInitExtraControls(
)
;

[
DllImport(
"coredll.dll"
)
]

private
static
extern
IntPtr CreateWindowEx(

uint
dwExStyle,

string
lpClassName,

string
lpWindowName,

uint
dwStyle,

int
x,

int
y,

int
nWidth,

int
nHeight,

IntPtr hWndParent,

IntPtr hMenu,

IntPtr hInstance,

IntPtr lpParam)
;

private
static
readonly
string
WC_SIPPREF =
"SIPPREF"
;

private
static
readonly
uint
WS_CHILD =
0x40000000;


One interesting fact is that we PInvoke a function called
CreateWindowEx, while the native example above called CreateWindow. If
you dig deeper into the Window Header files you will notice that
CreateWindow is actually a macro which expands into a call to
CreateWindowEx. At the operating system level the CreateWindow function
doesn’t actually exist.

With this boiler plate code out of the way, the solution is very similar to the native one…

protected
override
void
OnLoad(
)

{

// Initialise the extra controls library

SHInitExtraControls(
)
;

// Create our SIPPREF control which will enumerate all existing

// controls created by the InitializeControl() call.

IntPtr hWnd =
CreateWindowEx(
0
, WC_SIPPREF, ""
, WS_CHILD,

0
, 0
, 0
, 0
, this
.Handle
, IntPtr.Zero
, IntPtr.Zero
, IntPtr.Zero
)
;

}


In the above example we simply create the SIPPREF control within the OnLoad method of our form. Within the downloadable sample project
I have wrapped up this code into a static method called SIPPref.Enable(Form f) to enable to it easily be reused between forms.

Hopefully today I have shown you that having knowledge of the
underlying operating system is still a useful skill to have for .NET
Compact Framework developers. Knowing the features provided by the
operating system can allow you to add some neat functionality to your
applications with little additional effort on your behave.

This entry was posted
on Tuesday, October 2nd, 2007 at 10:32 pm and is filed under User Interface
.
You can follow any responses to this entry through the RSS 2.0
feed.
You can leave a response
, or trackback
from your own site.

3 Responses to “Add Cut/Copy/Paste functionality to a Textbox”



Alex Prozor
says:

March 4, 2008 at 2:29 am

Hi, unfortunately it doesn’t works on TextBoxes with non empty PasswordChar. (at least on WM6).

Textbox doesn’t repaint.

Do you have ideas how to fix it?



MinhDanh
says:

March 10, 2009 at 8:58 pm

It basically works from .NET CF via P/Invoke. But I discover a few things

(1) If a panel is used and the textbox is inside that Panel, the
handle of the panel (and not the form) needs to passed to hWndParent in
the call to CreateWindowEx. If the form handle is to be passed, no
context menu will appear

(2) Context menu won’t appear on textboxes which are added to the
form programmatically after the call to CreateWindowEx. Attempting to
call CreateWindowEx again on the same form handle will hang the
application!

(3) If an InputPanel is used to resize the form when the SIP
appears, the context menu will disappear after a while. I am not sure
exactly what triggers this, but I suspect it stops working either when
the SIP is raised, or when the form is resized.

(4) Like Alex Prozor said, this will not work on a password textbox.
When I tried this, my textbox is repainted properly but no context menu
appears.

Any idea how to fix this? Thanks



Jose Zunom
says:

March 12, 2009 at 10:31 pm

Hello,

thank you for the code. But I would like to comment that this code
only adds the copy/cut/… context menu to the controls that are added
directly in the form not any textBox added to a Panel or other
containers.

So you have to do the same call with all the panel that have controls:

IntPtr hWnd = CreateWindowEx(0, WC_SIPPREF, “”, WS_CHILD,0, 0, 0, 0, panel1.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

IntPtr hWnd = CreateWindowEx(0, WC_SIPPREF, “”, WS_CHILD,0, 0, 0, 0, panel2.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);



and so on.

By the way, congratulations by the blog.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐