dcsimg
Results 1 to 23 of 23

Thread: Moving a PictureBox Inside a PictureBox

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Moving a PictureBox Inside a PictureBox

    Hello,

    I'm trying to move a PictureBox inside another PictureBox
    and color PictureBox2 to indicate whether the mouse is over it or not.

    My code below kind of works.
    Problem 1: only allows movement in Right direction and Down direction
    Problem 2: after PictureBox2 is moved, bOver is always TRUE

    Any ideas as how to correct?

    Code:
    Option Explicit
    
    
    Private Declare Function SetCapture Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function ReleaseCapture Lib "user32" () As Long
    
    
    Private Sub Form_Load()
    
        Form1.ScaleMode = vbPixels
        
        Picture1.Appearance = 0
        Picture1.ScaleMode = vbPixels
        
        Picture2.Appearance = 0
        Picture2.BackColor = vbRed
        Picture2.ScaleMode = vbPixels
        Picture2.Left = 10
        Picture2.Top = 50
        
    End Sub
    
    
    Private Sub Picture2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Static bOver As Boolean
    
        If (X < 0) Or (Y < 0) Or (X > Picture2.ScaleWidth) Or (Y > Picture2.ScaleHeight) Then
            Call ReleaseCapture
            bOver = False
            Picture2.BackColor = vbRed
        Else
            If bOver = False Then
                Call SetCapture(Picture2.hwnd)
                bOver = True
                Picture2.BackColor = vbGreen
            End If
        End If
    
        If Button = vbLeftButton Then
            Picture2.Top = Picture2.Top + Y
            Picture2.Left = Picture2.Left + X
        End If
        
    End Sub

  2. #2
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    284

    Re: Moving a PictureBox Inside a PictureBox

    Code:
    Option Explicit
    
    Private Declare Function ReleaseCapture Lib "user32.dll" () As Long
    Private Declare Function SendMessageW Lib "user32.dll" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    
    Private Sub Form_Load()
        Form1.ScaleMode = vbPixels
    
        Picture1.Appearance = 0
        Picture1.ScaleMode = vbPixels
    
        Picture2.Appearance = 0
        Picture2.BackColor = vbRed
        Picture2.ScaleMode = vbPixels
        Picture2.Move 10!, 50!
    End Sub
    
    Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Picture2.BackColor = vbRed
    End Sub
    
    Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Const WM_NCLBUTTONDOWN = &HA1&, HTCAPTION = 2&
    
        ReleaseCapture
        SendMessageW Picture2.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&
    End Sub
    
    Private Sub Picture2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Picture2.BackColor = vbGreen
    End Sub
    Last edited by Victor Bravo VI; Nov 19th, 2019 at 02:28 PM. Reason: Changed ReleaseCapture from Sub to Function

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Thank you.
    That does work perfect...
    and it has the added bonus of not letting the small PictureBox move outside the bounds of the larger one.

    I don't understand the magic behind the ReleaseCapture and SendMessageW
    (I don't see any X values)

    I want the small PictureBox to move left and right only within the confines of the larger PictureBox

    Starting over, here is my code:

    I can move the small PictureBox left or right, but if I hit either edge, the small PictureBox will no longer move.

    Any ideas on how to correct this?

    Code:
    Option Explicit
    
    
    Private Sub Form_Load()
    
        Form1.ScaleMode = vbPixels
        
        Picture1.Appearance = 0
        Picture1.ScaleMode = vbPixels
        Picture1.Width = 200
        Picture1.Height = 50
        
        Picture2.Appearance = 0
        Picture2.BackColor = vbRed
        Picture2.ScaleMode = vbPixels
        Picture2.Width = 8
        Picture2.Height = 8
        Picture2.Left = 10
        Picture2.Top = 21
        
    End Sub
    
    
    Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Picture2.BackColor = vbRed
        
    End Sub
    
    
    Private Sub Picture2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        Picture2.BackColor = vbGreen
    
        If Button = vbLeftButton Then
            If (Picture2.Left + Picture2.Width < Picture1.Width - 2) And _
                    (Picture2.Left + Picture2.Width > Picture2.Width) Then
                Picture2.Left = Picture2.Left + X
            End If
        End If
        
        Form1.Caption = "Picture2 left edge = " & Picture2.Left
        
    End Sub

  4. #4
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    284

    Re: Moving a PictureBox Inside a PictureBox

    Quote Originally Posted by mms_ View Post
    I don't understand the magic behind the ReleaseCapture and SendMessageW
    (I don't see any X values)
    ReleaseCapture will make the OS think you are not pressing any mouse button on Picture2 anymore even though you still are. Sending WM_NCLBUTTONDOWN + HTCAPTION while in that unusual state will simulate dragging the window's title bar (and thus effectively moving the entire window itself). The PictureBox control (or any other child window for that matter) doesn't visually have a title bar, but from the point of view of the OS, it does have one. All windows, regardless of whether they are a top-level window or a child window, always have both client and nonclient areas. For top-level windows, the nonclient area (title bar, window border, menu bar, scroll bars, etc.) is typically visible, while for child windows, the client area usually has the same size as its nonclient area. In other words, the nonclient area has zero width along the left & right edges and zero height along the top & bottom edges of the window. That's why you don't see it. One exception to this is when a child window displays either or both of its scroll bars; in that case, the client area will be smaller than the nonclient area.

    Quote Originally Posted by mms_ View Post
    I want the small PictureBox to move left and right only within the confines of the larger PictureBox
    Well, you should have mentioned that requirement in your first post. Are there any other requirements we should know about?

    Quote Originally Posted by mms_ View Post
    Any ideas on how to correct this?
    Play with this example and see if you can adapt it to your actual program:

    Code:
    Option Explicit 'In a blank Form
    
    Private WithEvents Picture1 As VB.PictureBox
    Private WithEvents Picture2 As VB.PictureBox
    
    Private m_StartX            As Single
    
    Private Sub Form_Activate()
        Const PICTURE2_WIDTH = 100!, PICTURE2_HEIGHT = 100!
    
        Picture2.Move (Picture1.ScaleWidth - PICTURE2_WIDTH!) \ 2&, _
                      (Picture1.ScaleHeight - PICTURE2_HEIGHT) \ 2&, _
                      PICTURE2_WIDTH, _
                      PICTURE2_HEIGHT
        Picture2.Cls
    End Sub
    
    Private Sub Form_Load()
        ScaleMode = vbPixels
    
        Set Picture1 = Controls.Add("VB.PictureBox", "Picture1")
        Set Picture2 = Controls.Add("VB.PictureBox", "Picture2", Picture1)
    
        Picture1.Appearance = 0
        Picture1.AutoRedraw = True
        Picture1.ScaleMode = vbPixels
        Picture1.Visible = True
    
        Picture2.Appearance = 0
        Picture2.AutoRedraw = True
        Picture2.BackColor = vbRed
        Picture2.ScaleMode = vbPixels
        Picture2.Visible = True
    End Sub
    
    Private Sub Form_Resize()
        Const GAP = 10!
    
        On Error Resume Next
        Picture1.Move ScaleLeft + GAP, ScaleTop + GAP, ScaleWidth - GAP - GAP, ScaleHeight - GAP - GAP
        Picture1.Cls
        Picture1.Print CStr(Picture1.ScaleWidth)
    End Sub
    
    Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Picture2.BackColor = vbRed
    End Sub
    
    Private Sub Picture1_Resize()
        Dim Picture2_Left As Single, Picture2_Right As Single
    
        Picture2_Left = Picture2.Left
        Picture2_Right = Picture2_Left + Picture2.Width
    
        If Picture2_Right > Picture1.ScaleWidth Then
            Picture2_Left = Picture1.ScaleWidth - Picture2.Width
        End If
    
        If Picture2_Left < Picture1.ScaleLeft Then
            Picture2_Left = Picture1.ScaleLeft
        End If
    
        Picture2.Move Picture2_Left, (Picture1.ScaleHeight - Picture2.Height) * 0.5!
        PrintGaps
    End Sub
    
    Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then
            m_StartX = X
            Caption = X & " - " & m_StartX & " = " & (X - m_StartX)
        End If
    End Sub
    
    Private Sub Picture2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim Picture2_Left As Single, Picture2_Right As Single
    
        Picture2.BackColor = vbGreen
    
        If Button = vbLeftButton Then
            Picture2_Left = Picture2.Left + (X - m_StartX)
            Picture2_Right = Picture2_Left + Picture2.Width
    
            If Picture2_Right > Picture1.ScaleWidth Then
                Picture2_Left = Picture1.ScaleWidth - Picture2.Width
            End If
    
            If Picture2_Left < Picture1.ScaleLeft Then
                Picture2_Left = Picture1.ScaleLeft
            End If
    
            Picture2.Left = Picture2_Left
        End If
    
        Caption = X & " - " & m_StartX & " = " & (X - m_StartX)
        PrintGaps
    End Sub
    
    Private Sub PrintGaps()
        Dim sLeftGap As String, sRightGap As String
    
        sLeftGap = "<--" & (Picture2.Left - Picture1.ScaleLeft)
        sRightGap = (Picture1.ScaleWidth - (Picture2.Left + Picture2.Width)) & "-->"
        Picture2.Cls
    
        Picture2.CurrentY = (Picture2.ScaleHeight - Picture2.TextHeight(sLeftGap)) * 0.5!
        Picture2.Print sLeftGap;
    
        Picture2.CurrentX = Picture2.ScaleWidth - Picture2.TextWidth(sRightGap)
        Picture2.Print sRightGap;
    End Sub

  5. #5

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    OK thanks Victor Bravo VI

    Well, you should have mentioned that requirement in your first post. Are there any other requirements we should know about?
    I was trying to make question as simple as possible, and thought I could figure the rest out on my own... I guess not

    So, what I am really trying to do here, is have two small PictureBoxes inside a larger one.
    These two boxes represent a Left side marker and a Right side marker.
    These markers are movable, and are each user adjustable.
    They define print limits for printing, and are positioned over a scrollable landscape view, that the user drops objects on.

    The following code demonstrates what I am trying to do, and is 100% based on your methodology.

    I have a few bugs:
    1.
    I have it set up so Left marker can never cross the right marker and visa versa.
    If one hits the other, the moving one then pushes the other.
    This works for right to left always.
    Works left to right if done slowly; if done quickly the left marker sometimes passes thru the right marker.

    2.
    If scrolling with thumb bar, left & right markers drawn incorrectly (2 or 3 copies of each)

    3.
    I have it set up so left marker cannot move past left side of PictureBox if HScroll value is HScroll = Min,
    and right side marker cannot scroll past right side of PictureBox if HScroll value = Max
    This works OK
    I do want the markers to be able to be moved beyond the container limits at all other HScroll values
    because user might want it to be set the right marker to the very end (or some other far distance)
    and this takes forever as currently set up
    -> move the marker -> advance the scroll -> move the marker -> advance the scroll -> etc.
    Similar problem to the SelectionBox in MSPaint

    I have been working on correcting these problems, but so far, no luck.

    Any further help I could get always appreciated.

    Code:
    Option Explicit 'In a blank Form
    
    Private WithEvents Picture1 As VB.PictureBox
    Private WithEvents Picture2 As VB.PictureBox
    Private WithEvents Picture3 As VB.PictureBox
    Private WithEvents Picture4 As VB.PictureBox
    Private WithEvents HScroll1 As VB.HScrollBar
    
    Private m_StartX            As Single
    
    Private Const colWidth      As Long = 10
    Private Const numCols       As Long = 10000
    
    Private oldHScrollVal       As Long
    Private i                   As Long
    
    Private Sub Form_Load()
        ScaleMode = vbPixels
    
        Set Picture1 = Controls.Add("VB.PictureBox", "Picture1")
        Set Picture2 = Controls.Add("VB.PictureBox", "Picture2", Picture1)
        Set Picture3 = Controls.Add("VB.PictureBox", "Picture3", Picture1)
        Set Picture4 = Controls.Add("VB.PictureBox", "Picture4")
        Set HScroll1 = Controls.Add("VB.HScrollBar", "HScroll1")
        
        Picture1.Appearance = 0
        Picture1.AutoRedraw = True
        Picture1.ScaleMode = vbPixels
        Picture1.Visible = True
    
        Picture2.Appearance = 0
        Picture2.AutoRedraw = True
        Picture2.BackColor = vbRed
        Picture2.ScaleMode = vbPixels
        Picture2.Visible = True
        
        Picture3.Appearance = 0
        Picture3.AutoRedraw = True
        Picture3.BackColor = vbBlue
        Picture3.ScaleMode = vbPixels
        Picture3.Visible = True
        
        Picture4.Appearance = 0
        Picture4.AutoRedraw = True
        Picture4.BackColor = vbWhite
        Picture4.ScaleMode = vbPixels
        Picture4.Visible = True
        
        HScroll1.Max = numCols
        HScroll1.LargeChange = 50
        HScroll1.Visible = True
    End Sub
    
    Private Sub Form_Resize()
        Const GAP = 10!
    
        On Error Resume Next
        Picture1.Move ScaleLeft + GAP, ScaleTop + GAP, ScaleWidth - GAP - GAP, ScaleHeight - GAP - 160
        Picture1.Cls
        
        Picture4.Move ScaleLeft + GAP, Picture1.Top + Picture1.Height + 1, ScaleWidth - GAP - GAP, ScaleHeight - GAP - 60
        Picture4.Cls
        
        HScroll1.Move Picture4.Left, ScaleHeight - 18 - GAP, Picture4.Width, 18
    End Sub
    
    Private Sub Form_Activate()
        Const PICTURE2_WIDTH = 10!, PICTURE2_HEIGHT = 20!
    
        Picture2.Move (Picture1.ScaleWidth - PICTURE2_WIDTH!) \ 2&, _
                      (Picture1.ScaleHeight - PICTURE2_HEIGHT) \ 2&, _
                      PICTURE2_WIDTH, _
                      PICTURE2_HEIGHT
        Picture2.Cls
        
        Picture3.Move Picture2.Left + Picture2.Width + 5, _
                      Picture2.Top, _
                      PICTURE2_WIDTH, _
                      PICTURE2_HEIGHT
        Picture3.Cls
    
        For i = 1 To numCols
            Picture4.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, Picture4.Height), vbBlack
        Next i
        Picture4.Refresh
        
        oldHScrollVal = HScroll1.Value
    End Sub
    
    Private Sub Picture1_Resize()
        Dim Picture2_Left As Single, Picture2_Right As Single
    
        Picture2_Left = Picture2.Left
        Picture2_Right = Picture2_Left + Picture2.Width
    
        If Picture2_Right > Picture1.ScaleWidth Then
            Picture2_Left = Picture1.ScaleWidth - Picture2.Width
        End If
    
        If Picture2_Left < Picture1.ScaleLeft Then
            Picture2_Left = Picture1.ScaleLeft
        End If
    
        Picture2.Move Picture2_Left, (Picture1.ScaleHeight - Picture2.Height) * 0.5!
    
        Dim Picture3_Left As Single, Picture3_Right As Single
    
        Picture3_Left = Picture3.Left
        Picture3_Right = Picture3_Left + Picture3.Width
    
        If Picture3_Right > Picture1.ScaleWidth Then
            Picture3_Left = Picture1.ScaleWidth - Picture3.Width
        End If
    
        If Picture3_Left < Picture1.ScaleLeft Then
            Picture3_Left = Picture1.ScaleLeft
        End If
    
        Picture3.Move Picture3_Left, (Picture1.ScaleHeight - Picture3.Height) * 0.5!
    End Sub
    
    Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Picture2.BackColor = vbRed
        Picture3.BackColor = vbBlue
    End Sub
    
    Private Sub Picture2_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then
            m_StartX = X
        End If
    End Sub
    
    Private Sub Picture2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim Picture2_Left As Single, Picture2_Right As Single
    
        Picture2.BackColor = vbGreen
        Picture3.BackColor = vbBlue
    
        If Button = vbLeftButton Then
            Picture2_Left = Picture2.Left + (X - m_StartX)
            Picture2_Right = Picture2_Left + Picture2.Width
    
            If Picture2_Left < Picture1.ScaleLeft And HScroll1.Value = 0 Then
                Picture2_Left = Picture1.ScaleLeft
            End If
    
            Picture2.Left = Picture2_Left
            
            If Picture2_Right > Picture3.Left Then
                Picture3.Left = Picture3.Left + 1
            End If
            
        End If
    End Sub
    
    Private Sub Picture3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then
            m_StartX = X
        End If
    End Sub
    
    Private Sub Picture3_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim Picture3_Left As Single, Picture3_Right As Single
        
        Picture3.BackColor = vbGreen
        Picture2.BackColor = vbRed
    
        If Button = vbLeftButton Then
            Picture3_Left = Picture3.Left + (X - m_StartX)
            Picture3_Right = Picture3_Left + Picture3.Width
    
            If Picture3_Right > Picture1.ScaleWidth And HScroll1.Value = HScroll1.Max Then
                Picture3_Left = Picture1.ScaleWidth - Picture3.Width
            End If
    
            Picture3.Left = Picture3_Left
    
             If Picture3_Left < Picture2.Left + Picture2.Width Then
                Picture2.Left = Picture3.Left - Picture2.Width + 1
            End If
    
        End If
    End Sub
    
    Private Sub HScroll1_Change()
        Picture4.Cls
        
        For i = 1 To numCols
            Picture4.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, Picture4.Height), vbBlack
        Next i
      
        Form1.Caption = HScroll1.Value & "(LM=" & Picture2.Left & ")" & "(RM=" & Picture3.Left & ")"
    
        Picture2.Left = Picture2.Left - HScroll1.Value + oldHScrollVal
        Picture3.Left = Picture3.Left - HScroll1.Value + oldHScrollVal
        
        oldHScrollVal = HScroll1.Value
    End Sub
    
    Private Sub HScroll1_Scroll()
        Call HScroll1_Change
    End Sub
    I had to remove all your debug code (ie PrintGaps, etc) as it didn't fit inside my new (smaller) picture boxes.
    It was very helpful though, in making me understand exactly what you were doing with your calculations.
    Last edited by mms_; Nov 20th, 2019 at 04:47 PM.

  6. #6

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    So I managed to get the auto scroll feature implemented (item 3)

    The basics are all working.

    The main bug is criss-crossing of left and right markers when auto scroll is executed.
    Left (red) marker must always be left of right (blue) marker.

    Help would still be appreciated.

    Code:
    Option Explicit 'In a blank Form
    
    Private WithEvents PictureC As VB.PictureBox    'Container
    Private WithEvents PictureL As VB.PictureBox    'Left Marker
    Private WithEvents PictureR As VB.PictureBox    'Right Marker
    Private WithEvents PictureU As VB.PictureBox    'User
    Private WithEvents HScroll1 As VB.HScrollBar
    Private WithEvents TimerL   As VB.Timer
    Private WithEvents TimerR   As VB.Timer
    
    Private m_StartX            As Single
    
    Private Const colWidth      As Long = 10
    Private Const numCols       As Long = 10000
    
    Private oldHScrollVal       As Long
    Private i                   As Long
    
    Private Sub Form_Load()
        ScaleMode = vbPixels
    
        Set PictureC = Controls.Add("VB.PictureBox", "PictureC")
        Set PictureL = Controls.Add("VB.PictureBox", "PictureL", PictureC)
        Set PictureR = Controls.Add("VB.PictureBox", "PictureR", PictureC)
        Set PictureU = Controls.Add("VB.PictureBox", "PictureU")
        Set HScroll1 = Controls.Add("VB.HScrollBar", "HScroll1")
        Set TimerL = Controls.Add("VB.Timer", "TimerL")
        Set TimerR = Controls.Add("VB.Timer", "TimerR")
        
        PictureC.Appearance = 0
        PictureC.AutoRedraw = True
        PictureC.ScaleMode = vbPixels
        PictureC.Visible = True
    
        PictureL.Appearance = 0
        PictureL.AutoRedraw = True
        PictureL.BackColor = vbRed
        PictureL.ScaleMode = vbPixels
        PictureL.Visible = True
        
        PictureR.Appearance = 0
        PictureR.AutoRedraw = True
        PictureR.BackColor = vbBlue
        PictureR.ScaleMode = vbPixels
        PictureR.Visible = True
        
        PictureU.Appearance = 0
        PictureU.AutoRedraw = True
        PictureU.BackColor = vbWhite
        PictureU.ScaleMode = vbPixels
        PictureU.Visible = True
        
        HScroll1.Max = numCols
        HScroll1.LargeChange = 50
        HScroll1.Visible = True
        
        TimerL.Enabled = False
        TimerL.Interval = 1
        
        TimerR.Enabled = False
        TimerR.Interval = 1
    End Sub
    
    Private Sub Form_Resize()
        Const GAP = 10!
    
        On Error Resume Next
        PictureC.Move ScaleLeft + GAP, ScaleTop + GAP, ScaleWidth - GAP - GAP, ScaleHeight - GAP - 160
        PictureC.Cls
        
        PictureU.Move ScaleLeft + GAP, PictureC.Top + PictureC.Height + 1, ScaleWidth - GAP - GAP, ScaleHeight - GAP - 60
        PictureU.Cls
        
        HScroll1.Move PictureU.Left, ScaleHeight - 18 - GAP, PictureU.Width, 18
    End Sub
    
    Private Sub Form_Activate()
        Const PictureL_WIDTH = 10!, PictureL_HEIGHT = 20!
    
        PictureL.Move (PictureC.ScaleWidth - PictureL_WIDTH!) \ 2&, _
                      (PictureC.ScaleHeight - PictureL_HEIGHT) \ 2&, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
        PictureL.Cls
        
        PictureR.Move PictureL.Left + PictureL.Width + 5, _
                      PictureL.Top, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
        PictureR.Cls
    
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
        PictureU.Refresh
        
        oldHScrollVal = HScroll1.Value
    End Sub
    
    Private Sub PictureC_Resize()
        Dim PictureL_Left As Single, PictureL_Right As Single
    
        PictureL_Left = PictureL.Left
        PictureL_Right = PictureL_Left + PictureL.Width
    
        If PictureL_Right > PictureC.ScaleWidth Then
            PictureL_Left = PictureC.ScaleWidth - PictureL.Width
        End If
    
        If PictureL_Left < PictureC.ScaleLeft Then
            PictureL_Left = PictureC.ScaleLeft
        End If
    
        PictureL.Move PictureL_Left, (PictureC.ScaleHeight - PictureL.Height) * 0.5!
    
        Dim PictureR_Left As Single, PictureR_Right As Single
    
        PictureR_Left = PictureR.Left
        PictureR_Right = PictureR_Left + PictureR.Width
    
        If PictureR_Right > PictureC.ScaleWidth Then
            PictureR_Left = PictureC.ScaleWidth - PictureR.Width
        End If
    
        If PictureR_Left < PictureC.ScaleLeft Then
            PictureR_Left = PictureC.ScaleLeft
        End If
    
        PictureR.Move PictureR_Left, (PictureC.ScaleHeight - PictureR.Height) * 0.5!
    End Sub
    
    Private Sub PictureC_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        PictureL.BackColor = vbRed
        PictureR.BackColor = vbBlue
    End Sub
    
    Private Sub PictureL_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then
            m_StartX = X
        End If
    End Sub
    
    Private Sub PictureL_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim PictureL_Left As Single, PictureL_Right As Single
    
        PictureL.BackColor = vbGreen
        PictureR.BackColor = vbBlue
    
        If Button = vbLeftButton Then
            PictureL_Left = PictureL.Left + (X - m_StartX)
            PictureL_Right = PictureL_Left + PictureL.Width
    
            If PictureL_Left < PictureC.ScaleLeft And HScroll1.Value > 0 Then
                TimerL.Enabled = True
            ElseIf PictureL_Left > PictureC.ScaleWidth And HScroll1.Value < HScroll1.Max Then
                TimerR.Enabled = True
            ElseIf PictureL_Left < PictureC.ScaleLeft And HScroll1.Value = HScroll1.Min Then
                PictureL_Left = PictureC.ScaleLeft
            End If
    
            PictureL.Left = PictureL_Left
            
            If PictureL_Right > PictureR.Left Then
                PictureR.Left = PictureR.Left + 1
            End If
            
        End If
    End Sub
    
    Private Sub PictureL_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        TimerL.Enabled = False
        TimerR.Enabled = False
    End Sub
    
    Private Sub PictureR_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then
            m_StartX = X
        End If
    End Sub
    
    Private Sub PictureR_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim PictureR_Left As Single, PictureR_Right As Single
        
        PictureR.BackColor = vbGreen
        PictureL.BackColor = vbRed
    
        If Button = vbLeftButton Then
            PictureR_Left = PictureR.Left + (X - m_StartX)
            PictureR_Right = PictureR_Left + PictureR.Width
    
            If PictureR_Right > PictureC.ScaleWidth And HScroll1.Value < HScroll1.Max Then
                TimerR.Enabled = True
            ElseIf PictureR_Right < PictureC.ScaleLeft And HScroll1.Value > HScroll1.Min Then
                TimerL.Enabled = True
            ElseIf PictureR_Right > PictureC.ScaleWidth And HScroll1.Value = HScroll1.Max Then
                PictureR_Left = PictureC.ScaleWidth - PictureR.Width
            End If
    
            PictureR.Left = PictureR_Left
    
             If PictureR_Left < PictureL.Left + PictureL.Width Then
                PictureL.Left = PictureR.Left - PictureL.Width + 1
            End If
    
        End If
    End Sub
    
    Private Sub PictureR_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        TimerL.Enabled = False
        TimerR.Enabled = False
    End Sub
    
    Private Sub HScroll1_Change()
        PictureU.Cls
        
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
      
        Form1.Caption = HScroll1.Value & "(LM=" & PictureL.Left & ")" & "(RM=" & PictureR.Left & ")"
    
        PictureL.Left = PictureL.Left - HScroll1.Value + oldHScrollVal
        PictureR.Left = PictureR.Left - HScroll1.Value + oldHScrollVal
        
        oldHScrollVal = HScroll1.Value
    End Sub
    
    Private Sub HScroll1_Scroll()
        Call HScroll1_Change
    End Sub
    
    Private Sub TimerL_Timer()
        If HScroll1.Value - 100 >= HScroll1.Min Then
            HScroll1.Value = HScroll1.Value - 100
        Else
            HScroll1.Value = HScroll1.Min
        End If
    End Sub
    
    
    Private Sub TimerR_Timer()
        If HScroll1.Value + 100 <= HScroll1.Max Then
            HScroll1.Value = HScroll1.Value + 100
        Else
            HScroll1.Value = HScroll1.Max
        End If
    End Sub
    Last edited by mms_; Nov 20th, 2019 at 04:49 PM.

  7. #7
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,612

    Re: Moving a PictureBox Inside a PictureBox

    {
    p.s. This is in response to post #5. You posted #6 while I was looking at your code from #5. The issues noted probably still apply, you've changed some of the names though.
    }
    Quote Originally Posted by mms_ View Post
    ...
    1.
    I have it set up so Left marker can never cross the right marker and visa versa.
    If one hits the other, the moving one then pushes the other.
    This works for right to left always.
    Works left to right if done slowly; if done quickly the left marker sometimes passes thru the right marker.
    In the Picture2_MouseMove sub you have this code
    Code:
            If Picture2_Right > Picture3.Left Then
                Picture3.Left = Picture3.Left + 1
            End If
    Does it make since to move Picture3 one pixel to the right whenever Picture2 is greater than Picture3's left?
    Why not set it to the right of Picture2?, i.e. Picture3.Left = Picture2_Right + 1

    Quote Originally Posted by mms_ View Post
    2.
    If scrolling with thumb bar, left & right markers drawn incorrectly (2 or 3 copies of each)
    Add a Picture1.Refresh line to the bottom of your HScroll1_Change Sub.

    3. I use a logic analyzer that can have a very wide graph to work through.
    There, they allow positioning markers by hitting a keyboard button to pop the marker to where the mouse is.
    You should probably do something similar, i.e. you can scroll to the far end and hit a key, or click on a button to bring the selected marker (i.e. you would have two buttons, one for each marker, or two keys defined) to where you are.
    Last edited by passel; Nov 20th, 2019 at 05:01 PM.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  8. #8

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Thanks passel

    Item 2 fix:
    Yes your thumb bar glitch fix works

    Item 1 fix:
    I originally had it coded that way, but this creates an annoying (flickering) 1 pixel gap between the two markers.
    Don't understand why the criss-cross problem doesn't happen in the other direction (same methodology).

    Item 3 fix (suggestion):
    I had implemented a solution (albeit still buggy) for this, as you typed your response.
    I would like to see what you suggest (or something similar), so I could perhaps try to emulate,
    and then decide what is more user friendly.

  9. #9

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    I think this is as good as I can get it.

    Approx 10% of time, auto-scroll scrolls one viewport width beyond the marker being scrolled.
    (hate these hiccups that can't be reproduced consistently)

    Anyways, is there anything that can be done to get rid of "flickering" of my left and right side markers?

    Code:
    Option Explicit 'In a blank Form
    
    Private WithEvents PictureC As VB.PictureBox    'Container
    Private WithEvents PictureL As VB.PictureBox    'Left Marker
    Private WithEvents PictureR As VB.PictureBox    'Right Marker
    Private WithEvents PictureU As VB.PictureBox    'User
    Private WithEvents HScroll1 As VB.HScrollBar
    Private WithEvents TimerL   As VB.Timer
    Private WithEvents TimerR   As VB.Timer
    Private WithEvents Label1   As VB.Label
    Private WithEvents Label2   As VB.Label
    Private WithEvents Label3   As VB.Label
    
    Private m_StartX            As Single
    
    Private Const colWidth      As Long = 10
    Private Const numCols       As Long = 10000
    
    Private oldHScrollVal       As Long
    Private i                   As Long
    
    
    Private Sub Form_Load()
        
        Form1.ScaleMode = vbPixels
    
        Set PictureC = Controls.Add("VB.PictureBox", "PictureC")
        Set PictureL = Controls.Add("VB.PictureBox", "PictureL", PictureC)
        Set PictureR = Controls.Add("VB.PictureBox", "PictureR", PictureC)
        Set PictureU = Controls.Add("VB.PictureBox", "PictureU")
        Set HScroll1 = Controls.Add("VB.HScrollBar", "HScroll1")
        Set TimerL = Controls.Add("VB.Timer", "TimerL")
        Set TimerR = Controls.Add("VB.Timer", "TimerR")
        Set Label1 = Controls.Add("VB.Label", "Label1")
        Set Label2 = Controls.Add("VB.Label", "Label2")
        Set Label3 = Controls.Add("VB.Label", "Label3")
        
        PictureC.Appearance = 0
        PictureC.AutoRedraw = True
        PictureC.ScaleMode = vbPixels
        PictureC.Visible = True
    
        PictureL.Appearance = 0
        PictureL.AutoRedraw = True
        PictureL.BackColor = vbRed
        PictureL.ScaleMode = vbPixels
        PictureL.Visible = True
        
        PictureR.Appearance = 0
        PictureR.AutoRedraw = True
        PictureR.BackColor = vbBlue
        PictureR.ScaleMode = vbPixels
        PictureR.Visible = True
        
        PictureU.Appearance = 0
        PictureU.AutoRedraw = True
        PictureU.BackColor = vbWhite
        PictureU.ScaleMode = vbPixels
        PictureU.Visible = True
        
        HScroll1.Max = numCols
        HScroll1.LargeChange = 50
        HScroll1.Visible = True
        
        TimerL.Enabled = False
        TimerL.Interval = 1
        
        TimerR.Enabled = False
        TimerR.Interval = 1
        
        Label1.Appearance = 0
        Label1.BackColor = vbWhite
        Label1.BorderStyle = 1
        Label1.Width = 200
        Label1.Height = 17
        Label1.Visible = True
        Label1.Caption = "Label1"
        
        Label2.Appearance = 0
        Label2.BackColor = vbWhite
        Label2.BorderStyle = 1
        Label2.Width = 200
        Label2.Height = 17
        Label2.Visible = True
        Label2.Caption = "Label2"
        
        Label3.Appearance = 0
        Label3.BackColor = vbWhite
        Label3.BorderStyle = 1
        Label3.Width = 200
        Label3.Height = 17
        Label3.Visible = True
        Label3.Caption = "Label3"
        
        '--------
        Const GAP = 10!
    
        On Error Resume Next
        PictureC.Move ScaleLeft + GAP, ScaleTop + GAP, ScaleWidth - GAP - GAP, 24
        PictureC.Cls
        
        PictureU.Move ScaleLeft + GAP, PictureC.Top + PictureC.Height + 1, ScaleWidth - GAP - GAP, 50
        PictureU.Cls
        
        HScroll1.Move PictureU.Left, PictureU.Top + PictureU.Height + 0, PictureU.Width, 18
        
        Label3.Move PictureU.Left, ScaleHeight - Label1.Height - GAP, PictureU.Width, 17
        Label2.Move PictureU.Left, Label3.Top - Label3.Height - 2, PictureU.Width, 17
        Label1.Move PictureU.Left, Label2.Top - Label2.Height - 2, PictureU.Width, 17
        
        '--------
        Const PictureL_WIDTH = 10!, PictureL_HEIGHT = 20!
    
        PictureL.Move (PictureC.ScaleWidth - PictureL_WIDTH!) \ 2&, _
                      (PictureC.ScaleHeight - PictureL_HEIGHT) \ 2&, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
        PictureL.Cls
        
        PictureR.Move PictureL.Left + PictureL.Width + 5, _
                      PictureL.Top, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
        PictureR.Cls
    
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
        PictureU.Refresh
        
        HScroll1.LargeChange = PictureC.ScaleWidth
        oldHScrollVal = HScroll1.Value
        
        Call DisplayMetrics
    
    End Sub
    
    
    Private Sub PictureC_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        PictureL.BackColor = vbRed
        PictureR.BackColor = vbBlue
        
    End Sub
    
    
    Private Sub PictureL_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        If Button = vbLeftButton Then
            m_StartX = X
        End If
        
    End Sub
    
    
    Private Sub PictureL_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        Dim PictureL_Left  As Single
        Dim PictureL_Right As Single
    
        PictureL.BackColor = vbGreen
        PictureR.BackColor = vbBlue
        
        TimerL.Enabled = False
        TimerR.Enabled = False
    
        If Button = vbLeftButton Then
    
            Select Case (PictureL.Left + PictureL.Width)
                
                Case Is <> PictureR.Left
                    
                    'Left and right markers NOT touching
                    
                    PictureL_Left = PictureL.Left + (X - m_StartX)
                    PictureL_Right = PictureL_Left + PictureL.Width
            
                    ' Past LEFT side of container
                    If PictureL_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureL_Left = PictureC.ScaleLeft
                    End If
            
                    ' Past RIGHT side of container
                    If PictureL_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureL_Left = PictureC.ScaleWidth - PictureL.Width
                    End If
            
                    PictureL.Left = PictureL_Left
            
                    ' Bump against RIGHT marker
                    If PictureL_Right > PictureR.Left Then
                        PictureR.Left = PictureL.Left + PictureL.Width
                    End If
    
                Case Is = PictureR.Left
                    
                    'Left and right markers ARE touching
                
                    PictureL_Left = PictureL.Left + (X - m_StartX)
                    PictureL_Right = PictureL_Left + PictureL.Width
            
                    ' Past LEFT side of container
                    If PictureL_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureL_Left = PictureC.ScaleLeft
                    End If
            
                    ' Past RIGHT side of container
                    If (PictureL_Right + PictureL.Width + PictureR.Width) > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureL_Left = PictureC.ScaleWidth - PictureL.Width - PictureR.Width
                    End If
            
                    PictureL.Left = PictureL_Left
            
                    ' Bump against RIGHT marker
                    If PictureL_Right > PictureR.Left Then
                        PictureR.Left = PictureL.Left + PictureL.Width
                    End If
                    
            End Select
    
        End If
        
        Call DisplayMetrics
        
    End Sub
    
    
    Private Sub PictureL_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        PictureL.BackColor = vbRed
    
    End Sub
    
    
    Private Sub PictureR_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        If Button = vbLeftButton Then
            m_StartX = X
        End If
        
    End Sub
    
    
    Private Sub PictureR_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Dim PictureR_Left As Single
        Dim PictureR_Right As Single
        
        PictureR.BackColor = vbGreen
        PictureL.BackColor = vbRed
        
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        If Button = vbLeftButton Then
    
            Select Case PictureR.Left
            
                Case Is <> (PictureL.Left + PictureL.Width)
                    
                    'Left and right markers NOT touching
    
                    PictureR_Left = PictureR.Left + (X - m_StartX)
                    PictureR_Right = PictureR_Left + PictureR.Width
                    
                    ' Past LEFT side of container
                    If PictureR_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureR_Left = PictureC.ScaleLeft
                    End If
                    
                    ' Past RIGHT side of container
                    If PictureR_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureR_Left = PictureC.ScaleWidth - PictureR.Width
                    End If
            
                    PictureR.Left = PictureR_Left
            
                    ' Bump against LEFT marker
                    If PictureR_Left < (PictureL.Left + PictureL.Width) Then
                        PictureL.Left = PictureR.Left - PictureL.Width
                    End If
    
                Case Is = (PictureL.Left + PictureL.Width)
                
                    'Left and right markers ARE touching
    
                    PictureR_Left = PictureR.Left + (X - m_StartX)
                    PictureR_Right = PictureR_Left + PictureR.Width
                
                    ' Past LEFT side of container
                    If (PictureR_Left - PictureL.Width) < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureR_Left = PictureC.ScaleLeft + PictureL.Width
                        PictureL.Left = PictureR.Left 'without this line bug -> drag left marker tight to left edge, drag right marker to bump left marker keeping mouse down, watch what happens
                    End If
                    
                    ' Past RIGHT side of container
                    If PictureR_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureR_Left = PictureC.ScaleWidth - PictureR.Width
                    End If
            
                    PictureR.Left = PictureR_Left
            
                    ' Bump against LEFT marker
                    If PictureR_Left < (PictureL.Left + PictureL.Width) Then
                        PictureL.Left = PictureR.Left - PictureL.Width
                    End If
                    
            End Select
    
        End If
        
        Call DisplayMetrics
    
    End Sub
    
    
    Private Sub PictureR_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        PictureR.BackColor = vbBlue
        
    End Sub
    
    
    Private Sub HScroll1_Change()
    
        PictureU.Cls
    
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
    
        PictureL.Left = PictureL.Left - HScroll1.Value + oldHScrollVal
        PictureR.Left = PictureR.Left - HScroll1.Value + oldHScrollVal
    
        oldHScrollVal = HScroll1.Value
        
        Call DisplayMetrics
    
        PictureC.Refresh 'passel fix
    
    End Sub
    
    
    Private Sub HScroll1_Scroll()
        
        Call HScroll1_Change
    
    End Sub
    
    
    Private Sub TimerL_Timer()
    
        If PictureL.Left <= PictureC.ScaleLeft And HScroll1.Value = HScroll1.Min Then
            TimerL.Enabled = False
        End If
        
        If HScroll1.Value - PictureC.ScaleWidth >= HScroll1.Min Then
            HScroll1.Value = HScroll1.Value - PictureC.ScaleWidth
        Else
            HScroll1.Value = HScroll1.Min
        End If
    
    End Sub
    
    
    Private Sub TimerR_Timer()
    
        If PictureR.Left + PictureR.Width >= PictureC.ScaleWidth And HScroll1.Value = HScroll1.Max Then
            TimerR.Enabled = False
        End If
    
        If HScroll1.Value + PictureC.ScaleWidth <= HScroll1.Max Then
            HScroll1.Value = HScroll1.Value + PictureC.ScaleWidth
        Else
            HScroll1.Value = HScroll1.Max
        End If
    
    End Sub
    
    
    Private Sub DisplayMetrics()
    
        Label1.Caption = "HScroll = " & HScroll1.Value
        Label2.Caption = "(LM=" & PictureL.Left & ")" & _
                         "(RM=" & PictureR.Left & ")"
        Label3.Caption = "(LM=" & PictureL.Left + HScroll1.Value & ")" & _
                         "(RM=" & PictureR.Left + HScroll1.Value & ")"
                         
    End Sub
    Last edited by mms_; Nov 22nd, 2019 at 04:18 PM.

  10. #10
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,612

    Re: Moving a PictureBox Inside a PictureBox

    Quote Originally Posted by mms_ View Post
    ...
    Anyways, is there anything that can be done to get rid of "flickering" of my left and right side markers?
    ...
    I would say the best way to get rid of the flickering is to not use controls for the markers, just draw them. That is what I would have done from the start for something like this. I guess I could look at your latest code to see if I can modify it to show what I mean, and to see what difference it would make for myself.

    Also, I'm wondering if the markers should be "snapped" to align with your columns, rather than be on any arbitrary pixel in the range.
    Last edited by passel; Nov 22nd, 2019 at 03:51 PM.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  11. #11

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Also, I'm wondering if the markers should be "snapped" to align with your columns, rather than be on any arbitrary pixel in the range.
    Yes... needs to be "snapped"

  12. #12
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,612

    Re: Moving a PictureBox Inside a PictureBox

    Ok, after looking at the code in more detail, I guess it isn't necessarily a control drawing issue, but the fact that you have the control positioned in two different ways.

    When you move the controls by dragging, and you start auto-scrolling, you keep the control within the container window by restricting the control to a position within the window, but in the timer you change the scroll value, and when the scroll value changes, the marker positions are changed to allow the markers to go far outside the container window, which is by design to allow the markers to follow the full range that your user window can be scrolled.

    So, the flashing is caused by which position is active at the time the screen is redrawn, the one that the scroll set, or the one that your drag set.
    So, a simple fix is to add a dragging flag that can tell the scroll code to not move the control being dragged.
    Code:
    'In the declarations
    Private DraggingFlag As Integer  '0 not dragging, 1 dragging left, 2 dragging right
    
    
    Private Sub PictureL_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        If Button = vbLeftButton Then
            m_StartX = X
            DraggingFlag = 1
        End If
        
    End Sub
    
    Private Sub PictureL_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        PictureL.BackColor = vbRed
        DraggingFlag = 0
    
    End Sub
    
    Private Sub PictureR_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        If Button = vbLeftButton Then
            m_StartX = X
            DraggingFlag = 2
    
        End If
        
    End Sub
    
    Private Sub PictureR_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        PictureR.BackColor = vbBlue
        DraggingFlag = 0
        
    End Sub
    
    Private Sub HScroll1_Change()
    
        PictureU.Cls
    
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
        
        If DraggingFlag <> 1 Then
          PictureL.Left = PictureL.Left - HScroll1.Value + oldHScrollVal
        End If
        
        If DraggingFlag <> 2 Then
          PictureR.Left = PictureR.Left - HScroll1.Value + oldHScrollVal
        End If
        
        oldHScrollVal = HScroll1.Value
        
        Call DisplayMetrics
    
        PictureC.Refresh 'passel fix
    
    End Sub
    I'm heading home from work, so will have to consider the snapping later.

    Also, will still flash if you're pushing one marker with the other. I guess that is a third state that could be handled by the dragging flag, i.e. if you're pushing the other marker, then set the draggingFlag to 3, indicating that both controls are being dragged (although technically one is dragged and the other pushed).
    You could then change the test in the HScroll code to do a boolean test.

    You can change to the boolean test now, and the current code will still work.
    Code:
        If (DraggingFlag And 1) <> 1 Then
          PictureL.Left = PictureL.Left - HScroll1.Value + oldHScrollVal
        End If
        
        If (DraggingFlag And 2) <> 2 Then
          PictureR.Left = PictureR.Left - HScroll1.Value + oldHScrollVal
        End If
    But you'll need to test for the pushing condition at the left and right edges to eliminate the flashing there. I can't do that right now, as I'm leaving.

    p.s. Actually, I couldn't help myself. It was a quick enough change to test, I did it anyway, so here you go.
    This is the PictureL code, look where DraggingFlag is set.
    PictureR code would be identical, except for the first place, where DraggingFlag would be set to 2, not 1.
    Code:
    Private Sub PictureL_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        Dim PictureL_Left  As Single
        Dim PictureL_Right As Single
    
        PictureL.BackColor = vbGreen
        PictureR.BackColor = vbBlue
        
        TimerL.Enabled = False
        TimerR.Enabled = False
    
        If Button = vbLeftButton Then
    
            Select Case (PictureL.Left + PictureL.Width)
                
                Case Is <> PictureR.Left
                    
                    'Left and right markers NOT touching
                    DraggingFlag = 1
                    PictureL_Left = PictureL.Left + (X - m_StartX)
                    PictureL_Right = PictureL_Left + PictureL.Width
            
                    ' Past LEFT side of container
                    If PictureL_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureL_Left = PictureC.ScaleLeft
                    End If
            
                    ' Past RIGHT side of container
                    If PictureL_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureL_Left = PictureC.ScaleWidth - PictureL.Width
                    End If
            
                    PictureL.Left = PictureL_Left
            
                    ' Bump against RIGHT marker
                    If PictureL_Right > PictureR.Left Then
                        DraggingFlag = 3
                        PictureR.Left = PictureL.Left + PictureL.Width
                    End If
    
                Case Is = PictureR.Left
                    
                    'Left and right markers ARE touching
                    DraggingFlag = 3
                    PictureL_Left = PictureL.Left + (X - m_StartX)
                    PictureL_Right = PictureL_Left + PictureL.Width
            
                    ' Past LEFT side of container
                    If PictureL_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureL_Left = PictureC.ScaleLeft
                    End If
            
                    ' Past RIGHT side of container
                    If (PictureL_Right + PictureL.Width + PictureR.Width) > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureL_Left = PictureC.ScaleWidth - PictureL.Width - PictureR.Width
                    End If
            
                    PictureL.Left = PictureL_Left
            
                    ' Bump against RIGHT marker
                    If PictureL_Right > PictureR.Left Then
                        PictureR.Left = PictureL.Left + PictureL.Width
                    End If
                    
            End Select
    
        End If
        
        Call DisplayMetrics
        
    End Sub
    p.p.s Since the code above now sets DraggingFlag to 1 or 2 in the MouseMove routines, they don't have to be set any longer to 1 or 2 in the MouseDown routines.
    Last edited by passel; Nov 22nd, 2019 at 10:23 PM.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  13. #13
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    284

    Re: Moving a PictureBox Inside a PictureBox

    FWIW, I've been working on a different implementation. Rather than using a Timer control, I've used the Timer function to introduce intermittent delays while scrolling the markers and columns (scrolling occurs too fast without it). I have also decided to add two more PictureBoxes that serves as viewports into PictureC and PictureU. However, I realized later that this approach is flawed. It turns out that windows have a maximum width/height due to "an architectural limitation". VB6 unfortunately limits PictureBox sizes to just 16,383 (&H3FFF) pixels. MoveWindow can raise that limit to twice that value: 32,767 (&H7FFF) and SetWindowPos can increase it to the maximum dimension: 65,535 (&HFFFF) but it's still not enough for the 100,000 pixels (1010000) needed for the columns. Sadly, I didn't catch this bug until it was too late to change course (I missed it because I've lowered the numCols constant from 10,000 to 100 for ease of debugging). You might still be able to salvage some of the logic in it, though. For example, the markers are working very well. I have also implemented passel's suggestion 3 in post #7 (instead of dragging the markers, you can just left or right click PictureC to reposition them).

    Code:
    Option Explicit
    
    Private Const colWidth      As Long = 10
    Private Const numCols       As Long = 100 '00
    Private Const DELAY         As Single = 0.1!
    
    Private WithEvents PictureC As VB.PictureBox    'Container
    Private WithEvents PictureL As VB.PictureBox    'Left Marker
    Private WithEvents PictureR As VB.PictureBox    'Right Marker
    Private WithEvents PictureU As VB.PictureBox    'User
    Private WithEvents Picture5 As VB.PictureBox    'Container of PictureC
    Private WithEvents Picture6 As VB.PictureBox    'Container of PictureU
    Private WithEvents HScroll1 As VB.HScrollBar
    
    Private m_StartX            As Single
    
    Private Sub Form_Load()
        ScaleMode = vbPixels
    
        Set Picture5 = Controls.Add("VB.PictureBox", "Picture5")
        Set PictureC = Controls.Add("VB.PictureBox", "PictureC", Picture5)
        Set PictureL = Controls.Add("VB.PictureBox", "PictureL", PictureC)
        Set PictureR = Controls.Add("VB.PictureBox", "PictureR", PictureC)
        Set Picture6 = Controls.Add("VB.PictureBox", "Picture6")
        Set PictureU = Controls.Add("VB.PictureBox", "PictureU", Picture6)
        Set HScroll1 = Controls.Add("VB.HScrollBar", "HScroll1")
    
        Picture5.Appearance = 0
        Picture5.BackColor = vbApplicationWorkspace
        Picture5.ScaleMode = vbPixels
        Picture5.Visible = True
    
        PictureC.Appearance = 0
        PictureC.BorderStyle = 0
        PictureC.ScaleMode = vbPixels
        PictureC.Visible = True
    
        PictureL.Appearance = 0
        PictureL.BackColor = vbRed
        PictureL.ScaleMode = vbPixels
        PictureL.Visible = True
    
        PictureR.Appearance = 0
        PictureR.BackColor = vbBlue
        PictureR.ScaleMode = vbPixels
        PictureR.Visible = True
    
        Picture6.Appearance = 0
        Picture6.BackColor = vbApplicationWorkspace
        Picture6.ScaleMode = vbPixels
        Picture6.Visible = True
    
        PictureU.Appearance = 0
        PictureU.BorderStyle = 0
        PictureU.AutoRedraw = True
       'PictureU.BackColor = vbWhite
        PictureU.ScaleMode = vbPixels
        PictureU.Visible = True
    
        HScroll1.LargeChange = 50
        HScroll1.Visible = True
    
        Form_Resize
        FormActivate
    End Sub
    
    Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        PictureL.BackColor = vbRed
        PictureR.BackColor = vbBlue
    End Sub
    
    Private Sub Form_Resize()
        Const GAP = 10!, HSCROLL1_HEIGHT = 18!, PICTURE5_HEIGHT = 36!
        Static LastValue As Integer
    
        On Error Resume Next
        Picture5.Move ScaleLeft + GAP, _
                      ScaleTop + GAP, _
                      ScaleWidth - GAP - GAP, _
                      PICTURE5_HEIGHT
    
        Picture6.Move ScaleLeft + GAP, _
                      Picture5.Top + Picture5.Height + 1!, _
                      ScaleWidth - GAP - GAP, _
                      ScaleHeight - GAP - PICTURE5_HEIGHT - 1! - HSCROLL1_HEIGHT - GAP
        On Error GoTo 0
    
        HScroll1.Move Picture6.Left, _
                      ScaleHeight - HSCROLL1_HEIGHT - GAP, _
                      Picture6.Width, _
                      HSCROLL1_HEIGHT
    
        If Picture6.ScaleWidth < PictureU.Width Then            'If viewport is smaller than the PictureBox it's viewing
            HScroll1.Enabled = True
            HScroll1.Max = PictureU.Width - Picture6.ScaleWidth
    
            If LastValue Then
                If LastValue <= HScroll1.Max Then
                    HScroll1.Value = LastValue
                    LastValue = 0
                Else
                    HScroll1.Value = HScroll1.Max
                End If
            End If
    
        ElseIf HScroll1.Enabled Then
            LastValue = HScroll1.Value
            HScroll1.Max = 0
            HScroll1.Enabled = False
        End If
    End Sub
    
    Private Sub FormActivate()
        Const PictureL_WIDTH = 10!, PictureL_HEIGHT = 20!
        Dim PictureU_ScaleHeight As Single, X As Single
    
        PictureL.Move (Picture5.ScaleWidth - (PictureL_WIDTH + 5! + PictureL_WIDTH)) \ 2&, _
                      (Picture5.ScaleHeight - PictureL_HEIGHT) \ 2&, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
    
        PictureR.Move PictureL.Left + PictureL.Width + 5!, _
                      PictureL.Top, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
    
        PictureC.Move Picture5.ScaleLeft, Picture5.ScaleTop, colWidth * numCols, Picture5.ScaleHeight
        PictureU.Move Picture6.ScaleLeft, Picture6.ScaleTop, colWidth * numCols, Screen.Height / Screen.TwipsPerPixelY
        Debug.Assert PictureU.ScaleWidth = colWidth * numCols
    
        X = colWidth
        PictureU_ScaleHeight = PictureU.ScaleHeight
    
        PictureU.Line (X, 0!)-(X, PictureU_ScaleHeight), vbRed      'Color first line red
    
        For X = colWidth * 2& To (numCols - 2&) * colWidth Step colWidth
            PictureU.Line (X, 0!)-(X, PictureU_ScaleHeight), vbBlack
        Next
    
        PictureU.Line (X, 0!)-(X, PictureU_ScaleHeight), vbRed      'Color last line red
    End Sub
    
    Private Sub PictureC_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Select Case Button
            Case vbLeftButton:  PictureL.Left = X - PictureL.Width * 0.5!
    
                                If PictureL.Left < PictureC.ScaleLeft Then
                                    PictureL.Left = PictureC.ScaleLeft
                                ElseIf PictureL.Left + PictureL.Width > PictureC.ScaleWidth - PictureR.Width Then
                                    PictureL.Left = PictureC.ScaleWidth - PictureL.Width - PictureR.Width
                                End If
    
                                If PictureR.Left < PictureL.Left + PictureL.Width Then
                                    PictureR.Left = PictureL.Left + PictureL.Width
                                End If
    
            Case vbRightButton: PictureR.Left = X - PictureR.Width * 0.5!
    
                                If PictureR.Left < PictureC.ScaleLeft + PictureL.Width Then
                                    PictureR.Left = PictureC.ScaleLeft + PictureL.Width
                                ElseIf PictureR.Left + PictureR.Width > PictureC.ScaleWidth Then
                                    PictureR.Left = PictureC.ScaleWidth - PictureR.Width
                                End If
    
                                If PictureL.Left + PictureL.Width > PictureR.Left Then
                                    PictureL.Left = PictureR.Left - PictureL.Width
                                End If
        End Select
    End Sub
    
    Private Sub PictureC_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        PictureL.BackColor = vbRed
        PictureR.BackColor = vbBlue
    End Sub
    
    Private Sub PictureL_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then m_StartX = X
    End Sub
    
    Private Sub PictureL_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim PictureL_Left As Single, PictureL_Right As Single, Delta As Single, Tmr As Single
        Static IsBeingDelayed As Boolean, IsScrollingLeft As Boolean, LastTimer As Single
    
        If IsBeingDelayed Then
            Tmr = Timer
            Delta = Tmr - LastTimer
            If Delta < 0! Then Delta = Tmr + 86400! - LastTimer 'If time just crossed midnight, add number of seconds in a day in order to calculate correct elapsed time
    
            If Delta < DELAY Then
                If IsScrollingLeft Then
                    HScroll1.Value = PictureL.Left
                Else
                    HScroll1.Value = PictureL.Left - (Picture5.ScaleWidth - PictureL.Width)
                End If
    
                Exit Sub
            Else
                IsBeingDelayed = False
            End If
        End If
    
        PictureL.BackColor = vbGreen
        PictureR.BackColor = vbBlue
    
        If Button = vbLeftButton Then
            PictureL_Left = PictureL.Left + (X - m_StartX)
            PictureL_Right = PictureL_Left + PictureL.Width
    
            If PictureL_Right > PictureC.ScaleWidth - PictureR.Width Then
                PictureL_Left = PictureC.ScaleWidth - PictureR.Width - PictureL.Width
            End If
    
            If PictureL_Left < PictureC.ScaleLeft Then
                PictureL_Left = PictureC.ScaleLeft
            End If
    
            PictureL.Left = PictureL_Left
            PictureL_Right = PictureL_Left + PictureL.Width
    
            If PictureL_Right > PictureR.Left Then
                PictureR.Left = PictureL_Right
            End If
    
            If PictureL_Left <= HScroll1.Value Then
                Delta = PictureL_Left - HScroll1.Value
    
                If Delta < 0! Then
                    LastTimer = Timer
                    IsBeingDelayed = True
                    IsScrollingLeft = True
                    HScroll1.Value = HScroll1.Value + Delta
    
                ElseIf Delta = 0! Then
                    If PictureL_Left = PictureC.ScaleLeft Then
                        HScroll1.Value = HScroll1.Min
                    End If
                End If
    
            ElseIf PictureL_Right >= HScroll1.Value + Picture5.ScaleWidth Then
                Delta = PictureL_Right - (HScroll1.Value + Picture5.ScaleWidth)
    
                If Delta > 0! Then
                    LastTimer = Timer
                    IsBeingDelayed = True
                    IsScrollingLeft = False
                    HScroll1.Value = HScroll1.Value + Delta
    
                ElseIf Delta = 0! Then
                    If PictureL_Right = PictureC.ScaleWidth - PictureR.Width Then
                        HScroll1.Value = HScroll1.Max
                    End If
                End If
            End If
        End If
    End Sub
    
    Private Sub PictureL_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        PictureL.BackColor = vbRed
    End Sub
    
    Private Sub PictureR_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = vbLeftButton Then m_StartX = X
    End Sub
    
    Private Sub PictureR_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Dim PictureR_Left As Single, PictureR_Right As Single, Delta As Single, Tmr As Single
        Static IsBeingDelayed As Boolean, IsScrollingLeft As Boolean, LastTimer As Single
    
        If IsBeingDelayed Then
            Tmr = Timer
            Delta = Tmr - LastTimer
            If Delta < 0! Then Delta = Tmr + 86400! - LastTimer 'If time just crossed midnight, add number of seconds in a day in order to calculate correct elapsed time
    
            If Delta < DELAY Then
                If IsScrollingLeft Then
                    HScroll1.Value = PictureR.Left
                Else
                    HScroll1.Value = PictureR.Left - (Picture5.ScaleWidth - PictureR.Width)
                End If
    
                Exit Sub
            Else
                IsBeingDelayed = False
            End If
        End If
    
        PictureR.BackColor = vbGreen
        PictureL.BackColor = vbRed
    
        If Button = vbLeftButton Then
            PictureR_Left = PictureR.Left + (X - m_StartX)
            PictureR_Right = PictureR_Left + PictureR.Width
    
            If PictureR_Right > PictureC.ScaleWidth Then
                PictureR_Left = PictureC.ScaleWidth - PictureR.Width
            End If
    
            If PictureR_Left < PictureC.ScaleLeft + PictureL.Width Then
                PictureR_Left = PictureC.ScaleLeft + PictureL.Width
            End If
    
            PictureR.Left = PictureR_Left
            PictureR_Right = PictureR_Left + PictureR.Width
    
            If PictureR_Left < PictureL.Left + PictureL.Width Then
                PictureL.Left = PictureR_Left - PictureL.Width
            End If
    
            If PictureR_Left <= HScroll1.Value Then
                Delta = PictureR_Left - HScroll1.Value
    
                If Delta < 0! Then
                    LastTimer = Timer
                    IsBeingDelayed = True
                    IsScrollingLeft = True
                    HScroll1.Value = HScroll1.Value + Delta
    
                ElseIf Delta = 0! Then
                    If PictureR_Left = PictureC.ScaleLeft + PictureL.Width Then
                        HScroll1.Value = HScroll1.Min
                    End If
                End If
    
            ElseIf PictureR_Right >= HScroll1.Value + Picture5.ScaleWidth Then
                Delta = PictureR_Right - (HScroll1.Value + Picture5.ScaleWidth)
    
                If Delta > 0! Then
                    LastTimer = Timer
                    IsBeingDelayed = True
                    IsScrollingLeft = False
                    HScroll1.Value = HScroll1.Value + Delta
    
                ElseIf Delta = 0! Then
                    If PictureR_Right = PictureC.ScaleWidth Then
                        HScroll1.Value = HScroll1.Max
                    End If
                End If
            End If
        End If
    End Sub
    
    Private Sub PictureR_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        PictureR.BackColor = vbBlue
    End Sub
    
    Private Sub HScroll1_Change()
        HScroll1_Scroll
    End Sub
    
    Private Sub HScroll1_Scroll()
        PictureC.Left = -HScroll1.Value
        PictureU.Left = -HScroll1.Value
    
        Caption = HScroll1.Value & " LM=" & PictureL.Left & " RM=" & PictureR.Left
    End Sub
    Last edited by Victor Bravo VI; Nov 23rd, 2019 at 01:17 PM. Reason: Fixed a bug in Sub Form_Resize() by adding If LastValue <= HScroll1.Max Then ... Else ... End If

  14. #14
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,612

    Re: Moving a PictureBox Inside a PictureBox

    As for snapping to a column, I guess the question is it looks like you plan on having 10,000 columns, as seeing that each column is 10 pixels wide, your horizontal drawing area should be a virtual 100,000 pixels wide.

    But, your horizontal scrolling is currently being done in pixels, over a range of 0 to 10,000.
    You are drawing lines over a range of 10 to 100,000 pixels.

    {The next bit talking about the number of columns showing depends on the size of your form. In my case the form was fairly small, only a little over 300 pixels wide, so the container and user pictureboxes ended up being 282 pixels scalewidth.
    Of course, DPI settings may throw a monkey wrench into how things are drawn, I don't know
    Your values may be different.
    Ideally, you would want to size the form, and your internal windows to end up with a client area that is multiple of your column size.
    If not, then when setting the User ScaleMode, I guess you should set the scalemode to pixels to identify what the scale with in pixels, then divide that value by column size to get the width in units of column, and set the Scalewidth to that value. Then it shouldn't matter if the width was an exact number of columns, the scalewidth should mathematically map column boundaries to a whole number of pixels, and you may have a partial column visible at the end.
    }
    So, if the scrollbar is at 0, you are showing the first 28 columns. (remember this is based on the size of my form, your values will likely be different).
    If your scrollbar is at max (10,000) you are showing columns 1000 to 1028.

    So there are 8972 columns that you are not showing currently.

    I assume you mean to multiply the scrollbar value by the column width, so that can scroll through 10,000 columns, not 10,000 pixels.
    This will work for drawing the lines, because the lines are drawn using mathematical offsets, as shown below.
    Trying to draw outside the bounds of the picture will not cause an error, and when the math brings the lines into PictureU, they are drawn.
    Code:
    Private Sub HScroll1_Change()
        Dim ColumnPixelOffset As Single
        
        PictureU.Cls
        ColumnPixelOffset = HScroll1.Value * colWidth
        
        For i = 1 To numCols
        '   If i Mod 500 = 0 Then Stop
    '        PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
            PictureU.Line (i * colWidth - ColumnPixelOffset, 0)-(i * colWidth - ColumnPixelOffset, PictureU.Height), vbBlack
        Next i
        
        If (DraggingFlag And 1) <> 1 Then
    '      PictureL.Left = PictureL.Left - HScroll1.Value + oldHScrollVal
          PictureL.Left = PictureL.Left - ColumnPixelOffset + oldHScrollVal
        End If
        
        If (DraggingFlag And 2) <> 2 Then
    '      PictureR.Left = PictureR.Left - HScroll1.Value + oldHScrollVal
          PictureR.Left = PictureR.Left - ColumnPixelOffset + oldHScrollVal
        End If
        
    '    oldHScrollVal = HScroll1.Value
        oldHScrollVal = ColumnPixelOffset
        
        Call DisplayMetrics
    
        PictureC.Refresh 'passel fix
    
    End Sub
    But, as Victor Bravo VI has rediscovered, there is a limit to the size of the actual picture in a picturebox, which restricts the values you can set PictureR.Left and PictureL.Left to (to +/- 16386, give or take) within PictureC, so while the above allows you to draw within the visual part of PictureU and ignore the drawing outside of the window, the correct position of the markers gets limited and since you count on the .Left property to keep track of the position of the marker within your window when you scroll, they end up failing to maintain relative position long before you've used the scrollbar to scroll the full range of your virtual space.

    You need to rethink what you want to do.
    There are multiple ways this can be handled.
    One way, is to use a custom scale, i.e. since you would like to snap things to the columns, then rather than use pixels for your scale, use a user scale based on columns.

    If you set ScaleWidth to represent units of colWidth
    now you wouldn't have to multiply by colWidth in your calculations, you would just use column.
    Instead of this
    Code:
        ColumnPixelOffset = HScroll1.Value * colWidth
        For i = 1 To numCols
            PictureU.Line (i * colWidth - ColumnPixelOffset, 0)-(i * colWidth - ColumnPixelOffset, PictureU.Height), vbBlack
        Next i
    You would have this
    Code:
        For i = 1 To numCols
            PictureU.Line (i - HScroll1.Value, 0)-(i - HScroll1.Value, PictureU.Height), vbBlack
        Next i
    Since your coordinate system would now be based horizontally on units of column, then everything positioned at an Integer offset would be snapped to that column by definition.
    But it still won't allow you to specify a left value that exceeds the range of a 16-bit value in pixels, i.e. now that the ScaleWidth is set in units of columns, 10 pixels per column, it won't allow you to set pictureL.Left to less than -1638.4 or greater than 1638.3 .

    I think you need your own values to keep track of where the markers are and if the markers are not close to where they would be visible in the window, don't bother updating their "physical" position, their .Left property. Always use your marker maintained position everywhere and update the .Left property of the window, when it is close to being visible in your window.

    I don't know if I want to hack at what you have, or just implement an approach based on what your requirements appear to be. I have "real" work I should be doing, so I may have to let you guys work at it as best you can.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  15. #15

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    passel and Victor Bravo VI
    Thank you both for all of your help... it is appreciated!

    What OS are you using?

    As I tried my code here at home this morning, it is behaving differently on my Windows7 machine,
    than it was yesterday at work on my Windows10 laptop.

    The line I had marked 'bug fix was to fix a bug when both markers were
    tight together and ScrollBar was at zero

    Here at home, the right marker does not "push" the left marker at all at any ScrollBar position,
    rather the left marker "drags" the right marker if they are tight together, and at the extreme left edge.
    This (dragging) should not happen... "pushing" only in either direction is all that is allowed.

    I am going to drive back to work to pick up my Windows10 laptop so I can investigate tonight.


    Victor Bravo VI
    I think your implementation of the "click set" of each of the markers is great and I will most definitely use this,
    either in conjunction with my dragging methodology, or alone if I can't get it working properly.

    passel
    I will try to implement your DraggingFlag fixes tonight, when I bring home my Windows10 laptop.
    I will worry about the "snapping" feature later.
    Last edited by mms_; Nov 23rd, 2019 at 11:53 AM.

  16. #16
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    284

    Re: Moving a PictureBox Inside a PictureBox

    Quote Originally Posted by mms_ View Post
    What OS are you using?
    I have tested my code only on Windows 7 (x64). I just did minimal testing of your latest code in post #9 (I've incorporated passel's modifications in post #12) on the same OS and my observations are: (1) the left marker is working just fine; it can be dragged to either left or right without any problem; it can also push the right marker without any issue. (2) The right marker can also be dragged left or right and it can also push the left marker, but if it happens to be pushing the left marker and the left marker touches the left side of PictureC, then they both stop moving to the left. Sometimes, this is also accompanied by a graphical glitch, especially when the mouse is dragged quickly to the left.

  17. #17

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Victor Bravo VI
    Thanks for testing...

    Yes, your description of program operation is exactly as I see on my Windows7 system
    (ie everything working OK except when right marker pushes left marker)

    As I stated earlier, I thought everything was working almost perfect on my Windows10 system,
    and after bringing my work Windows10 laptop home and testing again,
    I did confirm that everything was in fact, working good.

    That takes the wind out of my sails...
    Why difference between the two systems?

    It is getting late here, but I will spend another hour or two and try to figure it out.
    Last edited by mms_; Nov 24th, 2019 at 12:52 AM.

  18. #18

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Code below now works on both Windows7 and Windows10 I think.

    It is a combination of
    - my Post #9 code (with the addition of a MsgBox display when marker not visible in viewport at MouseUp glitch) '------
    - fix so code works for both Windows7 and 10 '******
    - passel's DraggingFlag addition
    - Victor Bravo VI's "click set" code

    I think on Windows10 it is perfect.

    On Windows7, one small glitch that I can't get rid of:
    If left marker is tight to viewport left edge when HScroll = 0, and right marker is dragged to bump against it,
    the two markers are drawn slightly incorrect until mouse is released.
    If right marker dragged slowly, this does not happen, but even with a slight speed this happens.
    I notice in debug labels, if LM=0 and RM=10 markers drawn correctly, but if I see say LM=0 and RM=13 say,
    markers drawn incorrectly until MouseUp

    With the implementation of passel's DraggingFlag, I have never seen the glitch MsgBox appear on either Windows7 or 10
    Without it, it would display 25% of the time.

    passel
    But, as Victor Bravo VI has rediscovered, there is a limit to the size of the actual picture in a picturebox, which restricts the values you can set PictureR.Left and PictureL.Left to (to +/- 16386, give or take) within PictureC, ...
    I have to investigate this further, because if this is the case, none of this will work in my main program,
    as ScrollBar max can be as high as 6,000,000 (I have tested my app with 1,000,000 objects, and I have my column size set to 6 pixels)
    It was just a theoretical test to see how many objects it could handle, not that it ever would in real use.

    Also, how does
    If (DraggingFlag And 1) <> 1 Then
    read?
    ie what does (DraggingFlag And 1) translate to?
    The And operator always confuses me.

    Code:
    Option Explicit 'In a blank Form
    
    Private WithEvents PictureC As VB.PictureBox    'Container
    Private WithEvents PictureL As VB.PictureBox    'Left Marker
    Private WithEvents PictureR As VB.PictureBox    'Right Marker
    Private WithEvents PictureU As VB.PictureBox    'User
    Private WithEvents HScroll1 As VB.HScrollBar
    Private WithEvents TimerL   As VB.Timer
    Private WithEvents TimerR   As VB.Timer
    Private WithEvents Label1   As VB.Label
    Private WithEvents Label2   As VB.Label
    Private WithEvents Label3   As VB.Label
    Private WithEvents Label4   As VB.Label
    
    Private m_StartX            As Single
    
    Private Const colWidth      As Long = 10
    Private Const numCols       As Long = 10000
    
    Private oldHScrollVal       As Long
    Private i                   As Long
    
    Private DraggingFlag As Integer  '0 not dragging, 1 dragging left, 2 dragging right, 3 dragging left or right and simultaneously pushing
    
    
    Private Sub Form_Load()
        
        Form1.ScaleMode = vbPixels
    
        Set PictureC = Controls.Add("VB.PictureBox", "PictureC")
        Set PictureL = Controls.Add("VB.PictureBox", "PictureL", PictureC)
        Set PictureR = Controls.Add("VB.PictureBox", "PictureR", PictureC)
        Set PictureU = Controls.Add("VB.PictureBox", "PictureU")
        Set HScroll1 = Controls.Add("VB.HScrollBar", "HScroll1")
        Set TimerL = Controls.Add("VB.Timer", "TimerL")
        Set TimerR = Controls.Add("VB.Timer", "TimerR")
        Set Label1 = Controls.Add("VB.Label", "Label1")
        Set Label2 = Controls.Add("VB.Label", "Label2")
        Set Label3 = Controls.Add("VB.Label", "Label3")
        Set Label4 = Controls.Add("VB.Label", "Label4")
        
        PictureC.Appearance = 0
        PictureC.AutoRedraw = True
        PictureC.ScaleMode = vbPixels
        PictureC.Visible = True
    
        PictureL.Appearance = 0
        PictureL.AutoRedraw = True
        PictureL.BackColor = vbRed
        PictureL.ScaleMode = vbPixels
        PictureL.Visible = True
        
        PictureR.Appearance = 0
        PictureR.AutoRedraw = True
        PictureR.BackColor = vbBlue
        PictureR.ScaleMode = vbPixels
        PictureR.Visible = True
        
        PictureU.Appearance = 0
        PictureU.AutoRedraw = True
        PictureU.BackColor = vbWhite
        PictureU.ScaleMode = vbPixels
        PictureU.Visible = True
        
        HScroll1.Max = numCols
        HScroll1.LargeChange = 50
        HScroll1.Visible = True
        
        TimerL.Enabled = False
        TimerL.Interval = 1
        
        TimerR.Enabled = False
        TimerR.Interval = 1
        
        Label1.Appearance = 0
        Label1.BackColor = vbWhite
        Label1.BorderStyle = 1
        Label1.Width = 200
        Label1.Height = 17
        Label1.Visible = True
        Label1.Caption = "Label1"
        
        Label2.Appearance = 0
        Label2.BackColor = vbWhite
        Label2.BorderStyle = 1
        Label2.Width = 200
        Label2.Height = 17
        Label2.Visible = True
        Label2.Caption = "Label2"
        
        Label3.Appearance = 0
        Label3.BackColor = vbWhite
        Label3.BorderStyle = 1
        Label3.Width = 200
        Label3.Height = 17
        Label3.Visible = True
        Label3.Caption = "Label3"
        
        Label4.Appearance = 0
        Label4.BackColor = vbWhite
        Label4.BorderStyle = 1
        Label4.Width = 200
        Label4.Height = 17
        Label4.Visible = True
        Label4.Caption = "Label4"
        
        '--------
        Const GAP = 10!
    
        On Error Resume Next
        PictureC.Move ScaleLeft + GAP, ScaleTop + GAP, ScaleWidth - GAP - GAP, 24
        PictureC.Cls
        
        PictureU.Move ScaleLeft + GAP, PictureC.Top + PictureC.Height + 1, ScaleWidth - GAP - GAP, 50
        PictureU.Cls
        
        HScroll1.Move PictureU.Left, PictureU.Top + PictureU.Height + 0, PictureU.Width, 18
        
        Label4.Move PictureU.Left, ScaleHeight - Label1.Height - GAP, PictureU.Width, 17
        Label3.Move PictureU.Left, Label4.Top - Label3.Height - 2, PictureU.Width, 17
        Label2.Move PictureU.Left, Label3.Top - Label3.Height - 2, PictureU.Width, 17
        Label1.Move PictureU.Left, Label2.Top - Label2.Height - 2, PictureU.Width, 17
        
        '--------
        Const PictureL_WIDTH = 10!, PictureL_HEIGHT = 20!
        Const PictureR_WIDTH = 10!, PictureR_HEIGHT = 20!
    
        PictureL.Move (PictureC.ScaleWidth - PictureL_WIDTH!) \ 2&, _
                      (PictureC.ScaleHeight - PictureL_HEIGHT) \ 2&, _
                      PictureL_WIDTH, _
                      PictureL_HEIGHT
        PictureL.Cls
        
        PictureR.Move PictureL.Left + PictureL.Width + 5, _
                      PictureL.Top, _
                      PictureR_WIDTH, _
                      PictureR_HEIGHT
        PictureR.Cls
    
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
        PictureU.Refresh
        
        HScroll1.LargeChange = PictureC.ScaleWidth
        oldHScrollVal = HScroll1.Value
        
        Call DisplayMetrics
    
    End Sub
    
    
    Private Sub PictureC_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Select Case Button
            Case vbLeftButton:  PictureL.Left = X - PictureL.Width * 0.5!
    
                If PictureL.Left < PictureC.ScaleLeft Then
                    PictureL.Left = PictureC.ScaleLeft
                ElseIf PictureL.Left + PictureL.Width > PictureC.ScaleWidth - PictureR.Width Then
                    PictureL.Left = PictureC.ScaleWidth - PictureL.Width - PictureR.Width
                End If
    
                If PictureR.Left < PictureL.Left + PictureL.Width Then
                    PictureR.Left = PictureL.Left + PictureL.Width
                End If
    
            Case vbRightButton: PictureR.Left = X - PictureR.Width * 0.5!
    
                If PictureR.Left < PictureC.ScaleLeft + PictureL.Width Then
                    PictureR.Left = PictureC.ScaleLeft + PictureL.Width
                ElseIf PictureR.Left + PictureR.Width > PictureC.ScaleWidth Then
                    PictureR.Left = PictureC.ScaleWidth - PictureR.Width
                End If
    
                If PictureL.Left + PictureL.Width > PictureR.Left Then
                    PictureL.Left = PictureR.Left - PictureL.Width
                End If
                
        End Select
        
    End Sub
    
    
    Private Sub PictureC_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        PictureL.BackColor = vbRed
        PictureR.BackColor = vbBlue
        
        PictureC.ToolTipText = "Left Click to set Left Marker, Right Click to set Right Marker"
        
    End Sub
    
    
    Private Sub PictureL_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        If Button = vbLeftButton Then
            m_StartX = X
        End If
        
        Label4.Caption = ""
        
    End Sub
    
    
    Private Sub PictureL_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Dim PictureL_Left  As Single
        Dim PictureL_Right As Single
    
        PictureL.BackColor = vbGreen
        PictureR.BackColor = vbBlue
        
        TimerL.Enabled = False
        TimerR.Enabled = False
    
        If Button = vbLeftButton Then
    
            Select Case (PictureL.Left + PictureL.Width)
                
                Case Is <> PictureR.Left
                    
                    'Left and right markers NOT touching
                    DraggingFlag = 1
                    
                    PictureL_Left = PictureL.Left + (X - m_StartX)
                    PictureL_Right = PictureL_Left + PictureL.Width
            
                    ' Past LEFT side of container
                    If PictureL_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureL_Left = PictureC.ScaleLeft
                    End If
            
                    ' Past RIGHT side of container
                    If PictureL_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureL_Left = PictureC.ScaleWidth - PictureL.Width
                    End If
            
                    PictureL.Left = PictureL_Left
            
                    ' Bump against RIGHT marker
                    If PictureL_Right > PictureR.Left Then
                        DraggingFlag = 3
                        PictureR.Left = PictureL.Left + PictureL.Width
                    End If
    
                Case Is = PictureR.Left
                    
                    'Left and right markers ARE touching
                    DraggingFlag = 3
                
                    PictureL_Left = PictureL.Left + (X - m_StartX)
                    PictureL_Right = PictureL_Left + PictureL.Width
            
                    ' Past LEFT side of container
                    If PictureL_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureL_Left = PictureC.ScaleLeft
                    End If
            
                    ' Past RIGHT side of container
                    If (PictureL_Right + PictureL.Width + PictureR.Width) > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureL_Left = PictureC.ScaleWidth - PictureL.Width - PictureR.Width
                    End If
            
                    PictureL.Left = PictureL_Left
            
                    ' Bump against RIGHT marker
                    If PictureL_Right > PictureR.Left Then
                        PictureR.Left = PictureL.Left + PictureL.Width
                    End If
                    
            End Select
    
        End If
        
        Call DisplayMetrics
        
        PictureL.ToolTipText = "Drag to reposition Left Marker"
        
    End Sub
    
    
    Private Sub PictureL_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        PictureL.BackColor = vbRed
        DraggingFlag = 0
        
        '-------------------------------------------------------------------------------------------
        ' Kludge fixes for viewport (sometimes) auto-scrolling ONE viewport width beyond marker
        ' Windows7 only?
    
        ' Fix for SINGLE left marker @ left edge
        If (PictureL.Left + HScroll1.Value) - HScroll1.Value = PictureC.ScaleWidth Then
            Label4.Caption = (PictureL.Left + HScroll1.Value) - HScroll1.Value & _
                            " single left marker @ left edge"
            MsgBox "Glitch"
            HScroll1.Value = (PictureL.Left + HScroll1.Value)
        End If
        
        ' Fix for SINGLE left marker @ right edge
        If (PictureL.Left + HScroll1.Value) - HScroll1.Value = -PictureL.Width Then
            Label4.Caption = (PictureL.Left + HScroll1.Value) - HScroll1.Value & _
                            " single left marker @ right edge"
            MsgBox "Glitch"
            HScroll1.Value = HScroll1.Value - PictureC.ScaleWidth
        End If
    
        ' Fix for TOUCHING left and right markers @ right edge
        If (PictureL.Left + HScroll1.Value) - HScroll1.Value = -(PictureL.Width + PictureR.Width) Then
            Label4.Caption = (PictureL.Left + HScroll1.Value) - HScroll1.Value & _
                            " touching right and left markers @ right edge"
            MsgBox "Glitch"
            HScroll1.Value = HScroll1.Value - PictureC.ScaleWidth
        End If
        '-------------------------------------------------------------------------------------------
    
    End Sub
    
    
    Private Sub PictureR_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        If Button = vbLeftButton Then
            m_StartX = X
            DraggingFlag = 0
        End If
        
        Label4.Caption = ""
        
    End Sub
    
    
    Private Sub PictureR_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Dim PictureR_Left As Single
        Dim PictureR_Right As Single
        
        PictureR.BackColor = vbGreen
        PictureL.BackColor = vbRed
        
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        If Button = vbLeftButton Then
    
            Select Case PictureR.Left
            
                Case Is <> (PictureL.Left + PictureL.Width)
                    
                    'Left and right markers NOT touching
                    DraggingFlag = 2
    
                    PictureR_Left = PictureR.Left + (X - m_StartX)
                    PictureR_Right = PictureR_Left + PictureR.Width
                    
                    ' Past LEFT side of container
                    If PictureR_Left < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                        'Contain at LEFT side
                        PictureR_Left = PictureC.ScaleLeft
                    End If
                    
                    ' Past RIGHT side of container
                    If PictureR_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureR_Left = PictureC.ScaleWidth - PictureR.Width
                    End If
            
                    PictureR.Left = PictureR_Left
            
                    ' Bump against LEFT marker
                    If PictureR_Left < (PictureL.Left + PictureL.Width) Then
                        DraggingFlag = 3
                        PictureL.Left = PictureR.Left - PictureL.Width
                    End If
    
                Case Is = (PictureL.Left + PictureL.Width)
                
                    'Left and right markers ARE touching
                    DraggingFlag = 3
    
                    PictureR_Left = PictureR.Left + (X - m_StartX)
                    PictureR_Right = PictureR_Left + PictureR.Width
                
                    ' Past LEFT side of container
                    If (PictureR_Left - PictureL.Width) < PictureC.ScaleLeft Then
                        'Invoke auto-scroll
                        TimerL.Enabled = True
                            
                        '***************************************************************************
                        ' Fix so Windows7 and Windows10 behave the same
                        
                        If HScroll1.Value > 0 Then
                            'Contain at LEFT side
                            PictureR_Left = PictureC.ScaleLeft + PictureL.Width
                        Else
                            PictureR_Left = PictureC.ScaleLeft + PictureL.Width
                            PictureL.Left = PictureR.Left
                        End If
                        '***************************************************************************
                    End If
                    
                    ' Past RIGHT side of container
                    If PictureR_Right > PictureC.ScaleWidth Then
                        'Invoke auto-scroll
                        TimerR.Enabled = True
                        'Contain at RIGHT side
                        PictureR_Left = PictureC.ScaleWidth - PictureR.Width
                    End If
            
                    PictureR.Left = PictureR_Left
            
                    ' Bump against LEFT marker
                    If PictureR_Left < (PictureL.Left + PictureL.Width) Then
                        PictureL.Left = PictureR.Left - PictureL.Width
                    End If
                    
            End Select
    
        End If
        
        Call DisplayMetrics
        
        PictureR.ToolTipText = "Drag to reposition Right Marker"
    
    End Sub
    
    
    Private Sub PictureR_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
        
        TimerL.Enabled = False
        TimerR.Enabled = False
        
        PictureR.BackColor = vbBlue
        DraggingFlag = 0
        
        '-------------------------------------------------------------------------------------------
        ' Kludge fixes for viewport (sometimes) auto-scrolling ONE viewport width beyond marker
        ' Windows7 only?
        
        ' Fix for SINGLE right marker @ left edge
        If (PictureR.Left + HScroll1.Value) - HScroll1.Value = PictureC.ScaleWidth Then
            Label4.Caption = (PictureR.Left + HScroll1.Value) - HScroll1.Value & _
                            " single right marker @ left edge"
            MsgBox "Glitch"
            HScroll1.Value = HScroll1.Value + PictureC.ScaleWidth
        End If
        
        ' Fix for SINGLE right marker @ right edge
        If (PictureR.Left + HScroll1.Value) - HScroll1.Value = -PictureR.Width Then
            Label4.Caption = (PictureR.Left + HScroll1.Value) - HScroll1.Value & _
                            " single right marker @ right edge"
            MsgBox "Glitch"
            HScroll1.Value = HScroll1.Value - PictureC.ScaleWidth
        End If
    
        ' Fix for TOUCHING right and left markers @ left edge
        If (PictureL.Left + HScroll1.Value) - HScroll1.Value = PictureC.ScaleWidth Then
            Label4.Caption = (PictureL.Left + HScroll1.Value) - HScroll1.Value & _
                            " touching right and left markers @ left edge"
            MsgBox "Glitch"
            HScroll1.Value = HScroll1.Value + PictureL.Left
        End If
        '-------------------------------------------------------------------------------------------
        
    End Sub
    
    
    Private Sub HScroll1_Change()
    
        PictureU.Cls
    
        For i = 1 To numCols
            PictureU.Line (i * colWidth - HScroll1.Value, 0)-(i * colWidth - HScroll1.Value, PictureU.Height), vbBlack
        Next i
    
        If (DraggingFlag And 1) <> 1 Then
          PictureL.Left = PictureL.Left - HScroll1.Value + oldHScrollVal
        End If
        
        If (DraggingFlag And 2) <> 2 Then
          PictureR.Left = PictureR.Left - HScroll1.Value + oldHScrollVal
        End If
    
        oldHScrollVal = HScroll1.Value
        
        Call DisplayMetrics
    
        PictureC.Refresh 'passel fix
    
    End Sub
    
    
    Private Sub HScroll1_Scroll()
        
        Call HScroll1_Change
    
    End Sub
    
    
    Private Sub TimerL_Timer()
    
        If PictureL.Left <= PictureC.ScaleLeft And HScroll1.Value = HScroll1.Min Then
            TimerL.Enabled = False
        End If
        
        If (HScroll1.Value - PictureC.ScaleWidth) >= HScroll1.Min Then
            HScroll1.Value = HScroll1.Value - PictureC.ScaleWidth
        Else
            HScroll1.Value = HScroll1.Min
        End If
    
    End Sub
    
    
    Private Sub TimerR_Timer()
    
        If PictureR.Left + PictureR.Width >= PictureC.ScaleWidth And HScroll1.Value = HScroll1.Max Then
            TimerR.Enabled = False
        End If
    
        If (HScroll1.Value + PictureC.ScaleWidth) <= HScroll1.Max Then
            HScroll1.Value = HScroll1.Value + PictureC.ScaleWidth
        Else
            HScroll1.Value = HScroll1.Max
        End If
    
    End Sub
    
    
    Private Sub DisplayMetrics()
    
        Label1.Caption = "HScroll = " & HScroll1.Value
        Label2.Caption = "(LM=" & PictureL.Left & ")" & _
                         "(RM=" & PictureR.Left & ")"
        Label3.Caption = "(LM=" & PictureL.Left + HScroll1.Value & ")" & _
                         "(RM=" & PictureR.Left + HScroll1.Value & ")"
                         
    End Sub
    Last edited by mms_; Nov 24th, 2019 at 03:57 AM.

  19. #19
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,612

    Re: Moving a PictureBox Inside a PictureBox

    "And" in this case is a Boolean math operation, I believe they referred to Boolean operations and logic as "Boolean Algebra" when I learned about it in my Navy School days.

    Part of what computers do on their lowest level is operate on the binary digits (i.e. bits) of the numbers in registers.
    There are various logical gate structures that are defined to take a bit from each number as an input, and output a resulting bit based on a Boolean operation. These operations are defined to work on all the bits in parallel between two values.

    You can look up Boolean Algebra and Boolean Operations and Logic, and probably find many more complete tutorials, but I'll cover the "And" operations, and a couple more, i.e. "Or" and "XOr".

    We'll limit our example to a three bit number. A three bit unsigned number can represent the values 0 to 7.
    Code:
    binary = decimal
    000 = 0
    001 = 1
    010 = 2
    011 = 3
    100 = 4
    101 = 5
    110 = 6
    111 = 7
    The "truth" table for the "And" operation (remember two bits in, one bit out) is
    Code:
    0 And 0 = 0   'i.e. False And False = False
    0 And 1 = 0   'i.e. False And True = False
    1 And 0 = 0   'i.e. True  And False = False
    1 And 1 = 1   'i.e. True  And True = True.
    Logically, for the result to be True, both the Left AND Right values have to be True.
    I am 30 And you are 20. That whole statement is only true if both statements joined by the And are true.

    In the Case of "Or", then the result is true if either input is true.
    I am 30 Or you are 20. If I am 30 Or you are 20 then the statement is true. If I am 30 and you are 20, then the statement is still true.
    So, the "truth" table for the "Or" operation looks like
    Code:
    0 Or 0 = 0   'Both sides False, the result is False
    0 Or 1 = 1   'Either side, or both sides True, then the result is True
    1 Or 0 = 1
    1 Or 1 = 1
    Now, "XOr" (exclusive Or) is kind of a pedantic version of "Or". It is like saying that when you make the statement "I am 30 Or you are 20", that the two statements are exclusive, i.e. only one side can be true, and the other has to be false for the statement to be true.
    Exclusive Or is the logic that is used in those logic word puzzles. If I am 30, then you are not 20, and if I am not 30, then you must be 20.

    While a logic puzzle depends on the exclusive nature of one statement being true, and the other having to be false, in the case of the XOR operation, that is only the middle of the story, and the operation result is True if that is the condition, i.e. one side is true and the other side is false, then the result is true. But, if you have the other two cases, where both sides are the same (neither is True or Both are True), then the result is false.
    So, XOr only returns True if both sides are different.
    That truth table looks like
    Code:
    0 Xor 0 = 0  'False Xor False = False, the two sides are not different
    0 Xor 1 = 1  'False Xor True = True, the two sides are different
    1 Xor 0 = 1  'True Xor False = True, the two sides are different
    1 Xor 1 = 0  'True Xor True = False, the two sides are not different.
    Ok, so we have the statement
    (DraggingFlag And 1)

    DraggingFlag can be set to 0, 1, 2 or 3, if you implemented all my code.
    Using our three bit number set, then the binary representation of Dragging flag can be the following four values,
    0 (000), 1 (001), 2 (010) and 3 (011). The value is in the ().

    All the bits of the DraggingFlag will be Anded with all the bits of the the number 1, and the resulting number will be compared to 1.
    Likewise, in the other check (DraggingFlag And 2), all the bits of the DraggingFlag will be anded with the bits of the number 2, and the resulting number will be compared to 2.
    I'll try to illustrate that here, using the lowest three bits that make up the numbers.
    I'm only showing the operation on the lowest bit of the three, but you can complete the operations on the other two bits (the operation is between the parallel bits, here that would be the bits in the same column. I'm only expanding the rightmost column (i.e. bit) for the first case of anding with 1, but the operation applies to all columns of bits.

    The And Operation for Anding with 2 shows the results as well, and if I expanded it, I would be looking at the second column from the right bit, as that is the bit that is set for the value 2.
    If I wanted to test the next bit (the third column), then I would use the mask 100 (i.e. 4), to isolate that bit from the rest using the And operation.
    Code:
    DraggingFlag   0    1    2    3
    binary        000  001  010  011
    And with 1    001  001  001  001
                    |    |    |    |
                    |    |    |    1 and 1 = 1
                    |    |    0 and 1 = 0
                    |    1 and 1 = 1
                    0 and 1 = 0
                    |    |    |    |
                  ------------------
    result        000  001  000  001
                   0    1    0    1
    
    DraggingFlag   0    1    2    3
    binary        000  001  010  011
    And with 2    010  010  010  010
                  ------------------
    result        000  000  010  010
                   0    0    2    2
    I don't know if that helps at all. You can try to read up on it from various different sources, to see if multiple ways of describing it will make it click.
    It comes down to the binary representation of numbers, and operations on those binary representations to get a new binary number based on the two numbers input and the type of operation.

    From a higher perspective, there are three common uses for the three operations I mentioned.

    AND is used to pull the bits from a number, to isolate those bits from the rest of the bits in the number. This is useful when you want to map multiple small values of just a few bits into a larger collection of bits, i.e. if you only need a number in the range of 0 to 9, that will fit in a four bit field, so you can store two of these numbers in a single byte. That is essentially the definition of Binary Coded Decimal. The decimal number 0 to 9 is save as a four bit number and two of them are packed into a single byte, one in the lower four bits, and the other in the upper four bits.

    OR is used to Set bits within a number. You can OR a value into the middle of a group of bits without changing the value of the other bits outside the range you want to change.

    XOR is used to toggle bits. You can change the state of a bit from 0 to 1 or 1 to 0 by XOR'ing the bit with 1.
    One example would be to XOR two colors together to get a third color. That color will be a combination of the two colors, where bits that don't match between the two colors are flipped to the other state (0 to 1 or 1 to 0).
    If you XOR that third color with either of the original colors, you get the other color.
    Say you have a picturebox, and you set its backcol
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  20. #20

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Thank you for the explanation.

    I get the low-level bit math in principle, but still, as I read
    If (DraggingFlag And 1) <> 1
    my old brain just doesn't quickly process the line

    Your last "code wrapped table" showing the math, does help however.

    So am I correct in saying that
    If (DraggingFlag And 1) <> 1
    can be re-written
    If DraggingFlag <>1 And DraggingFlag <> 3 ??

    and
    If (DraggingFlag And 2) <> 2
    can be re-written
    If DraggingFlag <>2 And DraggingFlag <> 3 ??

    Even with the two translations I present, I am not sure if I should be using the And or the Or operator

    What's wrong with me?

  21. #21
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,612

    Re: Moving a PictureBox Inside a PictureBox

    Quote Originally Posted by mms_ View Post
    ...
    So am I correct in saying that
    If (DraggingFlag And 1) <> 1
    can be re-written
    If DraggingFlag <>1 And DraggingFlag <> 3 ??

    and
    If (DraggingFlag And 2) <> 2
    can be re-written
    If DraggingFlag <>2 And DraggingFlag <> 3 ??
    ...
    Within the limited range of numbers we are dealing with, then the result is the same so you could rewrite it that way.
    But, if I were going to check for the values directly rather use the values as combination of flags, then I would take the common check out and do it first.
    Code:
    If DraggingFlag <> 3  Then  'if we are not dragging both markers
        If DraggingFlag <> 1 Then 'if we are not dragging the left marker
            'Scroll the left marker
        End If
        If DraggingFlag <> 2 Then 'if we are not dragging the right marker
            'Scroll the right marker
        End If
    End If
    Sometimes, checking for the not equal (<>) condition to do something is referred to as negative logic and can be a source of some confusion.
    In this case, making the checks "positive", but doing the opposite action, I think would be hiding the negative logic in a sense, and possibly be even more confusing, i.e.
    Code:
    If DraggingFlag = 0 Or DraggingFlag = 2 Then 'if we are not dragging any markers Or only dragging the Right marker 
        'Scroll the LeftMarker
    End If
    If DraggingFlag = 0 Or DraggingFlag = 1 Then 'if we are not dragging any markers Or only dragging the Left Marker
       'Scroll the RightMarker
    End If
    With a bit of redundancy, you could use a Select Case statement
    Code:
       
    SelectCase DraggingFlag
        Case 0  'neither marker being dragged
            'Scroll the LeftMarker
            'Scroll the RightMarker
    
        Case 1  'the LeftMarker being dragged
            'Scroll the RightMarker
    
        Case 2  'the RightMarker being dragged
            'Scroll the LeftMarker
    End Select
    The fourth case, Case 3, doesn't have to be written since you don't want to scroll either marker when they are both being dragged.
    Again, we're checking for dragging of one marker, and scrolling the other which seems non-intuitive. That is because of the nature of the issue, i.e. that we want to disable the scrolling of the marker when we are dragging the marker.

    As far as whether you should use the And or Or, even someone who has done this for years, may still have to think a bit when coding a line that uses them, because of the way we use the conjunction "And" in a sentence to include two things, whereas in Boolean logic the meaning is a bit different. We might think of And in speaking as being a form of addition, i.e. 1 And 2 is 3, but a boolean operation of 1 And 2 is not 3 it is 0. 1 Or 2 is 3 in Boolean math, and in speaking, 1 Or 2 just means one or the other, it doesn't combine the values in some way.

    So it is a bit easy to trip up on the usage of And and Or if we let our focus wander a bit, or write something quick without thinking it through.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  22. #22

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Very confusing (for me anyway), but in the context of this project, I think I understand well enough.

    Thanks!

  23. #23

    Thread Starter
    Addicted Member
    Join Date
    Nov 2018
    Posts
    192

    Re: Moving a PictureBox Inside a PictureBox

    Quote Originally Posted by Victor Bravo VI
    VB6 unfortunately limits PictureBox sizes to just 16,383 (&H3FFF) pixels.
    Quote Originally Posted by passel
    But, as Victor Bravo VI has rediscovered, there is a limit to the size of the actual picture in a picturebox, which restricts the values you can set PictureR.Left and PictureL.Left to (to +/- 16386, give or take) within PictureC...
    So in trying to move forward, I discover this for myself.

    I cannot implement the draggable markers in my project.
    I will have to use Victor Bravo VI's "click-set" markers on their own.
    Last edited by mms_; Nov 26th, 2019 at 08:20 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width