开发多点触控MFC应用程序

当下计算机变得越来越智能化,越来越无所不能,触摸屏的普及只是时间问题了。 虽然鼠标和键盘不会很快就离开人们的视野,毕竟人们使用鼠标跟键盘已经成为一种习惯,但是处理信息或者说操作计算机的其他方法也层出不穷——比如触控技术。从硬件技术的角度来讲,触控技术是可行的,随着最新一代的触摸屏技术,接触技术现在已经存在,如今Web开发人员可以利用Silverlight 3提供多点触摸功能。可惜的是,只有Windows 7同时支持Silverlight和多点触摸能力。这个因素大大制约了这部分功能的运用和推广,不过如果多点触摸继续流行开来的话,情况会有所改变的,不过现在Windows 7为触摸屏技术提供了发挥的软件空间,Windows 7让屏幕触控技术成为可能。

  借助 Windows 7 和触摸感应屏幕,您只需使用手指即可在电脑上翻阅在线报纸,翻阅相册,拖拽文件和文件夹。 多年来在 Windows 中早已开始采用触控技术。Windows 7 进一步将其扩展到电脑的每个角落。“开始”菜单和任务栏现在都采用了加大显示、易于手指触摸的图标。常用的 Windows 7 程序也都可以执行触摸操作。您甚至可以在“画图”中使用手指来画图!

  Windows 触控功能还可以识别多点触控(使用适当的监视器)。是否需要缩小图片将手指捏在一起即可。是否要用鼠标右键单击某项内容?用一个手指触及该内容,并用第二个手指点击屏幕即可。Windows 触控功能仅在 Windows 7 的家庭高级版、专业版和旗舰版中提供。

  什么是触控操作呢?触控指 Windows 允许你使用手指直接与计算机进行交互的方式。与使用鼠标、键盘或手写笔相比,触摸更加方便、自然、具有吸引力。也更符合人们日常的交流习惯。而Windows 7中引入了全新的多点触摸的概念。多点触摸又称多点触控,简而言之可以理解为一个屏幕多点操作。多点触摸不但是两个点或者几个点同时应用到屏幕上这么一点点便利,由于是多点触摸,所以他能感应到手指滑动的快慢以及力度(力度用触摸点的多少转换来实现),从而操作系统应用起来更加人性化。传统的触控屏幕一次只能判断一个触控点,若同时有两个以上的点被触碰,就不能做出正确反应,或者说反应混乱了。多重触控的任务可以分解为两个方面的工作,一是同时采集多点信号,二是对每路信号的意义进行判断,也就是所谓的手势识别。最早在Apple公司的iPhone上应用。多点触摸技术是一项划时代的输入方式。可以设想随着全息投影的发展,完全有可能实现屏幕在空中投影,而用户直接在投影中触控电脑,科幻电影中的场景将会变成现实。


      下面我们来看一段关于用手玩转win7触摸屏多点触摸屏电脑,现在您是不是被这种绚丽界面所吸引,被这种便捷而有趣的操作所震撼?
      我们自己打造的程序里面如何使用多点触摸技术呢,就是让我们的程序也具有此种多点触摸功能,用手就可以玩转我们的应用程序呢?
      好了,不多说了,下面我用实例来讲解这些功能实现:

首先:我们基于MFC新建一个简单窗体工程,如下图所示:

 单击"finish"完成工程创建

接下来我们向应用程序添加触控支持,表现以下两点:
1.我们正在构建的应用程序需要支持触控的硬件,因此我们需要在应用程序中查看这一点。

2.在 Scratchpad.cpp 中,在 CScratchPadApp::InitInstance(): 后添加以下检查代码:

BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);  
if ((digitizerStatus & (0x80 + 0x40)) == 0)   
//堆栈就绪+多触点  
{  
AfxMessageBox(L"No touch input is currently available.");  return false;  }  BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);  
CString str;  str.Format(L"Touch input available with %d touch points.", nInputs);  AfxMessageBox(str);  return true;  

3. 您可以看到,除了查看触控可用性和就绪情况以外,我们还可以发现硬件支持的触控输入数量。

4. 编译并运行。

5. 根据机器上触控输入的数量,您应该看到类似下图的输出:

6.为了注册应用程序客户端视图窗口来接收触控消息,我们需要调用 MFC 函数 CWnd::RegisterTouchWindow()。我们将在视图创建之后执行该操作,即在 OnCreate() 事件处理程序中完成。

