zoukankan      html  css  js  c++  java
  • 菜鸟学 VB 用API在PictureBox中I划线|平移|放大|缩小 详解and分享

    首先声明下本人也是个小菜鸟,这个功能也是在老大的指点下才实现的,所以在此特此感谢我们老大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

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

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

    呵呵加油!!!!

  • 相关阅读:
    18 个 Java8 日期处理的实践,太有用了!
    IntelliJ IDEA 2019.3安装激活破解使用教程
    低收入人员如何打造自己核心竞争优势
    关于企业网络营销的实战步骤
    关于如何做好微信营销和QQ营销的心得
    百度竞价项目的一些简要说明
    ASO是什么?AppStore搜索规则是什么?
    如何选择APP推广渠道和推广技巧
    浅析Hibernate映射(二)——关系映射(3)
    浅析Hibernate映射(二)——关系映射(4)
  • 原文地址:https://www.cnblogs.com/kingzx/p/2072969.html
Copyright © 2011-2022 走看看