首先声明下本人也是个小菜鸟,这个功能也是在老大的指点下才实现的,所以在此特此感谢我们老大BOSS曾,谢谢你是一位伟大的程序员。

《PS:个人认为第一篇文摘写的很乱,特别乱。有耐心的朋友们可以继续看下去,但是我不保证你会不会出现精神异常哦,呵呵》

写程序最害怕出现什么,个人觉得现在我害怕两个:1是出现若有若无的BUG,这种BUG找起来是相当的麻烦。如果是小项目的话有可能解决那个BUG的时间我基本上都可以从头在写一个项目了。第2个就是项目做到最后发现根本无法结合在一起,就这样死掉了。也就是没有做可行性分析,怎么办从头再来呗。从头再来其实不可怕可怕的是它会影响你的情绪。

很不幸也很幸运这两个让我头疼的问题都在那个小功能中出现了,让我吃足了苦头,不过个人觉得收获更大。

首先简单介绍下我个人,一个培训学校出来的学生才走上不到一个月。以前主学的是.NET,现在从事的是嵌入式。一个高中基本上没学过一点知识从培训学校搞.NET的学生搞嵌入式有多大挑战想必很多大牛们都知道,不过我相信只要肯努力+坚持是没有什么事情做不到的。CSDN上面一位姓肖的老师说过一句话,这句话也会是我今后十年为之奋斗的目标:“从二十岁到三十岁之间这十年,“勤奋”这两个字我背得起”。

呵呵偏题了回到主题,上面提到了我遇到2个自己认为最头疼的问题,那是为什么会遇到呢???问题出在那呢???个人分析了下有下面几点:

1:个人认为是最主要的原因,那就是在解决问题的思维上面出现了问题,可能是习惯了面向对象的编程,也或者也有是自己对面向对象的的理解不够。

下面就是我最初的思路:

首先我是把每个功能分开来实现的(PS:这应该是开发的大忌,没有经过一定的分析就把项目拆分,会对项目留下致命的隐患。希望菜鸟们都引以为戒。)

我首先做的是划线,大家会觉得用API划线应该是很简单的事情吧,不就是应用个API在给API传个几个参数的事情吗?可能是因为我比较笨吧,搞这个功能我也搞了老半天才搞出来。所以为了照顾下和我一样的菜鸟们我也把这个说下。

首先要用到API肯定得添加API呗,你要问我什么是API。老实说我也讲不清楚个人感觉就是MS提供的系统函数吧。

