一个软件专业与否,在细微之处体现的淋漓精致。目前软件开发基于组件思想,使得软件开发像搭积木一样。软件模块的封装分两种,一种是业务功能的封装(我称它为组件),一种是表现界面的封装(我称它为控件)。
组件的封装因为与界面无关,所以问题大多在接口数据类型上。控件与界面有关,很多朋友在开发控件的时候很有激情,看着自己的东东在什么环境下面都可以使用,很是有成就感。然而稍有专业水准的人应该会发现这不是一件完美的事情,因为microsoft没有为activex控件处理加速键消息处理,以至于导致很多问题:up、down、left、right、home、end、tab、backspace等在自己的控件里面不起作用,编辑框无法删除字符,tab键无法跳转到下一个控件上。我是深受其害,以至于有一年多不敢染指控件开发。这个问题很多人遇见过,但是没见到谁给出个好的方法来。
对于上面的问题,微软也意识到,但是一直没有给出完美的一套方案,只是在msdn里面零星提到一下,给出了一个基于mfc activex的解决办法。由于我一般使用atl开发控件,所以那个办法我一直未曾感到满意过。上网搜索不少,一般都不能解决实质性问题,有甚者把钩子都搬出来,个人觉得没有那么大必要,毕竟我一般不敢将hook技术应用在大软件里面。我依据个人摸索,基本解决了大部分问题,当然任何事情都是不完美的,拿出来为了一个csdn朋友需要,也为了大家提出更好的解决办法。
本篇以vs2005环境,实做出atl标准控件、复合控件,测试环境分别为VB6 VC6(dialog) .net(C#) IE。分别展示出问题以及解决办法,记忆里vb控件在mfc里面也有问题(好像tab键出不去,需要一个隐藏控件),对于此本文不做探讨,如有需要,下次在说。
这里先谈谈做控件的选择:标准控件分无窗口和有窗口。
如果只设计图形操作,不需要窗口,可以通过IOleObject来解决位置以及大小,这样可以减少不少控件的尺寸。
对于有窗口的控件,一般情况是一个做好的窗口类,为了封装以使用于各种开发环境而依附到atl控件窗口上面(至于不创建窗口可否?当然可以,这里不想讲,牵扯太远)。对于这种控件个人认为最好只依附一个窗口,如果想依附几个窗口,请使用复合控件。
复合控件类似一个对话框面板,你可以托放控件到面板上,这种控件问题比较少,我推荐使用窗口控件的人选择此类型,尽管dll尺寸可能大点。
标准带窗口控件问题:
我制作的控件是一个edit创建在atl窗口上面,不作任何处理在各种环境里面使用情况如下:
VB6:Tab正常,但是在atl控件的时候,edit无Caret(不知道怎么翻译)闪烁,并且鼠标点击在里面后有Caret,然而按下左键跑到上一个tab窗口上面去了。
VC6:比VB6好一点,左右键是好的,也是tab后无Caret闪烁
.net:和VB6情况一样,无语,难怪.net和VB6一样好用(^_^,没有瞧不起.net,我深受VC之苦,好想用.net开发界面)
IE:和VB6大致一样就是左右键全部不起作用。
处理方法:
1、添加SetFocuse消息处理:
LRESULT CATLFullCtrl_Step2::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&
bHandled)
{
//
TODO: 在此添加消息处理程序代码和/或调用默认值
LRESULT lRes
=
CComControl
<
CATLFullCtrl_Step2
>
::OnSetFocus(uMsg, wParam, lParam, bHandled);
if
(m_bInPlaceActive)
{
DoVerbUIActivate(
&
m_rcPos, NULL);
if
(
!
IsChild(::GetFocus()))
{
m_wndEdit.SetFocus();
}
}
return
lRes;
}
当atl控件接到focuse消息后,把焦点给edit窗口。还不够,接着是主要的:
2、重载加速键需函数:
让我们先看看微软做了什么:
BOOL PreTranslateAccelerator(LPMSG /*pMsg*/, HRESULT& /*hRet*/)
{
return FALSE;
} 靠,明显不作为嘛!什么都不处理,怎么可能正确啊......
我来试一试:
BOOL CATLFullCtrl_Step2::PreTranslateAccelerator(LPMSG pMsg, HRESULT& hRet)
{
// TODO: 在此添加专用代码和/或调用基类
if(pMsg->message == WM_KEYDOWN &&
(pMsg->wParam == VK_LEFT ||
pMsg->wParam == VK_RIGHT ||
pMsg->wParam == VK_UP ||
pMsg->wParam == VK_DOWN))
{
hRet = S_FALSE;
return TRUE;
}
return __super::PreTranslateAccelerator(pMsg, hRet);
} 为什么??还记得前面的问题嘛??Tab键好像是好的,就是左右键不正确啊,^_^,这里我假设处理拉,你就别跳别处了好嘛???
下面来看看各种环境里面使用情况:
VB6:正常
VC6:正常
.net:正常
IE:第一此tab到控件焦点控件外框,点击编辑框之后下次就是好的了。不晓得是不是我没有处理好。
复合控件的问题:
我制作的复合控件是一个按钮、一个复选框、一个编辑框、一个单选框。不作任何处理在各种环境里面使用情况如下:
VB6:没大毛病,就是默认焦点在复合控件第一个tab窗口上(假象,你切换一下窗口就恢复到正常情况下,但是复合控件button里面按钮的默认黑色外框还有,我实在处理不好),且编辑框输入汉语是乱码(好像与Unicode编码有关)。
VC6:很完美,还是自产自消比较对路。
.net:和VB6情况差不多,但是没有乱码问题。
IE:问题不大,你自己看看,实在不好描述,我保证不影响使用。
处理方法:
1、乱码问题,不要问我为什么,我也是瞎蒙的,^_^。不过你要想知道为什么复合控件问题这么少,我建议你看看基类CComCompositeControl的PreTranslateAccelerator实现,晕,那么多判断代码,还有问题,咳,不说了。
STDMETHOD(TranslateAccelerator)(LPMSG pMsg)
{
if(pMsg->message == WM_CHAR)
{
return S_FALSE;
}
HRESULT hr = NO_ERROR;
hr = IOleInPlaceActiveObjectImpl<CATLCompoundCtrl_Step2>::TranslateAccelerator(pMsg);
if(hr != S_OK)
{
CComQIPtr<IOleControlSite,&IID_IOleControlSite> spCtrlSite(m_spClientSite);
if(spCtrlSite)
{
hr = spCtrlSite->TranslateAccelerator(pMsg, 0);
if(hr != S_OK)
{
IsDialogMessage(pMsg);
}
}
}
return S_OK;
} 主要解决乱码的就是那段判断WM_CHAR消息返回S_FALSE的代码。下面那段代码借用microsoft的,呵呵。
2、VB6那个假象focus处理,这个我是在VB6里面做的,就是强制focus到form第一个tab窗口上,焦点倒是对了,但是复合控件上面那个按钮的默认黑色外框在tab一个轮回后才正常。处理代码:
Private Sub Form_Activate()
Command1.SetFocus
End Sub
3、.net以及IE的我没有解决掉那个假focus,望高人支招。
下面来看看各种环境里面使用情况:
VB6:那个默认按钮黑色外框没解决掉,其他ok
VC6:完美
.net:假focuse没有解决掉
IE:假focuse没有解决掉
下面来总结一下:
好像没有一种万能办法可以解决掉所有情况下的问题,我尝试很久总结出这些东西不知道算不算好的办法,但是还算是消除了一些关键问题,不知道是否使用你遇到的问题。.net控件好像很方便也没有那么多资源切换啊,加速键消息处理问题啊。没办法,我们这些it“马崽”很多事情是不由自己的。
遗留问题:
假focuse、默认按钮黑色外框没有处理好。
代码下载说明:包括activex控件、VB6测试、VC6测试、.net测试。
posted on 2006-11-17 21:40
万连文 阅读(2362)
评论(1) 编辑 收藏 引用 所属分类:
ATL