使用CRectTracker类进行对象动态定位

摘要:
使用CRectTracker类动态定位对象。内容提要使用VC、VB、Delphi和其他可视化工具来设计程序。可以使用它们中的大多数来拾取、拖动和动态定位控件。在VC中,我们可以通过CRectTracker类实现这些操作。首先,CrectTRacker类MFC中的CrectTRacker类已被很好地封装,因此我们可以在很少开发的情况下实现上述操作。
  1. 使用CRectTracker类进行对象动态定位
  2. 内容提要
  3. 使用VC,VB,Delphi等可视化工具进行程序设计的时候用的最多的可能就是控件的拾取,拖动以及动态定位了。例如我们可以在VC中用鼠标一次拾取数个控件,然后通过鼠标或者通过左右上下方向键进行控件的微移,从而使控件移到合适的位置,这种技术就是对象动态定位。动态定位也是交互式程序设计中最基本的操作之一。在VC中我们可以通过CRectTracker类来实现这些操作。
  4. 一关于CRectTracker类
  5. MFC中的CrectTRacker类已经进行了很好的封装,从而使得我们只需要很少的开发就可以实现上面的几个操作。对象允许我们在窗口里面移动一个矩形对象或者改变一个矩形对象的大小,它适合与任何的包括OLE在内的应用程序。首先我们了解一下类的成员变量和成员函数.
  6. 1.CRectTracker类的成员变量:
  7. m_nHandleSize:对象的调整句柄的数目,默认情况下为8个
  8. CRectm_rect:对象矩形目前所占大小的位置
  9. m_sizeMin:对象所占的矩形的最小宽度和最小高度
  10. m_nStyle:目前对象边框和调整句柄的类型
  11. 2.CRectTracker类的成员函数
  12. Draw(CDC*pDC):通过调用这个函数来画对象边框和调整句柄的类型。对象边框和调整句柄的具体类型由成员变量m_nStyle决定,一共有以下几种形式:
  13. CRectTracker::solidLine外部边界使用实线形式
  14. CRectTracker::dottedLine外部边界使用点虚线形式
  15. CRectTracker::hatchedBorder外部边界使用带阴影的形式
  16. CRectTracker::resizeInside调整句柄处于对象内部区域
  17. CRectTracker::resizeOutside调整句柄处于对象外部区域
  18. CRectTracker::hatchInside内部所有区域使用带阴影的形式
  19. 需要注意的是
  20. CRectTracker::solidLine和CRectTracker::dottedLine不能同时并存
  21. CRectTracker::resizeInside和CRectTracker::resizeOutside不能同时共存
  22. Track(CWnd*pWnd,CPointpoint,BOOLbAllowInvert=FALSE,CWnd*pWndClipTo=NULL)
  23. 这个函数和下面的TrackRubberBand函数是整个CRectTracker类中最重要的函数,通常在消息WM_LBUTTONDOWN中调用处理,如果鼠标指针落在矩形的边框上,用户就可以拖动以调整矩形的大小;如果鼠标落在了矩形的内部,则用户可以拖动鼠标移动矩形。当ESC键按下时候,函数返回FALSE,函数没有起作用,否则松开鼠标时候,返回TRUE;
  24. TrackRubberBand(CWnd*pWnd,CPointpoint,BOOLbAllowInvert=FALSE,CWnd*pWndClipTo=NULL)
  25. 如果鼠标没有点中对象矩形,那么鼠标移动时候将会产生橡皮条,这个函数就是用来定位”橡皮条”,橡皮条内的项目称之为被选中.
  26. GetTrueRect(LPRECTlpTrueRect):获取对象所占矩形的大小,如果边框形式为CRectTracker::hatchOutside则矩形大小包括外部调整句柄的范围
  27. IntHitTest(Cpointpoint):函数返回光标在CrectTrack类中的位置,通过返回值可以确定拖动句柄的位置
  28. CRectTracker::hitNothing-1: 没有点击任何地方
  29. CRectTracker::hitTopLeft0:点击调整标记的左上角
  30. CRectTracker::hitTopRight1:点击调整标记的右上角
  31. CRectTracker::hitBottomRight2:点击调整标记的右下角
  32. CRectTracker:hitBottomLeft3: 点击调整标记的左下角
  33. CRectTracker:hitTop4:   点击调整标记的上方
  34. CRectTracker:hitRight5: 点击调整标记的右方
  35. CRectTracker:hitBottom6:   点击调整标记的下方
  36. CRectTracker:hitLeft7:   点击调整标记的左方
  37. CRectTracker:hitMiddle8:   点击调整标记的中央
  38. BOOLSetCursor(CWnd*pWnd,UINTnHitTest):当点击特定的位置时改变光标的形状
  39. 二.应用示例
  40. 整个程序界面如下,我们可以通过工具栏来设置图像的边界和调整句柄的类型,同时我们可以用鼠标移动和调整整个图像,另外程序还支持用←↑→↓来微调矩形的位置,通过Shift+←↑→↓来微调矩形的大小.程序的开发步骤如下:
  41. 步骤一:使用VC的MFCAppWizard创建单文档应用程序ExamTracker,创建过程中保留默认值。
  42. 步骤二:在CExamTrackerDoc中增加两个成员变量
  43. CRectTrackerm_tracker;
  44. BOOLm_bAllowInvert;//是否允许逆向拖动,即橡皮条的落点是否小于起点。
  45. 步骤三:在CrectTrckerDemoDoc的构造函数中给m_tracker对象赋初值
  46. m_tracker.m_rect.left=200;
  47. m_tracker.m_rect.top=100;
  48. m_tracker.m_rect.right=301;
  49. m_tracker.m_rect.bottom=201;
  50. //初始边界形式为实线
  51. m_tracker.m_nStyle=CRectTracker::solidLine;
  52. //允许逆向拖动
  53. m_bAllowInvert=TRUE;
  54. 步骤四;在CExamTrackerView中编写函数OnDraw()绘制图像所示的矩形.下面的代码相信大家都看的懂.
  55. voidCExamTrackerView::OnDraw(CDC*pDC)
  56. {
  57. CExamTrackerDoc*pDoc=GetDocument();
  58. ASSERT_VALID(pDoc);
  59. CBrush*pOldBrush=NULL;
  60. TRY
  61. {
  62. //用不同的画刷绘制矩形
  63. CBrushbrush1,brush2;
  64. CRectrect;
  65. intnWidth=pDoc->m_tracker.m_rect.Width();
  66. intnHeight=pDoc->m_tracker.m_rect.Height();
  67. intnSgnX=nWidth!=0?nWidth/abs(nWidth):1;
  68. intnSgnY=nHeight!=0?nHeight/abs(nHeight):1;
  69. pDC->SetTextAlign(TA_CENTER);//设置字体显示方式为中间对齐方式
  70. pDC->SetBkMode(TRANSPARENT);//设置背景色为透明色
  71. intnCenterX,nCenterY;
  72. TEXTMETRICtm;
  73. pDC->GetTextMetrics(&tm);//获取字体大小
  74. brush1.CreateSolidBrush(RGB(255,0,0));
  75. pOldBrush=pDC->SelectObject(&brush1);
  76. //设置第一个图形区域矩形的大小函数为自定义.
  77. SetNormalRect(rect,pDoc->m_tracker.m_rect.left,pDoc->m_tracker.m_rect.top,nWidth/2,nHeight/2);
  78. //绘制矩形
  79. pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY);
  80. nCenterX=rect.left+rect.Width()/2;
  81. nCenterY=rect.top+rect.Height()/2-tm.tmHeight/2;
  82. //在矩形中央显示字符1
  83. pDC->ExtTextOut(nCenterX,nCenterY,ETO_CLIPPED,rect,_T("1"),1,NULL);
  84. brush2.CreateSolidBrush(RGB(0,255,0));
  85. pDC->SelectObject(&brush2);
  86. brush1.DeleteObject();
  87. SetNormalRect(rect,pDoc->m_tracker.m_rect.left+nWidth/2,pDoc->m_tracker.m_rect.top,(nWidth+nSgnX)/2,nHeight/2);
  88. pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY);
  89. nCenterX=rect.left+rect.Width()/2;
  90. nCenterY=rect.top+rect.Height()/2-tm.tmHeight/2;
  91. pDC->ExtTextOut(nCenterX,nCenterY,ETO_CLIPPED,rect,_T("2"),1,NULL);
  92. brush1.CreateSolidBrush(RGB(0,0,255));
  93. pDC->SelectObject(&brush1);
  94. brush2.DeleteObject();
  95. SetNormalRect(rect,pDoc->m_tracker.m_rect.left,pDoc->m_tracker.m_rect.top+nHeight/2,nWidth/2,(nHeight+nSgnY)/2);
  96. pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY);
  97. nCenterX=rect.left+rect.Width()/2;
  98. nCenterY=rect.top+rect.Height()/2-tm.tmHeight/2;
  99. pDC->ExtTextOut(nCenterX,nCenterY,ETO_CLIPPED,rect,_T("3"),1,NULL);
  100. brush2.CreateSolidBrush(RGB(192,192,192));
  101. pDC->SelectObject(&brush2);
  102. brush1.DeleteObject();
  103. SetNormalRect(rect,pDoc->m_tracker.m_rect.left+nWidth/2,pDoc->m_tracker.m_rect.top+nHeight/2,(nWidth+nSgnX)/2,(nHeight+nSgnY)/2);
  104. pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY);
  105. nCenterX=rect.left+rect.Width()/2;
  106. nCenterY=rect.top+rect.Height()/2-tm.tmHeight/2;
  107. pDC->ExtTextOut(nCenterX,nCenterY,ETO_CLIPPED,rect,_T("4"),1,NULL);
  108. //cleanupDC
  109. if(pOldBrush!=NULL)
  110. pDC->SelectObject(pOldBrush);
  111. brush2.DeleteObject();
  112. //这一行代码是必不可少的,通过它可以绘制限定形式的图形区域的外部边界和内部区域.
  113. pDoc->m_tracker.Draw(pDC);
  114. }
  115. CATCH_ALL(e)
  116. {
  117. if(pOldBrush!=NULL)
  118. pDC->SelectObject(pOldBrush);
  119. }
  120. END_CATCH_ALL
  121. }
  122. 在程序中用到了自定义的全局函数
  123. staticvoidSetNormalRect(CRect&rect,intleft,inttop,intwidth,intheight),函数用来设置矩形的大小,同时对矩形进行相应的校正.这种校正一般在逆向拖动时候需要,作用相当于CRect::NormalizeRect().函数定义如下:
  124. staticvoidSetNormalRect(CRect&rect,intleft,inttop,intwidth,intheight)
  125. {
  126. rect.left=left;
  127. rect.top=top;
  128. rect.right=left+width;
  129. rect.bottom=top+height;
  130. intnTemp;
  131. if(rect.left>rect.right)
  132. {
  133. nTemp=rect.left;
  134. rect.left=rect.right;
  135. rect.right=nTemp;
  136. }
  137. if(rect.top>rect.bottom)
  138. {
  139. nTemp=rect.top;
  140. rect.top=rect.bottom;
  141. rect.bottom=nTemp;
  142. }
  143. }
  144. 步骤五:使用AppWizard为CExamTrackerView增加WM_LBUTTONDOWN处理消息,在函数中我们必须处理三种情况;鼠标选中矩形对象但是不处于矩形边界,这种情况仅仅是对矩形进行移动,第二种情况:鼠标处于矩形边界,这时候拖动鼠标将会调整矩形的大小.第三种情况时鼠标没有选中矩形的任何地方,这个时候将会产生橡皮条效果,橡皮条内的所有内容被选中.具体代码如下:
  145. voidCExamTrackerView::OnLButtonDown(UintnFlags,CPointpoint)
  146. {
  147. CExamTrackerDoc*pDoc=GetDocument();
  148. CRectrectSave;
  149. //获取图形区域矩形对象的大小
  150. pDoc->m_tracker.GetTrueRect(rectSave);
  151. //如果没有点中图形,这时候HitTest将返回-1.这时候产生橡皮条.
  152. if(pDoc->m_tracker.HitTest(point)<0)
  153. {
  154. CRectTrackertracker;
  155. //画橡皮擦
  156. if(tracker.TrackRubberBand(this,point,pDoc->m_bAllowInvert))
  157. {
  158. //下面的工作将用来检查橡皮条的矩形是否与图形区域的矩形相交.
  159. CRectrectT;
  160. //对橡皮条的矩形进行校正.
  161. tracker.m_rect.NormalizeRect();//sointersectrectworks
  162. //橡皮条区域与图形区域的交叉区域不为空,则将图形区域的调整句柄进行相应
  163. //的设置
  164. if(rectT.IntersectRect(tracker.m_rect,pDoc->m_tracker.m_rect))
  165. {
  166. //如果调整句柄位于矩形内部(resizeInside)则将调整句柄设置在矩形的外
  167. //部(resizeOutside)
  168. if(pDoc->m_tracker.m_nStyle&CRectTracker::resizeInside)
  169. {
  170. //去除resizeInside形式
  171. pDoc->m_tracker.m_nStyle&=~CRectTracker::resizeInside;
  172. //设置resizeOutside形式
  173. pDoc->m_tracker.m_nStyle|=CRectTracker::resizeOutside;
  174. }
  175. //否则如果调整句柄在外部就将调整句柄放在区域内部
  176. else
  177. {
  178. //justuseinsideresizehandlesonfirsttime
  179. pDoc->m_tracker.m_nStyle&=~CRectTracker::resizeOutside;
  180. pDoc->m_tracker.m_nStyle|=CRectTracker::resizeInside;
  181. }
  182. //更新所有视图,显示调整后的图形
  183. pDoc->UpdateAllViews(NULL,(LPARAM)(LPCRECT)rectSave);
  184. pDoc->UpdateAllViews(NULL);
  185. }
  186. }
  187. }
  188. //如果选中了图形区域,则调用Track函数自动处理,同时处理后更新视图即可.
  189. elseif(pDoc->m_tracker.Track(this,point,pDoc->m_bAllowInvert))
  190. {
  191. //normaltrackingaction,whentrackeris"hit"
  192. pDoc->UpdateAllViews(NULL,(LPARAM)(LPCRECT)rectSave);
  193. pDoc->UpdateAllViews(NULL);
  194. }
  195. CView::OnLButtonDown(nFlags,point);
  196. }
  197. 步骤六:到目前程序的原型基本上已经定下了,可以运行了!运行结果试试看!但是运行中你会发现一个问题,在VC,Delphi中鼠标再不同的区域可以有不同的形状,比如如果选中图形区域鼠标为十字形状,选中边界时成一字形.在程序中我们通过处理WM_SETCURSOR消息来获取这种效果。
  198. BOOLCExamTrackerView::OnSetCursor(CWnd*pWnd,UintnHitTest,Uintmessage)
  199. {
  200. CExamTrackerDoc*pDoc=GetDocument();
  201. if(pWnd==this&&pDoc->m_tracker.SetCursor(this,nHitTest))
  202. returnTRUE;
  203. returnCView::OnSetCursor(pWnd,nHitTest,message);
  204. }
  205. 从上面的代码可以看出函数中仅仅是调用了CRectTracker自己的SetCursor函数,具体的处理过程我们已经不需要做了.
  206. 写到这儿的时候程序已经基本上结束了,用鼠标可以做大部分的事情,剩下的事情就是位置和大小的微调了,具体的微调通过处理WM_KEYDOWN消息来实现.具体的代码如下。
  207. voidCExamTrackerView::OnKeyDown(UintnChar,UintnRepCnt,UintnFlags)
  208. {
  209. CExamTrackerDoc*pDoc=GetDocument();
  210. switch(nChar)
  211. {
  212. //如果按下的是←
  213. caseVK_LEFT:
  214. //按下←的同时按下了Shift键,将图像区域向左减少5个位置
  215. if(::GetKeyState(VK_SHIFT)&0xff00)
  216. {
  217. pDoc->m_tracker.m_rect.right=pDoc->m_tracker.m_rect.right-5;
  218. pDoc->UpdateAllViews(NULL);
  219. }
  220. //否则,图像将向左移动5个位置
  221. else
  222. {
  223. pDoc->m_tracker.m_rect.left=pDoc->m_tracker.m_rect.left-5;
  224. pDoc->m_tracker.m_rect.right=pDoc->m_tracker.m_rect.right-5;
  225. pDoc->UpdateAllViews(NULL);
  226. }
  227. break;
  228. //如果按下的是→caseVK_RIGHT:
  229. //按下→的同时按下了Shift键,将图像区域向右增加5个位置
  230. if(::GetKeyState(VK_SHIFT)&0xff00)
  231. {
  232. pDoc->m_tracker.m_rect.right=pDoc->m_tracker.m_rect.right+5;
  233. pDoc->UpdateAllViews(NULL);
  234. }
  235. //否则,图像将向右移动5个位置
  236. else
  237. {
  238. pDoc->m_tracker.m_rect.left=pDoc->m_tracker.m_rect.left+5;
  239. pDoc->m_tracker.m_rect.right=pDoc->m_tracker.m_rect.right+5;
  240. pDoc->UpdateAllViews(NULL);
  241. }
  242. break;
  243. //如果按下的是↑
  244. caseVK_UP:
  245. //按下↑的同时按下了Shift键,将图像区域向上减少5个位置
  246. if(::GetKeyState(VK_SHIFT)&0xff00)
  247. {
  248. pDoc->m_tracker.m_rect.bottom=pDoc->m_tracker.m_rect.bottom-5;
  249. pDoc->UpdateAllViews(NULL);
  250. }
  251. //否则,图像将向上移动5个位置
  252. else
  253. {
  254. pDoc->m_tracker.m_rect.top=pDoc->m_tracker.m_rect.top-5;
  255. pDoc->m_tracker.m_rect.bottom=pDoc->m_tracker.m_rect.bottom-5;
  256. pDoc->UpdateAllViews(NULL);
  257. }
  258. break;
  259. //如果按下的是↓,图像将向下移动5个位置
  260. caseVK_DOWN:
  261. if(::GetKeyState(VK_SHIFT)&0xff00)
  262. {
  263. pDoc->m_tracker.m_rect.bottom=pDoc->m_tracker.m_rect.bottom+5;
  264. pDoc->UpdateAllViews(NULL);
  265. }
  266. else
  267. {
  268. pDoc->m_tracker.m_rect.top=pDoc->m_tracker.m_rect.top+5;
  269. pDoc->m_tracker.m_rect.bottom=pDoc->m_tracker.m_rect.bottom+5;
  270. pDoc->UpdateAllViews(NULL);
  271. }
  272. break;
  273. }
  274. CView::OnKeyDown(nChar,nRepCnt,nFlags);
  275. }
  276. 所有的任务都完成了,慢,还有通过工具栏改变区域边界的方法,我就提供一个吧,剩下的大家就看源代码吧,应该很简单的,大家都能看的懂,需要注意的是几种形式的不可兼容性,否则会出错,其余的我就不费笔墨和口舌了
  277. voidCExamTrackerDoc::OnSolidline()
  278. {
  279. CRectrectTrue;
  280. m_tracker.GetTrueRect(&rectTrue);
  281. m_tracker.m_nStyle&=~CRectTracker::dottedLine;
  282. m_tracker.m_nStyle^=CRectTracker::solidLine;
  283. UpdateAllViews(NULL,(LPARAM)(LPCRECT)rectTrue);
  284. UpdateAllViews(NULL);
  285. }

