zoukankan      html  css  js  c++  java
  • 在Win CE程序开发中使用软件输入面板

    软件输入面板(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。可以使用如下的代码:

     1 CTypedPtrMap<CMapStringToPtr,CString,CLSID*> g_SipMap;
     2 int SipEnumIMProc(IMENUMINFO *pIMInfo)
     3 {
     4  CLSID* pCLSID = new CLSID;
     5  memcpy(pCLSID,&pIMInfo->clsid,sizeof(CLSID));
     6  g_SipMap.SetAt(CString(pIMInfo->szName),pCLSID);
     7  TRACE(_T("%sn"),CString(pIMInfo->szName));
     8  return 1;
     9 }
    10 void CSIPDemoDlg::OnButtonEnum()
    11 {
    12  SipEnumIM(SipEnumIMProc);
    13  CString sSipName;
    14  CLSID *pCLSID = NULL;
    15  for (POSITION pos = g_SipMap.GetStartPosition(); pos; )
    16  {
    17   g_SipMap.GetNextAssoc(pos,sSipName,pCLSID);
    18   m_SipList.AddString(sSipName);
    19  }
    20 }

    代码所作的就是填充一个全局的map,它包含了"SIP名"/CLSID对。这个例子和其他的都用到了MFC,当然你也可以使用熟悉的Win32 API或者其他的框架
    如何选择,显示和隐藏特定的SIP
    当你知道某个SIP的CLSID,你可以选择它。同时,当前选择的SIP也可以被获得:

     1 void CSIPDemoDlg::OnButtonEnum()
     2 {
     3  SipEnumIM(SipEnumIMProc);
     4  CLSID CurrSip;
     5  SipGetCurrentIM(&CurrSip);
     6  int nCurrSip = LB_ERR, nSipCount = 0;
     7  CString sSipName, sCurrSipName;
     8  CLSID *pCLSID = NULL;
     9  for (POSITION pos = g_SipMap.GetStartPosition(); pos; )
    10  {
    11   g_SipMap.GetNextAssoc(pos,sSipName,pCLSID);
    12   m_SipList.AddString(sSipName);
    13   if ( memcmp(&CurrSip,pCLSID,sizeof(CLSID)) == 0 )
    14   {
    15    nCurrSip = nSipCount;
    16    sCurrSipName = sSipName;
    17   }
    18   nSipCount++;
    19  }
    20  m_SipList.SelectString(0,sCurrSipName);
    21 }
    22 void CSIPDemoDlg::OnButtonSelect()
    23 {
    24  int nSel = m_SipList.GetCurSel();
    25  if ( LB_ERR == nSel )
    26   return;
    27  CString sSipName;
    28  m_SipList.GetText(nSel,sSipName);
    29  CLSID *pCLSID = NULL;
    30  if ( !g_SipMap.Lookup(sSipName,pCLSID) )
    31   return;
    32  BOOL bRes = SipSetCurrentIM(pCLSID);
    33  if ( !bRes )
    34   TRACE(L"SipSetCurrentIM returned %lun",GetLastError());
    35 }
    36 void CSIPDemoDlg::OnButtonShowHide()
    37 {
    38  if ( !g_bShow )
    39  {
    40   SipShowIM(SIPF_ON);
    41   g_bShow = TRUE;
    42  }
    43  else
    44  {
    45   SipShowIM(SIPF_OFF);
    46   g_bShow = FALSE;
    47  }
    48 }
    49 void CSIPDemoDlg::OnButtonShowHide2()
    50 {
    51  SIPINFO SipInfo;
    52  memset(&SipInfo,0,sizeof(SipInfo));
    53  SipInfo.cbSize=sizeof(SIPINFO);
    54  BOOL bRes = SipGetInfo(&SipInfo);
    55  if ( bRes )
    56  {
    57   if ( !g_bShow )
    58   {
    59    SipInfo.fdwFlags |= SIPF_ON;
    60    g_bShow = TRUE;
    61   }
    62   else
    63   {
    64    SipInfo.fdwFlags = SIPF_OFF;
    65    g_bShow = FALSE;
    66   }
    67   bRes = SipSetInfo(&SipInfo);
    68  }
    69  else
    70  {
    71   TRACE(L"SipGetInfo returned %lun",GetLastError());
    72  }
    73 }

    这里,你可以看到修改的例子(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进行处理。下面的代码可以帮助你:

     1 void CSipEdit::OnSetFocus(CWnd* pOldWnd)
     2 {
     3  CEdit::OnSetFocus(pOldWnd);
     4  SHSipPreference(m_hWnd,SIP_UP);
     5 }
     6 void CSipEdit::OnKillFocus(CWnd* pNewWnd)
     7 {
     8  CEdit::OnKillFocus(pNewWnd);
     9  SHSipPreference(m_hWnd,SIP_FORCEDOWN);
    10 }


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

     1 void CSIPDemoDlg::OnButtonMove()
     2 {
     3  SIPINFO SipInfo;
     4  memset(&SipInfo,0,sizeof(SipInfo));
     5  SipInfo.cbSize=sizeof(SIPINFO);
     6  BOOL bRes = SipGetInfo(&SipInfo);
     7  if ( bRes )
     8  {
     9   CRect rc(SipInfo.rcSipRect);
    10   rc.OffsetRect(0,-20);
    11   SipSetDefaultRect(&rc);
    12   CLSID clsid;
    13   if ( SipGetCurrentIM(&clsid) )
    14   {
    15    SipSetCurrentIM(&clsid);
    16   }
    17   SipShowIM(SIPF_ON);
    18  }
    19 }

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

    1 long lStyle=GetWindowLong(hwndSIP,GWL_STYLE);
    2 lStyle |= WS_CAPTION|WS_SYSMENU;
    3 SetWindowLong(hwndSIP,GWL_STYLE,lStyle);
  • 相关阅读:
    42. Trapping Rain Water
    223. Rectangle Area
    645. Set Mismatch
    541. Reverse String II
    675. Cut Off Trees for Golf Event
    安装 VsCode 插件安装以及配置
    向上取整 向下取整 四舍五入 产生100以内随机数
    JS 判断是否为数字 数字型特殊值
    移动端初始配置,兼容不同浏览器的渲染内核
    Flex移动布局中单行和双行布局的区别以及使用
  • 原文地址:https://www.cnblogs.com/91program/p/5246587.html
Copyright © 2011-2022 走看看