Run Time用Mouse移动Line物件
来源:cww

这个技术是属於Region的技术,请先查Mouse是否处於不规则区域内
请先在Form中放数个Line物件,Form_Load时,会看看有多少个Line物件,而依其所在的
座标产生一狭长的条形Region,所以当Mouse移过去时便能得知是否处於某个Region中
我不知道外面的绘图软体是不是也用这种方式来做,不过我们至少在VB也初步完成了

Option Explicit
Private Type POINTAPI
	X As Long
	Y As Long
End Type
Private Declare Function CreatePolygonRgn Lib "gdi32" (lpPoint As POINTAPI, ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long
Private Declare Function PtInRegion Lib "gdi32" (ByVal hRgn As Long, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function Rectangle Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long

Private hreg1 As Long, hreg2 As Long '选取的Line Objec其端点会有两个小正方形
				     '记录这两个正方形的handle of Region
Private haveSel As Boolean '目前是否有Line Object被选取
Private inReg1 As Boolean  '是否在hreg1 的范围
Private inReg2 As Boolean  '是否在hreg2 的范围
Private oldPoint As POINTAPI '记录选取到Line Object时的Mouse座标
Private lp1 As POINTAPI, lp2 As POINTAPI
Private aLine As Line
Private NotRefresh As Boolean
Private PI As Double
Private Sub Form_Load()
Dim ctl As Control, i As Long
Me.ScaleMode = 3
Me.DrawStyle = 0
Me.DrawMode = 13
Me.FillColor = &H808000
Me.FillStyle = 0
Me.ForeColor = &H8000000E

PI = 4 * Atn(1)
For Each ctl In Me
   If TypeOf ctl Is Line Then
      Set aLine = ctl
      Call SetRegion
      i = i + 1
  End If
Next

End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim i As Long, j As Long
Dim hRegion5 As Long
Dim ctl As Control
If Button = 1 Then
   NotRefresh = True
   Me.Refresh '如果先前有画上小框框,於此将之去除
   haveSel = False
   For Each ctl In Me
       If TypeOf ctl Is Line Then
	  hRegion5 = CLng(ctl.Tag)
	  i = PtInRegion(hRegion5, X, Y)
	  If i <> 0 Then
	     Set aLine = ctl
	     Exit For
	  End If
       End If
   Next
   If i <> 0 Then
      oldPoint.X = X
      oldPoint.Y = Y
      Call SetSelect
      haveSel = True
   Else
      haveSel = False
   End If
End If
'虽上面已Check Mouse是否处於某个 line的Region内,但是Line处於Select状态时,
'有画上两个小方框,这两个小方框未必在Region之内,所以User在方框处按Mouse也算有选取
i = PtInRegion(hreg1, X, Y)
j = PtInRegion(hreg2, X, Y)
inReg1 = False: inReg2 = False
If i <> 0 Or j <> 0 Then
   haveSel = True
   'Mouse down时mouse是否处於hreg1/ hreg2, 若是则影响Mouse move时Line的移动
   If i <> 0 Then inReg1 = True
   If j <> 0 Then inReg2 = True
End If
End Sub
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim i As Long, j As Long
If haveSel Then
   i = PtInRegion(hreg1, X, Y)
   j = PtInRegion(hreg2, X, Y)
End If
If Button = 0 Then
   If i <> 0 Or j <> 0 Then 'Mouse在选取的两个方框内时改变Mouse的形状
      Screen.MousePointer = 2
   Else
      Screen.MousePointer = 0
   End If
Else
   If Button = 1 Then
      If haveSel Then
	 Call MoveLine(X, Y)
      End If
   End If
End If
End Sub

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = 1 Then
   If haveSel Then '重新设定Line物件的hRegion范围
      Call SetSelect
      Call SetRegion
   End If
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
Dim ctl As Control
Dim hRegion5 As Long
For Each ctl In Me
    If TypeOf ctl Is Line Then
       hRegion5 = Val(ctl.Tag)
       DeleteObject hRegion5
    End If
Next
DeleteObject hreg1
DeleteObject hreg2
End Sub
Private Sub Form_Resize()
If Me.WindowState = vbNormal Or Me.WindowState = vbMaximized Then
   If haveSel Then
      DoEvents '等Form show出来
      Call SetSelect '重画小方框
   End If
End If
End Sub

'设定Line物件的hRegion
Private Sub SetRegion()
Dim hregion As Long
Dim pt(3) As POINTAPI
Dim n As Long, dx As Long, dy As Long
Dim sida As Double

hregion = Val(aLine.Tag)
DeleteObject hregion
n = 8
With aLine
    dx = .X2 - .X1
    dy = .Y2 - .Y1
End With
If dx <> 0 Then
   sida = Atn(dy / dx)
Else
   sida = PI / 2
End If
With aLine
  pt(0).X = CLng(.X2 + n * Sin(sida))
  pt(0).Y = CLng(.Y2 + n * Cos(sida))
  pt(1).X = CLng(.X2 - n * Sin(sida))
  pt(1).Y = CLng(.Y2 - n * Cos(sida))
  pt(2).X = CLng(.X1 - n * Sin(sida))
  pt(2).Y = CLng(.Y1 - n * Cos(sida))
  pt(3).X = CLng(.X1 + n * Sin(sida))
  pt(3).Y = CLng(.Y1 + n * Cos(sida))
End With
hregion = CreatePolygonRgn(pt(0), 4, 1)
aLine.Tag = Str(hregion) '将hRegion记录在line.Tag
End Sub
'设定被选取的 line物件两个端点的hRegion与画上两个方框
Private Sub SetSelect()
      With aLine
	Call Rectangle(Me.hdc, .X1 - 3, .Y1 - 3, .X1 + 3, .Y1 + 3)
	Call Rectangle(Me.hdc, .X2 - 3, .Y2 - 3, .X2 + 3, .Y2 + 3)
	DeleteObject hreg1
	DeleteObject hreg2
	hreg1 = CreateRectRgn(.X1 - 3, .Y1 - 3, .X1 + 3, .Y1 + 3)
	hreg2 = CreateRectRgn(.X2 - 3, .Y2 - 3, .X2 + 3, .Y2 + 3)
	lp1.X = .X1
	lp1.Y = .Y1
	lp2.X = .X2
	lp2.Y = .Y2
      End With

End Sub
Private Sub MoveLine(ByVal X As Single, ByVal Y As Single)
   Dim dx As Long, dy As Long
   If NotRefresh Then
      Me.Refresh '去除画上的两个小方框
      NotRefresh = False
   End If
   dx = X - oldPoint.X
   dy = Y - oldPoint.Y
   If inReg1 Then 'in hreg1 则(x2, y2)不动,只改(x1, y1)
      With aLine
	.X1 = X
	.Y1 = Y
      End With
   Else
     If inReg2 Then 'in hreg2 则(x1, y1)不动,只改(x2, y2)
	With aLine
	  .X2 = X
	  .Y2 = Y
	End With
     Else   '不在hreg1, hreg2中,所以是整条线移动
	With aLine
	  .X1 = lp1.X + dx
	  .Y1 = lp1.Y + dy
	  .X2 = lp2.X + dx
	  .Y2 = lp2.Y + dy
	End With
     End If
   End If
End Sub