多语言中的自定义快捷键实现
来源:互联网 发布:阿里云虚拟机怎么打开 编辑:程序博客网 时间:2024/06/16 11:10
一、问题的提出
一般的商业性软件的基本功能要求之一是实现自定义快捷键来提高易用性,这样用户可以根据自己习惯的更改快捷键,在长期使用中逐渐熟练并加快软件的使用速度,提高使用效率。
但这种快捷键要求与菜单提示相一致,并随快捷键的更换而菜单相应的修改显示,这有点麻烦,如果是多语言程序,牵扯的细节更多。本文就来论述相关的技术实现原理和相关实现细节。
二、界面预览
为了为对下文有直观的认识,首先预览下程序的界面实现
三、解决思路
基本思路比较简单:程序实现主体有一份快捷键资源,一般为Accelerator/IDR_MAINFRAME,在程序初始化中载入快捷键资源m_hAccelTable,并拷贝此资源到m_hAccelDefault,再从注册表中载入设置的快捷键键值,赋值给m_hAccelTable,所有的快捷键修改操作都是针对m_hAccelTable,用完再保存到注册表中。如果要重置所有快捷键,则载入m_hAccelDefault,。
对于快捷键资源的修改,因为与菜单资源互动,所以比较复杂一些。要定义一个快捷键资源控制类CAccelControl,这个类负责自定义快捷键从注册表中的载入载出。同步菜单和同步快捷键资源以及查找相关命令ID。
在文章后面的附注上有相关虚拟键与Modifier键值的解释。
四、具体流程实现
1) 初始化快捷键控制类CAccelControl
从注册表中取得相关键值,为其分配足够的尺寸,然后把键值内容复制到其成员变量ACCEL * m_pAccelTable句柄中,
m_pAccelControl = new CAccelControl;
if(!m_pAccelControl) {
return -1;
}
m_pAccelControl->ReadProfile(_T("Settings"));
2) 初始化程序中的快捷键资源,完成两个成员变量的赋值,并同步菜单资源
if( m_hAccelTable != NULL ) {
return FALSE;
}
m_hAccelTable = ::LoadAccelerators(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_RACCEL));
m_hAccelDefault = m_hAccelTable;
if(m_Control.GetCount())
{
m_hAccelTable = *m_xprControl.m_pAccelControl;
m_Control.UpdateMenuKeys(m_Menu.GetSafeHmenu());
}
3) 初始化程序中的快捷键资源,完成两个成员变量的赋值,并同步菜单资源
if( m_hAccelTable != NULL ) {
return FALSE;
}
m_hAccelTable = ::LoadAccelerators(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_RACCEL));
m_hAccelDefault = m_hAccelTable;
if(m_Control.GetCount())
{
m_hAccelTable = *m_xprControl.m_pAccelControl;
m_Control.UpdateMenuKeys(m_Menu.GetSafeHmenu());
}
4) 同步菜单资源
这段代码封装在CAccelControl类中实现。
因为菜单中的tooltips与快捷键要相匹配,并随快捷键的修改而动态修改,所以处理起来有些麻烦。
从主程序中传递来主程序的菜单句柄,快捷键资源句柄初始化类相关成员,通过递归所有菜单和其子菜单来处理’/t’,菜单项对应的快捷键ID,如果有就增加到菜单项上的字符串上,并通过ModifyMenu反映在菜单显示上。这段代码比较繁杂,所以只给出部分实现。
void CAccelControl::UpdateMenuKeys(HMENU hMenu)
{
XHotkeyPage dlg;
dlg.init((HMENU)1, m_hSysAccel, m_hSysAccel);
// 递归所有菜单(子项)
int nItems = GetMenuItemCount(hMenu);
MENUITEMINFO mi;
mi.cbSize = sizeof(MENUITEMINFO);
mi.fMask = MIIM_ID | MIIM_SUBMENU;
TCHAR buf[512];
CString name;
for( int i=0; i<nItems; i++ )
{
// 得到位置
GetMenuItemInfo(hMenu, i, TRUE, &mi);
if(mi.hSubMenu) {
UpdateMenuKeys(mi.hSubMenu);
}
.....
}
}
5) 对话框界面显示
在对话框实现函数ShowOptionDialog中,初始化传递过来的菜单句柄,原始快捷键资源句柄,由注册表载入的快捷键句柄。
hotkeyPage.init(m_hMenu, m_hAccelTable, m_hAccelDefault);在这个函数里完成对话框中相关变量的初始化。在DoModal之后,重新取得键值,赋值并更新菜单
if( hotkeyPage.m_nAccel )
{
if(m_pAccelControl->SetAccelTable(hotkeyPage.m_pNewAccels, hotkeyPage.m_nAccel))
{
// 使用新键值
m_hAccelTable = *m_pAccelControl;
m_pAccelControl->UpdateMenuKeys(m_hMenu);
}
}
6) 程序实现流程
上面的流程比较乱,现在给出程序实现流程
1. 主程序CMainFrame负责程序的初始化,快捷键资源的载入,类的初始化和界面显示
2. 针对主界面的主控制类由CControl**来处理,负责处理控制代码,和如主对话框数据交换
3. 针对快捷键资源的载入出,同菜单资源的同步等由CAccelControl实现
4. 快捷键对话框界面在对话框类CHotKey**中实现
5. 代码如果再次优化,可把3并到2中
五、后续
1) 从ACCEL转化为字符串
从ACCEL转化为字符串要与FCONTROL/FALT/FSHIFT/FVIRTKEY比较,其中的FVIRTKEY还要判断是否是扩展键,并通过GetKeyNameText获得字符串。
2) 从MAKELPARAM(m_pNewAccels[i].fVirt, m_pNewAccels[i].key) 组成的DWORD值中转换为CHotKeyCtrl可以显示的值
需要通过HOTKEYF_CONTROL/FCONTROL,FALT/HOTKEY_ALT,FSHIFT/HOTKEY_SHIFT,以及扩展键值的HOTKEYF_EXT转化。
3) 注意FNOINVERT
MSDN中描述:Specifies that no top-level menu item is highlighted when the accelerator is used. If this flag is not specified, a top-level menu item will be highlighted, if possible, when the accelerator is used.
如果从DWORD值分析所得的值没有与FNOINVERT |一下,所设的新键值则会与快捷键资源IDR_MAINFRAME中重复
感觉还是没讲清楚。
- 多语言中的自定义快捷键实现
- 自定义实现带checkbox的listView(用于项目中的多语言实现界面)
- C#自定义快捷键实现介绍
- C语言实现自定义多参数函数
- 简单聊聊Vim中的自定义快捷键
- WPF 纯代码实现自定义快捷键
- C语言中的多态实现
- winform中的多语言的实现
- C语言中的多态实现
- iOS APNs 实现多语言及自定义事件
- (c#)Winform中的快捷键实现方式
- c语言中的printf实现
- c语言中的printf实现
- c语言中的memcpy实现
- 屏蔽IE的F1帮助快捷键,实现自定义帮助
- 实现自定义对话框程序快捷键的两种方法
- BindKey——实现可自定义的快捷键
- 实现自定义对话框程序快捷键的两种方法
- DOS命令字典
- ASP.NET中Cookie编程的基础知识
- PHP&MYSQL分页原理及实现
- 半小时教你学会正则表达式
- 键盘上所有的键对应的值
- 多语言中的自定义快捷键实现
- iText的简单应用-表格
- 程序员面试宝典---2.1 简历注意事项
- Multithreaded singleton - Design Patterns 学习笔记(5)
- 程序员面试宝典---3.1 笔试
- sql server 日期时间函数
- 如何编译WinPcap
- 程序员面试宝典---3.2 电话面试
- 程序员面试宝典---3.3 面试