MFC--响应鼠标和键盘操作

  一个程序最重要的部分之一是对鼠标和键盘操作的响应.

  一.  理解鼠标事件.之前对鼠标事件的认识仅仅局限于处理控件的单击与双击事件.但实际鼠标的操作包含很多.这里将以一个画图的小程序讲解对鼠标的响应.

    首先新建一个MFC程序,选择对话框类型,将Mouse设为程序标题.建立程序框架后将对话窗口中所有的控件删除.这样整个对话框都可以用来作图.

    然后选中对话框窗口在右下角属性窗口中的message(消息)选项,会列出一大串的事件消息.例如WM_LBUTONDOWN(鼠标左键被按下),WM_LBUTTONUP(鼠标左键被释放),WM_MOUSEMOVE(鼠标在应用程序窗口空间中移动).画图程序主要通过mousemove事件来实现.选中事件add一个函数.进入函数的实现中加入如下代码.

void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	//检查鼠标左键是否被按下
	if ((nFlags&MK_LBUTTON) == MK_LBUTTON){
		//获取设备上下文
		CClientDC dc(this);
                 //画出像素
		dc.SetPixel(point.x, point.y, RGB(100, 100, 250));
	}
	CDialogEx::OnMouseMove(nFlags, point);
}    

    然后便可以运行程序了,只是这个画图程序与我们平常的并不一样.这个后面再说.

    我们先来看这段代码,有两个参数传递给这个函数.第一个参数是一组标记,用来判断哪个鼠标的按钮被按下,if中的判断前一半是按位与,筛选为便是左键被按下的标记然后与后一半进行匹配;第二个参数是当前鼠标的位置,即鼠标在对话框窗口中的坐标,它包含两个成员:x,y;然后可以使用这个信息在窗口上画一个点.

    在画点之前我们还需要为对话窗口获取设备上下文.即CClientDC dc(this);这条语句.通过为CClientDC类声明一个新的实例来完成,参数this是当前窗口的指针.这个类封装了设备上下文以及大多数可以对其进行的操作,包括所有的屏幕绘制操作.可以这么理解,设备上下文是一块画布,你可以在上面为你的程序作画.

    下一条语句就是画一个点了,并且可以控制点的颜色,颜色控制是RGB的值来控制.

  然后来说一说这个程序的问题,在运行程序的时候会发现如果移动过快就不是一条实线了而是一个个的点,这是程序的性质决定的我们的计算机每隔一段时间检查鼠标的位置然后画一个点,如果移动很慢还能是一条实现,如果过快就变成虚线了.那我们如何解决呢?很简单,在鼠标确定的两点间连上直线.可以按照如下步骤来实现:

    首先向我们的对话框类--CMouseDlg类中添加两个成员变量,m_iPrevX,m_iPrevY,类型为int,属性为private.用来保存上一个点的数据.方便连线.

    然后对上一个函数做点修改.

void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	//检查鼠标左键是否被按下
	if ((nFlags&MK_LBUTTON) == MK_LBUTTON){
		//获取设备上下文
		CClientDC dc(this);
		//从从前一点到当前点画一条线
		dc.MoveTo(m_iPrevX, m_iPrevY);
		dc.LineTo(point.x, point.y);
		//把当前点作为前一点保存
		m_iPrevX = point.x; m_iPrevY = point.y;
	}
	CDialogEx::OnMouseMove(nFlags, point);
}

   dc.MoveTo(m_iPrevX, m_iPrevY);dc.LineTo(point.x, point.y);这两局用来连线,首先需要移动到第一个位置,然后向第二个位置画线.这是非常重要的一步,如果没有这一步,windows将不知道从哪里开始画.这时再运行程序会好一些不再出现虚线,但又有了一个新的问题,每次按下鼠标左键的时候便与刚才最后一个点进行连线.

  现在进行最后的完善,将程序完善成:当鼠标左键被按下时,用当前的位置来初始化上一个位置的位置变量.

    首先在消息模式中为对话框对象的WM_LBUTTONDOWN消息添加一个函数.函数内容如下:

void CMouseDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO:  在此添加消息处理程序代码和/或调用默认值
    m_iPrevX = point.x;
    m_iPrevY = point.y;
    CDialogEx::OnLButtonDown(nFlags, point);
}

    再次运行时程序达到了我们想要的功能.

二.  对键盘的响应.

    获得键盘事件与获得鼠标事件非常相似.但键盘的事件比鼠标事件要少的多.

      WM_KEYDOWN  一个键被按下

      WM_KEYUP    一个键被释放

      WM_SYSKEYDOWN  F10被按下或者Alt与另一个键被同时按下

      WM_SYSKEYUP    F10被释放或者Alt与另一个键被同时释放

    这些事件消息对于对话框窗口对象是可用的,并且只有窗口中没有启用的控件时才会被激发.

  我们为上面的画图程序添加一点功能,当某个键被按下时,改变光标的形状.A改为默认的光标;B改为I型竖线;C改为沙漏型;X退出程序

    首先选择WM_KEYDOWN消息添加一个函数.代码如下:

