您的位置:首页 > 其它

右键弹出菜单和快捷键的设置

2015-04-25 13:53 246 查看
写MFC,少不了用到菜单和快捷键,此处我用到了右键菜单。我的项目是一个播放器的实现,那么在屏幕中央右键点击,应该会弹出右键菜单,比如播放,暂停等,如我这样:



对应的每一个菜单应该还有相应的快捷键。那么具体如何设置呢?此处我省略在资源中添加菜单的步骤了,我的菜单ID为IDR_MENU1。想要实现右键点击播放器区域弹出菜单,需要响应右键按下的消息WM_RBUTTONDOWN,添加事件处理函数如下:

void CMyDlg::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
mypic_video.EnableWindow(FALSE);

CMenu menu;
menu.LoadMenuA(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);//将客户区坐标转换为屏幕坐标

//显示右键菜单,由视类窗口拥有。
pPopup->TrackPopupMenu(nFlags,point.x,point.y,this);
//TrackPopupMenu的最后一个参数,可以为GetParent让父窗口既框架窗口拥有右键菜单,这样
//框架类窗口获得了对右键菜单中菜单项的命令响应,
CDialogEx::OnRButtonDown(nFlags, point);
}


其中值得一说的函数是TrackPopupMenu函数,简单的讲就是它决定了弹出菜单的位置。那么具体的讲呢,此处我引用互动百科上面的解释如下:TrackPopupMenu - 函数功能

该函数在指定位置显示快捷菜单,并跟踪菜单项的选择。快捷菜单可出现在屏幕上的任何位置。

TrackPopupMenu - 函数原型

BOOL TrackPopupMenu(HMENU hMenu,UINT uFlags,int x,int y,int nReserved,HWND hWnd,CONST RECT”prcRect);

TrackPopupMenu - 主要参数

hMenu:被显示的快捷菜单的句柄。此句柄可为调用CreatePopupMenu创建的新快捷菜单的句柄,也可以为调用GetSubMenu取得的与一个已存在菜单项相联系的子菜单的句柄。

uFlags:一种指定功能选项的位标志。用下列标志位之一来确定函数如何水平放置快捷菜单:

TPM_CENTERALLGN:若设置此标志,函数将按参数x指定的坐标水平居中放置快捷菜单。

TPM_LEFTALLGN:若设置此标志,函数使快捷菜单的左边界与由参数X指定的坐标对齐。

TPM_RIGHTALLGN:若设置此标志,函数使快捷菜单的右边界与由参数X指定的坐标对齐。

用下列标志位之一来确定函数如何垂直放置快捷菜单:

TPM_BOTTOMALLGN:若设置此标志,函数使快捷菜单的下边界与由参数y指定的坐标对齐。

TPM_TOPALLGN:若设置此标志,函数使快捷菜单的上边界与由参数y指定的坐标对齐。

TPM_VCENTERALLGN;若设置此标志,函数将按参数y指定的坐标垂直居中放置快捷菜单

用下列标志位之一来确定在菜单没有父窗口的情况下用户的选择:

TPM_NONOTIFY:若设置此标志,当用户单击菜单项时函数不发送通知消息。

TPM_RETURNCMD;若设置此标志;函数将用户所选菜单项的标识符返回到返回值里。

用下列标志位之一来确定在快捷菜单跟踪哪一个鼠标键:

TPM_LEFTBUTTON:若设置此标志,用户只能用鼠标左键选择菜单项。

TPM_RIGHTBUTTON:若设置此标志,用户能用鼠标左、右键选择菜单项。

X:在屏幕坐标下,快捷菜单的水平位置。

Y:在屏幕坐标下,快捷菜单的垂直位置。

NReserved:保留值,必须为零。

HWnd:拥有快捷菜单的窗口的句柄。此窗口接收来自菜单的所有消息。函数返回前,此窗口不接受来自菜单的WM_COMMAND消息。

如果在参数uFlags里指定了TPM_NONOTIFY值,此函数不向hWnd标识的窗口发消息。 但必须给hWnd里传一个窗口句柄,可以是应用程序里的任一个窗口句柄。

PrcRect:未用。

返回值:如果在参数uFlags里指定了TPM_RETURNCMD值,则返回值是用户选择的菜单项的标识符。如果用户未作选择就取消了菜单或发生了错误,则退回值是零。如果没在参数uFlags里指定TPM_RETURNCMD值,若函数调用成功,返回非零值,若函数调用失败,返回零。若想获得更多的错误信息,清调用GetLastError。


至此,运行发现确实能右键弹出菜单。但是我此时又想念起了我的女神,是她陪我度过了最近这段很充实很发愁的时光,于是打开“中国好声音刘明湘.mp4”,顿时播放器里边传来那温柔的声音,没办法,写软件少不了测试,每次都是湘姐来帮我测试。但此时我发现,右键点击,没有任何反应,怎么点都没有反应,我马上点击停止按钮,再右键播放区域,弹出了菜单。这就怪了,为什么播放的时候和不播放的时候是不同的反应呢?分析后只有一种可能,那就是播放的时候,右键的Rbuttondown消息被屏蔽了,或者说父窗口没有接收到Rbuttondown消息。后来在网上终于找到了一个方法,一句代码解决,就是mypic_video.EnableWindow(FALSE);先禁用picture控件,不让它拦截右键down消息。果然在此运行播放湘姐的时候,右键点击,能弹出菜单了!哈哈!