Public Declare Function MoveToEx Lib "gdi32.dll " (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, lpPoint As POINTAPI) As Long
   Public Declare Function LineTo Lib "gdi32.dll " (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

 因为有个POINTAPI类型的数据,所以我们需要自定义一个POINTAPI类型的结构。

Public  Type POINTAPI
        X As Long
        Y As Longs
   End Type

MoveToEx的功能就是一条线的起点,LineTo的功能就是一条线的终点坐标,有终点有起点就可以划线啦。可能有人要问我要话连续划线怎么办呢(就是第一条线的终点坐标是第二条线的起点坐标)???  这个其实MS已经替你想到啦,如果你要连续划线。那么你只需要在所需要连接点的后面跟上一个LineTo把终点坐标穿进去就OK啦。也就是说你化第一条线的终点坐标其实默认是你划第二条线的起点坐标,所以你只需要跟上一个LineTo终点坐标就是OK啦。 如果有人想问那么我不想划连线呢,那也很简单你划线的时候用MoveToEx把起点坐标改变就OK,也就是说把MoveToEx 与 LineTo 成对出现。

对MoveToEx 与 LineTo还需要说明的是,他们只认识像素点,所以提供的参数(也就是说的坐标)只能是像素点。

OK 废话说了那么多了还是看下代码吧。

Dim B As POINTAPI

MoveToEx Picture1.hdc, 100,100, B   '设置线的起点   Picture1.hdc是给API提供划线控件的句柄,如果想直接在窗体上话的话就直接提供窗口的句柄(窗体名).hdc  至于参数B的含意我突然搞忘了,那个高手知道的可以告诉下。呵呵

LineTo Picture1.hdc, 200,200    '线的终点     PS第一条线就画好了。再次提示200,200以及上面的100,100都是像素点参数。

LineTo Picture1.hdc, 400,100    '这就是传说中的连线啦   如果不想练线的话在本条命令之前加个 MoveToEx Picture1.hdc, 300,300,  B      |就OK啦

OK线画好了,接下来就是放大啦,因为是手动输入参数所以得要存放数据,什么东西存放这个最方便呢?答案就是动态的结构体数组啦。

嗯放大应该怎么放大呢?首先我们来分析下有什么不会变的,1肯定PictureBox的物理大小不会变 2我输入的像素值肯定也是不能变的 3像素点也是可能变的。既然这些都不能变那我怎么放大呢,我想啊想啊,最后想到了既然上面的1,2,3都是不能变的,那么我就只能修改传给LineTo的像素值咯,把传入LineTo的像素坐标都乘以同一个数。因为需要动态改变那我就用变量来动态保存咯。

Dim i As Double

i=1.5

MoveToEx Picture1.hdc, 100*i,100*i, B

LineTo Picture1.hdc, 200*i,200*i

上面的代码就是放大的代码咯,那么缩写的代码就不用写了吧,除以i就是咯。

好滴,我还真能干呢把功能完成一大半了,就剩下平移了。

那么平移应该怎么平移呢,我还是想啊想,最后用笔在书上画模拟图,终于还是有思路了,其实和上面放大缩小的思想一样,那1,2,3是不能改变的。我唯一可以改变的就是传入给LineTo的像素值了。他移动多少那么我就加上多少,可能有人会问要是我往后面移呢,还是那句话别担心MS不是你想的那么不完善。

PS:PictureBox控件的ScaleMode有很多属性值,其中3是设置为像素。所以我当时就是默认设置为3的,但是请记住如果你修改了

ScaleHeight    ScaleWidth  ScaleTop   ScaleLeft  这四个属性其中任何属性的值得话,那么ScaleMode都会变成0.

Dim MDownX As Double
Dim MDownY As Double
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

MDownX = X
      MDownY = Y

End Sub

Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

Dim MMoveX As Double
    Dim MMoveY As Double
   
        MUpX = X
        MUpY = Y
   
    MMoveX = MUpX - MDownX     '这里发现了前面提到解决往后移的办法吧
    MMoveY = MUpY - MDownY

'前面提到了我是用动态结构体数组存放的,但是这里为了方便就没有贴出来。所以下面只能的平移只能正确平移一次。

MoveToEx Picture1.hdc, 100+MMoveX,100+MMoveY, B

LineTo Picture1.hdc, 200+MMoveX,200+MMoveY                 
End Sub

终于把功能都做完那,雅玛蝶。OK好的那我把功能都整合起来吧。一整合就发现问题来拉。

首先我是动态结构数组来存放的数据(为了方便解释我们暂且定义为P1吧),对于放大缩小来说没什么关系。可是到平移那里来说说关系就大啦,看看上面的贴代码在注释中为什么平移只能成功一次呢?因为值没有平移后的值没有被存储,嗯这个好说我我在定义个同类型的动态结构体数组来存放每次改变的值呗(这里定义为P2)。真的就那么简单吗????我们再把放大缩小平移结合来看下:放大缩小的要求是输入的值不能改变所以得从P1里面获取到像素点,然后在或乘活除。平移呢?除了第一次平移是从P1里面取值外后面的取值都是从P2里面获取的。这两个一结合那么就发生了逻辑上的错误啊,如果我平移后在放大的话,那么我放大缩小的像素值怎么取,如果取P1的话,打印出来肯定是有问题的,如果我取值P2的话,那么就说明我  我输入的值发生了改变,这是根本不可以的。  当然线放大在平移也会出现问题。  所以这两个功能根本就不可能结合在一起。

这里可能说的不是很清楚,不过不要担心只要你拿笔话下我所说的。你就能明白了。

总结下这次的失败:没有把做到单一职责,平移只能做平移的事情,但是我却让他越位了。我所做的仅仅是为了显示而显示,完全没有考虑到其它问题。就算这种的 项目最后完成了那么后期的维护绝对是一个很大的问题。

还有就是:对项目没有分析就盲目的进行拆分。这是兵家大忌。

虽然失败了我们再来分析下第二个原因:

2:就是我解决问题的思路不对,以前老想着用程序的方式来解决,后来才发现高单片机之所以要求要比WEB高,还有其它的原因,比如说很多时候需要用数学的思维进行分析和解决,并不像面向对象里面的用方法来解决。很多时候我遇到问题首先想到的是调用方法来解决。这或许是纯面向对象的一个缺点吧。把方法都封装好了直接用就可以了。

//注意,前面讲的一堆话可能会让很多人很迷糊,我也注意到了这一点,在最后也是最重要方法上面,努力写的最好,没办法呀第一次写博客,请大家谅解,3Q

我在抛出问题出来,由于API的像素点的坐标原点都是从左上角默认为0,0的,和我们数学中用到的坐标原点左下角为原点,是不一样的,不信你可以在回上面去看下画的线如果传给MoveToEx的值是0,0那么绝对是从左上角出发的。还有一要求就是要求在放大缩小的时候鼠标在PictureBox上点击的X,Y值不能改变。

所以我们现在要解决的有3个问题了一个是变成数学上面的坐标,一个是平移,还有就是点击点X,Y值不能动

我们再来分析下平移。还是上面提到的1,2,3不能变。值可能改变的是传入给LineTo的像素点。

首先我们来解决下如何让左下角为原点的办法(PS:只是改变显示的原点坐标,再次注意API的原点是不可能改变的永远是左上角为原点)

通过上面的这种图,我们可以清楚的看到要改变显示的原点坐标可以通过改变  ScaleHeight    ScaleWidth  ScaleTop   ScaleLeft 这四个属性来设置。

OK,我们再来看下,下面用API划线的代码。

Private Sub Form_Load()
    Picture1.Width = 4335
    Picture1.Height = 4335
    Picture1.ScaleHeight = -50
    Picture1.ScaleTop = 50
    Picture1.ScaleWidth = 50
 
End Sub

Public Sub newDraw(Canvas As Object) 'Canvas是表示传入一个PictureBox控件对象

Canvas.Cls
    Dim k As Double
    Dim bX As Double
    Dim bY As Double
    Dim H As Double
    Dim i As Long
    Dim B As POINTAPI
        
    k = (Canvas.Width / 15) / Canvas.ScaleWidth

bX = -Canvas.ScaleLeft * k
    bY = -(Canvas.ScaleTop + Canvas.ScaleHeight) * k
    H = Canvas.Height / 15    
    
    MoveToEx Canvas.hdc, dataArr(i).X * k + bX, H - (dataArr(i).Y * k + bY), B ’dataArr自定义结构体数组(long X , long Y)
    
    For i = 1 To arrNum - 1  'arrNum表示数组大小s
    
         LineTo Canvas.hdc, dataArr(i).X * k + bX, H - (dataArr(i).Y * k + bY)
   
    Next
   
End Sub

我们首先来看下    (Canvas.Width / 15) / Canvas.ScaleWidth    前面已经提到了只要我们改动了 ScaleHeight    ScaleWidth  ScaleTop   ScaleLeft这任意属性那么,就会默认ScaleMode都会变成0.刻度单位为: 缇(Twip)。下面有详细百科解释,我就不画蛇添足了,(PS:就算你要我画蛇添足我现在也搞不了,呵呵)

bX = -Canvas.ScaleLeft * k  '有了比例在乘以Canvas.ScaleLeft肯定就能得到像素点了。bY,H同理

dataArr(i).X * k + bX  '就是获取当前需要AIP划线的X值   dataArr(i).X * k这个就不解释了。  至于为什么要加上bx呢因为还得加上当前显示坐标的原点X。(这里说的可能比较拗口,请见谅啊俺们现在也是资历有限啊。不过个人人物只要细细品读多看几篇在实践下,应该没多大问题的,个人建议多画图多动手来理解分析)

为什么H 要去减(dataArr(i).Y * k + bY)呢,请再去理解下我前面提到的怎么把显示的原点坐标为左下角。懂多动手很有收货的哦!

这样我们就把左下角作为原点坐标显示做好咯。大家可以去试一试哦,记住’dataArr自定义结构体数组(long X , long Y)是需要你们来写的哦

百科

缇是用来展示空间或定义在纸张,或其他要打印的或在计算机显示器上显示区域上物体的量度。1缇等于1/1440英寸或1/567厘米。也就是说1英寸中有1440缇或1厘米中有567缇。1缇等于传统打印量度点的二十分之一。1点近似等于1/72英寸。

由于我们在屏幕上操作时会习惯用「像素」(也就是我们常说的屏幕分辨率DPI,系统可以设置各种DPI值),所以我们直接输入数字时必须再将「像素」换算成「Twip」。当DPI设置为96时(系统默认值),1个像素=(1/96)*1440=15 Twips;当DPI设置为120时,1个像素=(1/120)*1440=12 Twips。当DPI为96时,如果希望窗体的高是「400」像素,宽是「300」像素,属性的设定值就是: Height=400×15=6000 Twips,Width =300×15=4500 Twips.

  许多计算机程序,如微软的Visual Basic及其Rich-Text格式,需要程序员确定屏幕位置和图像,图标的大小,以缇为度量而不是像素。和像素相同,缇可以在屏幕分辨率改变时调整大小,但是不同于像素的是它表示了打印的绝对值。
  由于不是所有的软件开发工具都使用缇,程序员有时需要在缇和像素之间进行转换。 缇是用来展示空间或定义在纸张,或其他要打印的或在计算机显示器上显示区域上物体的量度。1缇等于1/1440英寸或1/567厘米。也就是说1英寸中有1440缇或1厘米中有567缇。1缇等于传统打印量度点的二十分之一。1点近似等于1/72英寸。

由于我们在屏幕上操作时会习惯用「像素」(也就是我们常说的屏幕分辨率DPI,系统可以设置各种DPI值),所以我们直接输入数字时必须再将「像素」换算成「Twip」。当DPI设置为96时(系统默认值),1个像素=(1/96)*1440=15 Twips;当DPI设置为120时,1个像素=(1/120)*1440=12 Twips。当DPI为96时,如果希望窗体的高是「400」像素,宽是「300」像素,属性的设定值就是: Height=400×15=6000 Twips,Width =300×15=4500 Twips.

  许多计算机程序,如微软的Visual Basic及其Rich-Text格式,需要程序员确定屏幕位置和图像,图标的大小,以缇为度量而不是像素。和像素相同,缇可以在屏幕分辨率改变时调整大小,但是不同于像素的是它表示了打印的绝对值。
  由于不是所有的软件开发工具都使用缇,程序员有时需要在缇和像素之间进行转换。

显示做好了,我们先来看下缩小,注意缩小的要求,鼠标点击初的X,Y值不能改变的哦。

Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

MDownX = X
      MDownY = Y
     DrawAction

End Sub

Private Sub DrawAction(iaNotAction As Integer)
   
    Dim ValX As Double
    Dim ValY As Double
    
    ValX = MDownX
    ValY = MDownY

MDownY = MDownY - (Picture1.ScaleTop + Picture1.ScaleHeight)
    MDownX = MDownX - Picture1.ScaleLeft
    bY = MDownY / (-Picture1.ScaleHeight)
    bX = MDownX / Picture1.ScaleWidth

Picture1.ScaleWidth = Picture1.ScaleWidth * 1.1
   Picture1.ScaleHeight = Picture1.ScaleHeight * 1.1

Picture1.ScaleTop = Picture1.ScaleWidth * (1 - bY) + ValY  
    Picture1.ScaleLeft = Picture1.ScaleHeight * bX + ValX

newDraw Picture1
End Sub

想必大家都看出来了  DrawAction函数的作用就是重新算出Picture1.ScaleTop与    Picture1.ScaleLeft的值  来重新确定显示原点的坐标。

说先原理吧,我的思路就是把点击的点按照比例来确定的,我缩小前比例是多少,那么放大后在乘以那个比例不就OK了吗?

试一试是不是每次点击后点击点的X,Y是不是没有变呢? s缩小与放大是一个原理,我这就不一一列出了。请大家多动手去实践那样收货真的会更多。

还剩下最后的平移了,OK我们继续。

其实平移更简单,值需要算出所需要移动的距离就可以了,这里就不做解释了。

Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

MDownX = X
      MDownY = Y
   
End Sub

Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

Dim MMoveX As Double
    Dim MMoveY As Double
   
    MUpX = X
    MUpY = Y
   
    MMoveX = MUpX - MDownX
    MMoveY = MUpY - MDownY

DrawMove MMoveX, MMoveY
             
End Sub

Private Sub DrawMove(X As Double, Y As Double)

Picture1.ScaleTop = Picture1.ScaleTop - Y
    Picture1.ScaleLeft = Picture1.ScaleLeft - X
    Call newDraw(Me.Picture1)

End Sub

Public Sub newDraw(Canvas As Object)

Canvas.Cls
    Dim k As Double
    Dim bX As Double
    Dim bY As Double
    Dim H As Double
   
    Dim i As Long
    Dim B As POINTAPI
    
    k = (Canvas.Width / 15) / Canvas.ScaleWidth
    bX = -Canvas.ScaleLeft * k
    bY = -(Canvas.ScaleTop + Canvas.ScaleHeight) * k
    H = Canvas.Height / 15    
    
    MoveToEx Canvas.hdc, dataArr(i).X * k + bX, H - (dataArr(i).Y * k + bY), B
   
    For i = 1 To arrNum - 1
   
         LineTo Canvas.hdc, dataArr(i).X * k + bX, H - (dataArr(i).Y * k + bY)
   
    Next
   
End Sub

基本上功能都完成了,现在来看下这种方式是不是要比以前的老办法方便多了呢???

写后感:看着博客园里面的大鸟们的博客写的那么好一气呵成,里面充满了幽默与智慧,心里很深激动自己也想写博客。所以就开了这个博客啊,终于也写了篇自创的文章。个人感觉写的一塌糊涂,没有中心思想,逻辑思维很乱,本来想把思路写细可是真的写细后发现很多东西根本不是自己能够掌控的,很多思路自己只懂得皮毛完全没有理解或者是当时自己完全没有想到的,给自己也给大家照成了很多的费劲,再次向大家说声对不起,同时也很感谢你看到这里,因为这也是对我的一定肯定吧,还有很长的路要走加油。就像拼凑而来的,绕来绕去的。如何自己的写的再长一点,把自己都绕晕咯,或者在拓展了基本上更乱哦。这好比就像做开发没有做到  高内聚低耦合  也没做到分层描述。哎,自己很菜还是很菜,需要努力啊,做到老大说的,坚持+勤奋。希望下次出品的时候会大不一样,也对大家说声迟到的粽子节快乐吧,朋友们下次见!!!

呵呵加油!!!!

转载于:https://www.cnblogs.com/kingzx/archive/2011/06/06/2072969.html

菜鸟学 VB 用API在PictureBox中I划线|平移|放大|缩小 详解and分享相关推荐

  1. php eof bof,VB中Recordset之BOF与EOF详解

    VB中Recordset之BOF与EOF详解 2018-08-31 09:29阅读: id_999 单身,寻另一半!! 关注 BOF:指当前记录位置位于Recordset对象的第一个记录之前: EOF ...

  2. Python中的__name__和__main__含义详解

    背景 在写Python代码和看Python代码时,我们常常可以看到这样的代码: ? 1 2 3 4 5 def main():     ...... if __name__ == "__ma ...

  3. 教程-Delphi中Spcomm使用属性及用法详解

    Delphi中Spcomm使用属性及用法详解 Delphi是一种具有 功能强大.简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选 ...

  4. python怎么横着输出_对python3中, print横向输出的方法详解

    对python3中, print横向输出的方法详解 Python 2 : print打印的时候,如果结尾有逗号,打出来时候不会换行.但是在python3里面就不行了. Python3: 3.0的pri ...

  5. android pcm 音量_Android中实时获取音量分贝值详解

    基础知识 度量声音强度,大家最熟悉的单位就是分贝(decibel,缩写为dB).这是一个无纲量的相对单位,计算公式如下: 分子是测量值的声压,分母是参考值的声压(20微帕,人类所能听到的最小声压).因 ...

  6. php读音量大小,Android_Android中实时获取音量分贝值详解,基础知识 度量声音强度,大 - phpStudy...

    Android中实时获取音量分贝值详解 基础知识 度量声音强度,大家最熟悉的单位就是分贝(decibel,缩写为dB).这是一个无纲量的相对单位,计算公式如下: 分子是测量值的声压,分母是参考值的声压 ...

  7. go语言字符串换行_Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号(" ...

  8. python argv 详解_对python中的argv和argc使用详解

    主要问题 为什么argv中第一个,即index=0的内容就是文件名? python中argc是用什么实现的? 概念解释 argc:argument counter,命令行参数个数 argv:argum ...

  9. yii mysql 事务处理_Yii2中事务的使用实例代码详解

    前言 一般我们做业务逻辑,都不会仅仅关联一个数据表,所以,会面临事务问题. 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全 ...

  10. python时间函数报错_python3中datetime库,time库以及pandas中的时间函数区别与详解...

    1介绍datetime库之前 我们先比较下time库和datetime库的区别 先说下time 在 Python 文档里,time是归类在Generic Operating System Servic ...

最新文章

  1. WebView 上传文件 WebChromeClient之openFileChooser函数
  2. 游戏开发者怎么做出以假乱真的画面效果?大气散射渲染了解一下
  3. 【Python生成器与迭代器的区别】
  4. uwsgi搭配nginx
  5. HTML+CSS+JS实现 贪吃蛇游戏源码
  6. 10-5-展示后台数据
  7. 洛谷——P1320 压缩技术(续集版)
  8. Win10 64位+VS2015+OpenCV3.4.2重编译
  9. 操作系统课程设计 Kernel编译和WinDbg启动 批处理文件
  10. Tomcat环境部署以及tomcat多实例搭建(同一台机器)
  11. vue3.0 音频插件(vue-aplayer)
  12. 使用WePE对无法启动的系统文件进行操作
  13. 小米8到小米10一键刷机教程,快速获取Root权限下载安装twrp,magisk教程
  14. 基于CC2430的Zigbee的第一个实验
  15. 四大行业晋身今秋求贤大户 游戏人才缺口奇大
  16. 客运综合管理系统项目解析-安全检查(模块)-车辆安检情况查询
  17. JAVA日志框架与日志系统
  18. LVC 播放器buffer自定义读写的方法
  19. 关于微星主板官网下载网卡驱动后,网络适配器消失的问题
  20. 互联网思维——简约思维

热门文章

  1. ACL2020论文:使用强化学习为机器翻译生成对抗样本
  2. 基于汉语短文本对话的立场检测系统理论与实践
  3. 入门 | 神经网络词嵌入:如何将《战争与和平》表示成一个向量?
  4. 【每日算法Day 90】5种方法:求解数组中出现次数超过一半的那个数
  5. 神经网络中的激活函数
  6. tf.concat用法总结
  7. 数据挖掘是如何解决问题的
  8. 测试有道:微软测试技术心得 1
  9. 嵌入式系统开发快速体验
  10. 2.3 Hadoop安装与体验