当前位置:首页 > 编程开发

在WinCE程序开发中使用软件输入面板

webgou16年前 (2010-08-30)编程开发448

ps:ATl Dvoraksip (C:\Program Files\Windows Mobile 6.5.3 DTK\Samples\PocketPC\CPP\ATL\dvoraksip)来源:http://msdn.microsoft.com/en-us/library/bb158734.aspx

简介

软件输入面板(Software Input Panel,简称SIP)是每个装备了WinCE系统的移动平台的一个基本功能。它提供给用户在PDA上进行数据输入的一种手段。当谈及SIP的时候,我们一般会想到两点:一是SIP本身,二是如何在程序中使用SIP。

SIP是一个实现了IInputMethod或者IInputMethod2接口的COM对象。它应该被系统 调入使用,所以你不能在C#中进行SIP开发。C或者C++是很好的开发语言。因为SIP本身也只是另外一个COM对象,ATL使得开发过程变得极为简 单。这里我不想讨论SIP开发,SDK包里包含了一个很好的例程 ATLDvoraksip,所以你可以去学习这个例程以获取更多的信息。这里我想讨论的是如何在你自己的程序里管理SIP。这看起来很琐碎简单,但是如果 因为屏幕不太够用,而你又想使得你的程序更加聪明更加方便用户使用,那 SIP的管理就变成很重要的一个方面了。另外,如果你开发了很多用于不同场合的SIP(比如多语言,数字或者其他等等),你可能想在某个场合使用某个特定 的SIP。这种能力使得你可以完成很多不同的任务:当用户只需要进行数字输入的时候,你可以展示一个大的数字键盘,这样他不用笔而只需要用手指就可以输入 了。当然你也可以有自己的想法,这就看程序员该作些什么了。

Win32 API

SIP的API函数很简单,从sipapi.h中只可以看到只有极少几个函数:

DWORD WINAPI SipStatus();
BOOL WINAPI SipSetDefaultRect(RECT *);
BOOL WINAPI SipReGISterNotification(HWND);
BOOL WINAPI SipShowIM(DWORD);
BOOL WINAPI SipGetInfo(SIPINFO *);
BOOL WINAPI SipSetInfo(SIPINFO *);
int WINAPI SipEnumIM(IMENUMPROC);
BOOL WINAPI SipGetCurrentIM(CLSID *);
BOOL WINAPI SipSetCurrentIM(CLSID *);

我把这些放在开始,是因为它同时支持Windows Mobile和CE.net平台。如果你对Windows Mobile设备编程,那么aygshell.h文件提供给你更多的和SIP有关的函数供使用。当然选择哪个来使用就依赖于你自己的需求了,使用 Windows Mobile上的SIP会使得工作更加完美一些。如果你使用了相同的OS版本,但是不同的build版本的时候,你可能会获得稍微不同的SIP行为。所 以,一种方法并不一定像你所想的那样在所有的PDA上都会适用。

列举可用的SIP

第一步就是了解如何列举所有可用的SIP。可以使用如下的代码:

CTypedPtrMap<CMapStringToPtr,CString,CLSID*> g_SipMap;

int SipEnumIMProc(IMENUMINFO *pIMInfo)
{
CLSID* pCLSID = new CLSID;
memcpy(pCLSID,&pIMInfo->clsid,sizeof(CLSID));
g_SipMap.SetAt(CString(pIMInfo->szName),pCLSID);

 TRACE(_T("%sn"),CString(pIMInfo->szName));

 return 1;
}

void CSIPDemoDlg::OnButtonEnum()
{
SipEnumIM(SipEnumIMProc);

 CString sSipName;
CLSID *pCLSID = NULL;
for (POSITION pos = g_SipMap.GetStartPosition(); pos; )
{
g_SipMap.GetNextAssoc(pos,sSipName,pCLSID);
m_SipList.AddString(sSipName);
}
}

代码所作的就是填充一个全局的map,它包含了"SIP名"/CLSID对。这个例子和其他的都用到了MFC,当然你也可以使用熟悉的Win32 API或者其他的框架。以下是我的Dell Axim x50上的显示效果:

如何选择,显示和隐藏特定的SIP

当你知道某个SIP的CLSID,你可以选择它。同时,当前选择的SIP也可以被获得:

void CSIPDemoDlg::OnButtonEnum()
{
SipEnumIM(SipEnumIMProc);

 CLSID CurrSip;
SipGetCurrentIM(&CurrSip);

 int nCurrSip = LB_ERR, nSipCount = 0;
CString sSipName, sCurrSipName;
CLSID *pCLSID = NULL;
for (POSITION pos = g_SipMap.GetStartPosition(); pos; )
{
g_SipMap.GetNextAssoc(pos,sSipName,pCLSID);
m_SipList.AddString(sSipName);

  if ( memcmp(&CurrSip,pCLSID,sizeof(CLSID)) == 0 )
{
nCurrSip = nSipCount;
sCurrSipName = sSipName;
}
nSipCount++;
}
m_SipList.SelectString(0,sCurrSipName);
}


void CSIPDemoDlg::OnButtonSelect()
{
int nSel = m_SipList.GetCurSel();
if ( LB_ERR == nSel )
return;

 CString sSipName;
m_SipList.GetText(nSel,sSipName);
CLSID *pCLSID = NULL;
if ( !g_SipMap.Lookup(sSipName,pCLSID) )
return;

 BOOL bRes = SipSetCurrentIM(pCLSID);
if ( !bRes )
TRACE(L"SipSetCurrentIM returned %lun",GetLastError());
}

void CSIPDemoDlg::OnButtonShowHide()
{
if ( !g_bShow )
{
SipShowIM(SIPF_ON);
g_bShow = TRUE;
}
else
{
SipShowIM(SIPF_OFF);
g_bShow = FALSE;
}
}

void CSIPDemoDlg::OnButtonShowHide2()
{
SIPINFO SipInfo;
memset(&SipInfo,0,sizeof(SipInfo));
SipInfo.cbSize=sizeof(SIPINFO);
BOOL bRes = SipGetInfo(&SipInfo);
if ( bRes )
{
if ( !g_bShow )
{
SipInfo.fdwFlags |= SIPF_ON;
g_bShow = TRUE;
}
else
{
SipInfo.fdwFlags = SIPF_OFF;
g_bShow = FALSE;
}
bRes = SipSetInfo(&SipInfo);
}
else
{
TRACE(L"SipGetInfo returned %lun",GetLastError());
}
}

这里,你可以看到修改的例子(CSIPDemoDlg::OnButtonEnum()),它检测哪个SIP 是被激活的并且选择listbox里相应的行。其他例子的对话框方法选择SIP并且显示或者隐藏它。注意,为了使得SipGetInfo或者 SipSetInfo工作,你必须用sizeof (SIPINFO)的值初始化SIPINFO.cbSize,这样操作系统才能正常反应。这是一个很常用的Win32的解决方式。

SipGetInfo给你可视的桌面和SIP大小,这样你可以在需要的时候重新定制SIP的位置。而SipSetInfo不会改变SIP的位置。如果你需要移动SIP,使用SipSetDefaultRect。下面会给出一些例子来说明的。

使用Shell函数

如果你决定使用基于aygshell的函数,SHSipPreference也会为你完成相似的任务。和 SipShowIM一样,这个API将会显示和隐藏输入面板。它的最后一个参数定义了作什么,可以请求显示或隐藏输入面板,马上隐藏(因为操作系统在一个 常规事例里将会为此设置一个计时器)或者放弃所有的等待中的请求。SHSipInfo允许你作和SipXXX函数一样的操作。

通常,你可以用以上所有的API来响应WM_SETTINGCHAANGE或WM_CREATE消息。根据文 档,当你使用SHSipInfo来处理 WM_SETTINGCHANGE消息的时候要特别小心。这有几个原因:第一,SIP的改变可能导致shell来发送WM_SETTINGCHANGE消 息,所以当你在你的处理函数里调用的时候要小心无限循环;第二,SHSipInfo将会和Device.exe以及输入面板线程相互作用,因此将会占据 100ms左右的时间。这将导致WM_SETTINGCHANGE消息发送给所有运行的程序,所以系统将会因此而失去响应一段时间。将lParam值同时 和WM_SETTINGCHANGE传递将会避免这样的延迟。