void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO:  在此添加消息处理程序代码和/或调用默认值
    char cChar;//当前被按下的字符
    HCURSOR hCursor = 0;//显示光标句柄
    HCURSOR hPrevCursor = 0;//以前的光标句柄
    cChar = char(nChar);//将按下的键转换为字符
    if (cChar == 'A'){
        //加载箭头光标
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    }
    if (cChar == 'B'){
        //加载箭头光标
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
    }
    if (cChar == 'C'){
        //加载箭头光标
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
    }    
    if (cChar == 'X'){
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        hPrevCursor = SetCursor(hCursor);
        if (hPrevCursor)
            DestroyCursor(hPrevCursor);
        OnOK();//退出应用
    }
    else{
        if (hCursor){
            hPrevCursor = SetCursor(hCursor);
            if (hPrevCursor)
                DestroyCursor(hPrevCursor);
        }
    }
    CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags);
}

  这个函数有三个参数.第一个nChar是被按下的键,这个是字符的字符代码,在代码的的第一行需要被转化为字符.然后就可直接比较了;第二个参数nRepCnt是这个键被按下的时间.通常被按下就释放,这个值是1.如果一直按下这个键的值会上升,这个值告诉你Windows认为这个键被按下了多少次;第三个参数nFlags是个组合的标记,它可以确定在键被按下的时候是否同时有Alt键被按下,或者被按下的是一个扩展键.

   改光标的过程为:第一步将光标调入内存中,通过LoadStandardCursor(IDC_ARROW)实现.然后这个光标的句柄被传给SetCursor函数,这个函数将光标转换为句柄所对应的光标,并返回前一个光标的句柄.可以使用DestroyCursor(hPrevCursor)销毁前一个光标.

  注意:当鼠标移动过程中光标将切换为默认的箭头.

  AfxGetApp函数:上面的LoadStandardCursor是通过AfxGetApp来调用的,这个函数是一个全局函数,它返回当前应用程序类的一个实例.应用程序类是当前应用程序中CWinApp的子孙类.对我们写的程序来说就是CMouseApp类.当我们需要访问封装在CWinApp类中的功能或当前的派生类时,可以使用AfxGetApp函数得到指向它的指针.对继承派生不熟的回去好好看看c++的书.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GIS讲堂

基于Arcgis for Js的web GIS数据在线采集简介

在前一篇博文“Arcgis for js之WKT和geometry转换”中实现了wkt和geometry之间的相互转化,博文原文地址为:http://blog....

1232
来自专栏老九学堂

超详细的Web 前端知识体系,等你来挑战!

作为苦逼的IT行业,没日没夜的加班,妹纸也少的可怜,就算在一个班办公室或者一个部门,可能也只有一个妹纸,但估计也轮不到你来上,只能眼巴巴的放着光。可是程序猿一直...

4037
来自专栏黑泽君的专栏

粘贴到vi/vim的代码,怎样避免向右不断缩进,保持原来的格式?解决vi/vim在粘贴中会在行首多很多缩进和空格的问题。

SecureCRT会将你原来的文本原封不动的按照字符串的样式发送给服务器。所以当你的服务器上的vi/vim设置为autoindent的话,在 i 模式下,那么它...

3302
来自专栏技术墨客

React学习(最终篇)—— 高阶应用:高阶组件(HOCs)

高阶组件(higher-order components:以下简称HOC或HOC组件)是一个React组件复用的高级技巧。HOCs本身并不是React的API接...

2554
来自专栏奔跑的蛙牛技术博客

ElementRef & TemplateRef & ViewContainerRef

今天在做ng项目,看着前人的代码有 viewChild() 等关键字。新手有点疑惑,索性查查资料总结一下和ng相关的几个和DOM相关的几个概念

2282
来自专栏强仔仔

利用js实现输入框动态提示信息

为了提高和用户的交互性,现在的输入框往往都采用输入信息自动提示的功能,类似于百度输入框中的提示功能。 设计思路是:在输入框input的组件下面放置一个div,这...

6296
来自专栏Debian社区

Web前端知识体系精简

Web前端技术由html、css和javascript三大部分构成,是一个庞大而复杂的技术体系,其复杂程度不低于任何一门后端语言。而我们在学习它的时候往往是先从...

1913
来自专栏有趣的django

微信小程序入门(四)

WXSS(WeiXin Style Sheets)是一套样式语言,用于WXML的组件样式

2552
来自专栏木子墨的前端日常

react-navigation 使用笔记 持续更新中

React-Navigation是目前React-Native官方推荐的导航组件,代替了原用的Navigator。最近开始接触,做个笔记

1204
来自专栏IT开发技术与工作效率

VBA登录抓取网络数据

2384

扫码关注云+社区

领取腾讯云代金券