切换到 Class View 并选择 CChildView 类。

在 Properties 页面中,转到 Message 属性表并导航到 WM_CREATE,然后从下拉框中添加 OnCreate() 消息处理程序:
 
7.  在 CChildView::OnCreate() 处理程序中添加以下代码,注册视图窗口的触控输入:

if (!RegisterTouchWindow())  {  ASSERT(FALSE);  }  

注意:调用 CWnd::RegisterTouchWindow() 注册(和注销)窗口,使其具有触控功能,允许接收低级 WM_TOUCH 消息。

8. 因为我们注册了视图来接收触控输入,我们必须重写接收每个触控消息的处理程序:CWnd::OnTouchInput()。
该处理程序接收来自 Windows Touch 的单个输入,并在应用程序处理该消息时返回 TRUE;否则返回 FALSE。

9.在 ChildView.h 中添加该方法声明:

// 重写  
protected:  virtual BOOL OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput);  

10.在 ChildView.cpp 中提供相应的实现:

BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput)  
{       
// 在此处输入消息处理Tocuh  return false;  
}  

然后我们向项目添加笔画源和头文件,并使用手指绘制线条

我们希望将手指作为多输入设备。我们希望为每个触摸屏幕的手指绘制一条线。为此,我们将使用两个笔画集合。一个集合保存完成的笔画(线条),另一个集合保存正在绘制的线条。触摸屏幕的每个手指都指向 m_StrkColDrawing 集合中的笔画。当我们从屏幕拿开手指时,我们将手指的笔画从m_StrkColDrawing 移动到 m_StrkColFinished 集合。此外,如果用户在多点触控监视器上使用两个以上的手指,我们希望笔画有不同的颜色。

1.  在 Starter 文件夹中,您将找到两个文件:Stroke.h 和 Stroke.cpp。将它们复制到项目文件夹下并使用“Add Existing item…”将其添加到项目中。
2. 类似地,向项目添加 StrokeCollection.h 和 StrokeCollection.cpp。
3. 将 "Stroke.h" 和 "StrokeCollection.h" 放在 StdAfx.h 头文件结束处。

#include "Stroke.h"
#include "StrokeCollection.h"

4.  将这些私有成员变量定义添加到 ChildView.h 中:

private:  int m_iCurrColor;                    // 当前笔触的颜色  CStrokeCollection m_StrkColFinished; // 用户完成输入笔画,接触笔触焦点  CStrokeCollection m_StrkColDrawing;  // 收集用户当前的绘图笔画   

5.重要:我们必须初始化当前颜色。我们将在 ChildView.cpp 的 CChildView 构造函数中完成该操作:

CChildView::CChildView() :m_iCurrColor(0)  
{  }  

6. 要绘制完成的集合,我们将以下调用添加到 CChildView::OnPaint() 处理程序的末尾。它将绘制所有已完成的笔画。

m_StrkColFinished.Draw(&dc);

7. 我们需要处理所有接收到的触控输入消息,因此我们处理感兴趣的所有事件:touch input down、move 和 up。

8. 在 CChildView.h 中,声明以下方法,我们将用来处理不同的触控输入事件:

protected:
// 不同触摸输入事件的处理程序  
BOOL OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput);  
BOOL OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput);  
BOOL OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput);  

9.在 CChildView.cpp 中,添加每个触控输入处理程序的实现:

BOOL CChildView::OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput)  
{  // 创建新的画笔,并为他添加指针  COLORREF strokeColor = GetTouchColor((pInput->dwFlags & TOUCHEVENTF_PRIMARY) != 0);    CStroke* pStrkNew = new CStroke(pInput->dwID, strokeColor);  pStrkNew->Add(pt);  // 添加新的笔触收集画板中的笔画  m_StrkColDrawing.Add(pStrkNew);      return true;  
}  BOOL CChildView::OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput)  
{  // 在绘图查找笔画收集笔触。  int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID);  if (strokeIndex >= 0)  {  CStroke* pStrk =  m_StrkColDrawing[strokeIndex];  // 增加笔画触摸点  pStrk->Add(pt);  // 绘制最后的笔画  pStrk->Draw(GetDC());  }  return true;  
}  BOOL CChildView::OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput)  
{  // 在绘图查找笔画收集笔触.  int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID);  if (strokeIndex >= 0) {  CStroke* pStrkCopy = m_StrkColDrawing[strokeIndex];  // 从绘图上移除笔画.  m_StrkColDrawing.RemoveAt(strokeIndex);  // 在已经完成的笔画中增加一画.  m_StrkColFinished.Add(pStrkCopy);  }  return true;  
}  