免责声明:文章转载自《使用CRectTracker类进行对象动态定位》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇chorme 浏览器记住密码后input黄色背景处理C#多线程(二)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

《QT Creator快速入门》第十章:2D绘制(3)

1、绘制文字 使用QPainter::drawText()来绘制文字: void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); QRectF rect(10.0, 10.0, 580.0, 280.0); painter.setPen(Qt::r...

GdiPlus[57]: 图像(九) IGPBitmap 特有的属性与方法

这包括: //属性 IGPBitmap.Pixels; { 获取或设置指定像素的颜色 } //方法 IGPBitmap.SetResolution(); { 设置分辨率 } IGPBitmap.GetHBitmap(); { 创建 GDI 格式的 Bitmap 并返回句柄 } IGPBitmap.GetHIcon;...

【Ray Tracing The Next Week 超详解】 光线追踪2-7 任意长方体 &amp;amp;&amp;amp; 场景案例

上一篇比较简单,很久才发是因为做了一些好玩的场景,后来发现这一章是专门写场景例子的,所以就安排到了这一篇 Preface 这一篇要介绍的内容有: 1. 自己做的光照例子 2. Cornell box画质问题及优化方案 3. 新的场景几何体——长方体 轴平行长方体 任意长方体 我们这一篇重实践轻理论阐述 ready 1. 需要上一章的知识 但是,上一章的Co...

Unity3d—GUI能量条

1、打开Unity编辑器。2、在脚本文件夹中添加C#脚本,我的是添加了skill_01这个脚本。(要自己设置文件夹,方便管理,不然文件添乱不方便管理) 3、注意,脚本的名字一旦确定就不要去改动,因为一个脚本的名字就是一个类名,改动会容易出现程序的不正确。4、编写对应的脚本代码1 usingSystem.Collections; 2 usingSystem....

android 获取控件位置坐标,屏幕尺寸,标题栏,状态栏高度

1.屏幕尺寸,源代码如下: DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); int screenWidth=metrics.widthPixels; //屏幕宽度 int...

QGraphicsView, QGraphicsObject ,QQGraphicsSvgItem 图片接收鼠标事件 拖拉 收放

由于项目要求,需要加载svg格式图片和pixmap图片,并根据指定坐标在图上进行勾画,并且对相应位置接收鼠标事件。 -继承QGraphicsObject,实现加载pixmap的项 myimageitem.h #ifndef MYIMAGEITEM_H #define MYIMAGEITEM_H #include <QGraphicsObject>...