当说到shell函数时,有些Windows Mobile平台上的函数会非常有用也可能会变得让你头痛。这些包括SHInputDialog,SHFullScreen或者类似的调用。另外, SHHandleWMActivate和SHHandleWMSettingChange会让你充分享受MFC程序中键盘自动弹出的乐趣,因为它们被用在 合适的消息句柄的CDialog和CFrameWnd类中。如果你不想拥有缺省的行为,你可以重载OnActivate或OnSettingChange 句柄。

自定义控制

如果你想支持一种智能化的输入框类的行为--每次进行输入时键盘自动弹出,然后当输入焦点从输入框移开后键盘隐藏,那么需要对WM_SETFOCUS和WM_KILLFOCUS进行处理。下面的代码可以帮助你:

void CSipEdit::OnSetFocus(CWnd* pOldWnd)
{
CEdit::OnSetFocus(pOldWnd);

 SHSipPreference(m_hWnd,SIP_UP);
}

void CSipEdit::OnKillFocus(CWnd* pNewWnd)
{
CEdit::OnKillFocus(pNewWnd);

 SHSipPreference(m_hWnd,SIP_FORCEDOWN);
}

设定SIP位置

下面谈谈如何移动SIP到屏幕上的某个位置。SipSetDefaultRect将会改变缺省的SIP的举行,但是它不会立即生效,除非你重新选择输入法:

void CSIPDemoDlg::OnButtonMove()
{
SIPINFO SipInfo;
memset(&SipInfo,0,sizeof(SipInfo));
SipInfo.cbSize=sizeof(SIPINFO);
BOOL bRes = SipGetInfo(&SipInfo);
if ( bRes )
{
CRect rc(SipInfo.rcSipRect);
rc.OffsetRect(0,-20);
SipSetDefaultRect(&rc);

  CLSID clsid;
if ( SipGetCurrentIM(&clsid) )
{
SipSetCurrentIM(&clsid);
}
SipShowIM(SIPF_ON);
}
}

;

上面的代码告诉你如何在屏幕上移动SIP的位置。这在你想在屏幕低端放置一些控件的时候会非常有用。当然这样不符合微软的Pocket PC的GUI设计标准,但是有些时候你也没有其他的选择。

如果你可以获取窗口的句柄来控制窗口的样式,比如:

long lStyle=GetWindowLong(hwndSIP,GWL_STYLE);
lStyle |= WS_CAPTION|WS_SYSMENU;
SetWindowLong(hwndSIP,GWL_STYLE,lStyle);

结束语

我们已经讨论了在WindowsCE操作系统上对SIP进行管理的若干方面,希望这些能够帮助你的程序成为用户眼里的赢家。

 

来源:http://www.uml.org.cn/embeded/200607271.htm

扫描二维码推送至手机访问。

版权声明:本文由知了博客发布,如需转载请注明出处。

本文链接:https://www.webgou.info/?id=302

标签: wince
分享给朋友:

“在WinCE程序开发中使用软件输入面板” 的相关文章

NetLimiter v3.0.0.3 Beta3 - 网络流量控制软件

NetLimiter以前夜火介绍过他的 NetLimiter Pro 2.0.9.1 汉化版 ,NetLimiter可以限制系统中每个进程所使用的网络带宽,很强大,今天看到它的 NetLimiter v3.0.0.3 Beta3 版,将其发出来。...…

线程同步

效率      you should try to use nonshared data first and then use volatile reads, volatile writes, interlocked APIs, SRWL…

单位dB~

1、dB    dB是一个表征相对值的值,纯粹的比值,只表示两个量的相对大小关系,没有单位,当考虑甲的功率相比于乙功率大或小多少个dB时,按下面计算公式:10log(甲功率/乙功率),如果采用两者的电压比计算,要用20log(甲电压/乙电压)。  &…

预处理器-编译器-汇编器-链接器

这篇文章主要介绍内部链接与外部链接.…

android 签名

所有的android程序都需要签名1. 为什么要签名...…

c++操作符重载

c++有许多内置数据类型,包括int,char,double, 第一种类型有许多运算符,比如,+ - * /.当用户定义了许多类对象时,对像间是不能进行这些操作的.但c++类允许把这些运算符内置到c++类中,可以像操作内置对象一样操作.…

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。