基于Visual C++ 2010开发Windows7应用-开发多点触控MFC应用程序

来源:互联网 发布:金融炼金术 知乎 编辑:程序博客网 时间:2024/06/11 10:21
当下计算机变得越来越智能化,越来越无所不能,触摸屏的普及只是时间问题了。 虽然鼠标和键盘不会很快就离开人们的视野,毕竟人们使用鼠标跟键盘已经成为一种习惯,但是处理信息或者说操作计算机的其他方法也层出不穷——比如触控技术。从硬件技术的角度来讲,触控技术是可行的,随着最新一代的触摸屏技术,接触技术现在已经存在,如今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];      }  }  

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

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);      }  }