10.在 CChildView.cpp 中,修改 CChildView::OnTouchInput() 的实现,以根据需要调用每个触控输入处理程序:

BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput)  
{       if ((pInput->dwFlags & TOUCHEVENTF_DOWN) == TOUCHEVENTF_DOWN) // 触摸按下事件  {  return OnTouchInputDown(pt, pInput);  }  else if ((pInput->dwFlags & TOUCHEVENTF_MOVE) == TOUCHEVENTF_MOVE) // 触摸移动事件  {  return OnTouchInputMove(pt, pInput);  }  else if ((pInput->dwFlags & TOUCHEVENTF_UP) == TOUCHEVENTF_UP) // 触摸移动事件  {  return OnTouchInputUp(pt, pInput);  }  return false;  
}  

11.注意,调用了 GetTouchColor() 方法,但它尚未实现。当用户移动应用程序窗口上的多个手指时,该方法负责处理钢笔的颜色。在 CChildView.h 中添加该方法的声明

private:
COLORREF GetTouchColor(bool bPrimaryContact);

12.以下是 CChildView.cpp 的实现:

COLORREF CChildView::GetTouchColor(bool bPrimaryContact)  
{  
static COLORREF c_arrColor[] =  // 数组中的颜色  {  
RGB(255, 0, 0),             // 红  RGB(0, 255, 0),             // 绿  RGB(0, 0, 255),             // 蓝  RGB(0, 255, 255),           // 青  RGB(255, 0, 255),           // 品红  RGB(255, 255, 0)            // 黄  };  
COLORREF color;  
if (bPrimaryContact)  {  
// 主要接触中绘制黑色.  
color = RGB(0,0,0);         // 黑  }  
else  {  
// 保存当前的次要颜色.  
color = c_arrColor[m_iCurrColor];    
// 移动到数组中的下一个颜色  
m_iCurrColor = (m_iCurrColor + 1) % (sizeof(c_arrColor)/sizeof(c_arrColor[0]));  }  
return color;  
}  

13.最后,由于我们已经动态创建了许多笔画,我们需要确保每个笔画在应用程序退出之前都被销毁,因此我们在 CChildView‘ 的析构函数中包含以下内容:

CChildView::~CChildView()  
{  for (int i = 0; i < m_StrkColDrawing.GetCount(); ++i)  {  delete m_StrkColDrawing[i];  }  for (int i = 0; i < m_StrkColFinished.GetCount(); ++i)  {  delete m_StrkColFinished[i];  }  
}  

4.现在编码部分已经全部完成,可以开始实验刚才实现的应用程序了。

15.编译并运行应用程序。它应该如下所示:
==============================stdafx.h=====================

#pragma once  
#include "afxtempl.h"  #include <vector>  
using namespace std;  class CStroke : public CArray<POINT, POINT>  
{  
public:  CStroke(int id, COLORREF clr);  ~CStroke(void);  COLORREF GetColor() const { return m_clr; }  int GetId() const { return m_id; }  void Draw(CDC* pDC) const;  private:  COLORREF m_clr;       // Stroke color  int m_id;             // Stroke ID  
};  

==============================stdafx.cpp=====================

#include "StdAfx.h"  
#include "Stroke.h"  CStroke::CStroke(int id, COLORREF color):  m_clr(color),  m_id(id)  
{  SetSize(0, 1000);  
}  void CStroke::Draw(CDC* pDC) const  
{  if (GetCount() <= 0)  return;  CPen pen(PS_SOLID, 3, m_clr);  CPen* oldPen = pDC->SelectObject(&pen);  pDC->MoveTo(GetAt(0));  pDC->Polyline(GetData(), GetCount());  pDC->SelectObject(oldPen);  
}  CStroke::~CStroke(void)  
{  
}  

==============================StrokeCollection.h=====================

#pragma once  
#include "afxtempl.h"  
#include "Stroke.h"  class CStrokeCollection : public CArray<CStroke*, CStroke*>  
{  
public:  CStrokeCollection(void);  ~CStrokeCollection(void);  // Search the collection for given ID.  int FindStrokeById(int id) const;  // Draw the collection of the strokes.  void Draw(CDC* pDC) const;  };  

==============================StrokeCollection.h=====================

#include "StdAfx.h"  
#include "StrokeCollection.h"  CStrokeCollection::CStrokeCollection(void)  
{  
}  CStrokeCollection::~CStrokeCollection(void)  
{  
}  int CStrokeCollection::FindStrokeById(int id) const  
{  for (int i = 0; i < GetCount(); i++)  {  if (GetAt(i)->GetId() == id)  {  return i;  }  }  return -1;  
}  
void CStrokeCollection::Draw(CDC* pDC) const  
{  for (int i = 0; i < GetCount(); ++i)  {  GetAt(i)->Draw(pDC);  }  
}  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/64424.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用springboot+elasticserach7的demo,对比sider和百度ai的异同

对比aigc引擎&#xff1a;sider chatgpt3.5和百度ai 提示词&#xff1a; springboot2.5&#xff0c;连接elasticsearch7的demo&#xff0c;要有基本的操作&#xff0c;用模板方法 以下是一个使用Spring Boot 2.5连接Elasticsearch 7的示例代码&#xff0c;包括基本的操作方法…

springboot配置ym管理各种日记(log)

1&#xff1a;yml配置mybatis_plus默认日记框架 mybatis-plus:#这个作用是扫描xml文件生效可以和mapper接口文件使用&#xff0c;#如果不加这个,就无法使用xml里面的sql语句#启动类加了MapperScan是扫描指定包下mapper接口生效&#xff0c;如果不用MapperScan可以在每一个mapp…

docker常用中间件安装

文章目录 1、前言2、中间件安装2.1、mysql2.2、gitlab容器2.3、nacos2.4、redis2.5、xxljob2.6、zipkin2.7、sentinel2.8、seata2.8.1、获取镜像2.8.2、运行容器并获取配置 2.9、rockerMQ2.9.1、rockerMQ-namesrv2.9.2、rockerMQ-broker2.9.3、rockerMQ-console 2.10、jenkins2…

git status搜索.c和.h后缀及git新建分支

git status搜索.c和.h后缀及git新建分支 1.脚本代码2.git新建分支(1)创建新分支(2)删除本地分支(3)删除远端分支(4)合并分支3.指定历史版本创建分支1.脚本代码 $ git status | grep "\.[hc]$"$ 是行尾的意思 \b 就是用在你匹配整个单词的时候。 如果不是整个…

docker的数据卷、docker数据持久化

目录 前言docker数据持久化的2种方式数据卷 bind mount &#xff0c;即-v参数匿名数据卷 docker manager volume-v参数和匿名卷的区别docker volume 命令的使用数据卷容器孤儿volume总结 前言 环境&#xff1a;centos7.9 docker version 20.10.14 本篇我们来介绍docker的数据卷…

每日一题(链表中倒数第k个节点)

每日一题&#xff08;链表中倒数第k个节点&#xff09; 链表中倒数第k个结点_牛客网 (nowcoder.com) 思路: 如下图所示&#xff1a;此题仍然定义两个指针&#xff0c;fast指针和slow指针&#xff0c;假设链表的长度是5&#xff0c;k是3&#xff0c;那么倒数第3个节点就是值为…

【进阶篇】MySQL分库分表详解

文章目录 0. 前言1. 垂直分库分表2. 水平分库分表 1. 理解过程及实现方案问题讨论衍生出分库分表策略借助成熟组件使用分库分表阶段完成后面临的问题1. 异地多活问题2. 数据迁移问题3. 分布式事务问题4. join查询的问题 分库分表的策略实现示例 2. 参考文档 0. 前言 假设有一个…

windows笔记本远程连接如何打开任务管理器?

参考素材&#xff1a; https://jingyan.baidu.com/article/8275fc86a97f5207a03cf6cd.html https://www.anyviewer.cn/how-to/ctrl-alt-delete-remote-desktop-6540.html 网上查了很多方法&#xff0c;都说ctrlaltend可以解决这个问题。 但是笔记本键盘上没有end键。 继续查了一…

【数学建模】清风数模正课5 相关性分析

相关系数 相关性分析的关键是计算相关系数&#xff0c;在本节课中将会介绍两种常用的相关系数&#xff1a;皮尔逊相关系数&#xff08;Pearson&#xff09;和斯皮尔曼相关系数&#xff08;Spearman&#xff09;。 它们可以用来衡量两个变量间相关性的大小&#xff0c;对于不同…

Unity——脚本序列化

在介绍序列化之前&#xff0c;我们先来了解一下为什么要对数据进行序列化 数据序列化有以下几个主要的应用场景和目的&#xff1a; 1. 持久化存储&#xff1a;序列化可以将对象或数据结构转换为字节序列&#xff0c;使得其可以被存储在磁盘上或数据库中。通过序列化&#xff…

Android 13 - Media框架(9)- NuPlayer::Decoder

这一节我们将了解 NuPlayer::Decoder&#xff0c;学习如何将 MediaCodec wrap 成一个强大的 Decoder。这一节会提前讲到 MediaCodec 相关的内容&#xff0c;如果看不大懂可以先跳过此篇。原先觉得 Decoder 部分简单&#xff0c;越读越发现自己的无知&#xff0c;Android 源码真…

SQL sever命名规范

目录 一、标识符 二、表名&#xff08;Table&#xff09;: 三、字段名&#xff08;fields&#xff09;&#xff1a; 四、约束&#xff08;Constraint&#xff09;&#xff1a; 五、索引&#xff08;Index&#xff09;&#xff1a; 六、存储过程&#xff08;Stored Proced…

安卓 tcp 客户端

安卓 tcp 客户端 Server:8888 是Qt 写的Tcp 服务器 ip 是 192.168.2.103 port是8888 安卓手机运行 kotlin 语法的Tcp Client &#xff0c;连接&#xff0c;收发数据 效果如下图 Tcpclient package com.example.myapplicationimport android.os.Handler import android.os.Loo…

Debezium系列之:Debezium Server在生产环境大规模应用详细的技术方案

Debezium系列之:Debezium Server在生产环境大规模应用详细的技术方案 一、需求背景二、Debezium Server实现技术三、技术方案流程四、生成接入配置五、新增数据库接入和删除数据库接入效果六、监控zookeeper节点程序七、新增数据库接入部署debezium server程序八、删除数据库接…

Go操作各大消息队列教程(RabbitMQ、Kafka)

Go操作各大消息队列教程 1 RabbitMQ 1.1 概念 ①基本名词 当前市面上mq的产品很多&#xff0c;比如RabbitMQ、Kafka、ActiveMQ、ZeroMQ和阿里巴巴捐献给Apache的RocketMQ。甚至连redis这种NoSQL都支持MQ的功能。 Broker&#xff1a;表示消息队列服务实体Virtual Host&#x…

Java中的InetAddress类

InetAddress类 概念&#xff1a;InetAddress类是 Java 中用于表示 IP 地址的类。它提供了一种标准的方法来处理 IP 地址&#xff0c;无论是 IPv4 还是 IPv6 地址。InetAddress 类位于 java.net 包中&#xff0c;是 Java 网络编程的一部分。 常用方法&#xff1a; getLocalHost…

深入探讨梯度下降:优化机器学习的关键步骤(一)

文章目录 &#x1f340;引言&#x1f340;什么是梯度下降&#xff1f;&#x1f340;损失函数&#x1f340;梯度(gradient)&#x1f340;梯度下降的工作原理&#x1f340;梯度下降的变种&#x1f340;随机梯度下降&#xff08;SGD&#xff09;&#x1f340;批量梯度下降&#xf…

HTML5+CSS3+JS小实例:科技感满满的鼠标移动推开粒子特效

实例:科技感满满的鼠标移动推开粒子特效 技术栈:HTML+CSS+JS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport&qu…

回归拟合 | 灰狼算法优化核极限学习机(GWO-KELM)MATLAB实现

这周有粉丝私信想让我出一期GWO-KELM的文章&#xff0c;因此乘着今天休息就更新了(希望不算晚) 作者在前面的文章中介绍了ELM和KELM的原理及其实现&#xff0c;ELM具有训练速度快、复杂度低、克服了传统梯度算法的局部极小、过拟合和学习率的选择不合适等优点&#xff0c;而KEL…

HFSS 3维曲线导入

HFSS 3维曲线导入 简介环境参考代码使用结果 简介 如图一所示&#xff0c;CST中可以通过导入和到出由任意点组成的曲线&#xff0c;但是HFSS中貌似不能导入&#xff08;如图二所示&#xff09;&#xff0c;如果我们要将matlab的产生的曲线的点的数据导入特变麻烦&#xff0c;特…