那么播放器中,总是有很多快捷键的设置,比如Space按钮就是播放/暂停,<-和->就是后退和快进,等等。那么我接下来就是设置右键菜单的快捷键。此处我就写详细一点吧,毕竟当时我也是磕磕碰碰了很久。

首先选择资源视图,点击Accelerator,应该是有一个默认的项吧,不记得了,名字可定不是图中的名字,这里要注意,一定要将这个ID改为你准备设置快捷键的总菜单的ID,注意注意!



然后双击进入,为菜单里的每一项添加快捷键,如下所示:


这个是我自己的设置啦,对应上边讲到的右键菜单。

接下来还要添加代码。在对话框类里面添加成员变量HACCEL hAccel; 然后在OnInitDialog函数中添加

hAccel=::LoadAccelerators(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MENU1));,此时最重要的一步来了。需要重写PreTranslateMessage函数,具体方法如下:

在类视图中找到对话框类,然后右键属性,找到重写按钮,往下面找到PreTranslateMessage函数,点击添加。





在该函数里边添加代码:

if (WM_KEYFIRST<=pMsg->message&&pMsg->message<= WM_KEYLAST)

{

HACCEL ihAccel=hAccel;

if (ihAccel && ::TranslateAccelerator(GetSafeHwnd(), ihAccel, pMsg))

return TRUE;
}


至此,完成。运行后快捷键果然成功了。当然前提是,之前需对菜单里的每一个菜单项添加事件响应函数。

那么为什么要这么做呢?PreTranslateMessage函数又是干嘛的?

引用百度百科:类: CWinThread

头文件:

afxwin.h

功能:

若要在消息被分配到Windows函数::TranslateMessage和DispatchMessage之前

过滤Windows消息,应重载这个函数。

语法:

virtual Bool PreTranslateMessage(MSG * pMsg);[1]

参数:

pMsg 指向包含要处理的消息的MSG结构

返回值:

若消息在PreTranslateMessage已经完全处理,不需要作进一步处理,返回非零值。

若消息需要进一步处理,则返回零。

说明:

仅在用户界面线程中使用。

简单的理解就是这个函数具有较高的优先级,它能首先截获一些按键消息之类的,所以在截获后便可以调用自己的函数进行消息响应。

至此,右键菜单的设置和快捷键的设置就完了,当然我的项目中用到的还有其他的快捷键设置,还需要调试。加油!

——————————————————————————————————————————(以下为二次修改内容)

后来,发现好像还有另外一种很方便的方法,即在该菜单或者按钮的caption属性中,添加

&G就可以实现Alt+G键的快捷键,当然可以把G键换成其他的字母键。

除了快捷键,我也听说过热键,那么什么是热键呢?其实好像就是一样的意思,我到现在还没有明白。先不管了,我的重点不是说这个,我的重点是上面的快捷键设置方法只是local的,即当应用程序失去焦点后或者没有被激活前,快捷键是不起作用的。那么与此相对应的就是global的,即任何时候(不管程序有没有获得焦点,或是有没有被激活)都可以响应快捷键。

那么如何设置全局的快捷键?

首先,利用类向导添加消息WM _HOTKEY的响应函数,会在类中自动生成成员函数

afx_msg void OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2);


,同时在BEGIN_MESSAGE_MAP(CMyPlayerDlg, CDialogEx)里也生成衣蛾消息宏映射

ON_WM_HOTKEY()


上面两步不用我们自己操作,类向导已经帮我们做好了,接下来我们可以开始真正的编写设置。

在OnInitDialog中添加注册热键的代码:

//注册热键
int nRet = RegisterHotKey(GetSafeHwnd(),ID_hotkeyftp,MOD_ALT|MOD_CONTROL,'Y'); //热键 ctrl+alt+Y
if(!nRet)
AfxMessageBox("RegisterHotKey 0 false");
nRet = RegisterHotKey(GetSafeHwnd(),ID_hotkeytty,MOD_ALT|MOD_CONTROL,'T'); //热键 ctrl +alt + t
if(!nRet)
AfxMessageBox("RegisterHotKey 1 false");


其中ID_hotkeyftp和ID_hotkeytty是我自己定义的两个宏。可以在头文件中定义:

#define ID_hotkeyftp  1001
#define ID_hotkeytty  1002


关于热键的具体风格可以是如下的组合:(利用’|’进行组合)



然后在生成的void CMyPlayerDlg::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2)函数中编写代码:

void CMyPlayerDlg::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (nHotKeyId==ID_hotkeyftp)
{
//快捷键对应的动作响应函数
OnBnClickedButtonftp();
}
else if (nHotKeyId==ID_hotkeytty)
{
////快捷键对应的动作响应函数
OnBnClickedButtty();
}
else
{
NULL;
}

CDialogEx::OnHotKey(nHotKeyId, nKey1, nKey2);
}


还没有完,还需要在类的析构函数中书写如下代码:

UnregisterHotKey(m_hWnd,ID_hotkeyftp);
UnregisterHotKey(m_hWnd,ID_hotkeytty);


到此设置完毕,运行之后就可以看到效果了,不管有没有获得焦点,按下组合键,都能响应相应的动作。也就是全局的热键。

关于热键和快捷键,或许可以从字面理解,因为热键可以设置成全局的,所以应该是哪些经常用到的操作,所以称之为“热”嘛。哈哈!

拙见,小记!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: