Solved Class Module to Drag/Drop Multiple Labels on Form (1 Viewer)

G37Sam

Registered User.
Local time
Today, 16:12
Joined
Apr 23, 2008
Messages
456
This forum and its members have been providing me with solutions for 13 years now, I need a few lifetimes of posting on here to give back what I owe :)
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 08:12
Joined
May 21, 2018
Messages
5,081
Follow up question if I may: triggering these 3 mouse events now calls the code in your class module bypassing any code in the form itself, correct? How does this impact other events (on_click etc..)? Must they all fall under the class module now or can they be coded in the main form?
I demonstrate this exact thing here. As @arnelgp stated, you can still trap the event in the form's class too.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 08:12
Joined
May 21, 2018
Messages
5,081
My initial solution involved a traditional text box at first wherein the user just keyed in the distances from the top and left borders but the user wanted to drag/drop with their mouse and then use up down left right buttons to fine tune where everything sits.
If you want to practice. I would add some key down events too to make this user friendly. Still allow the user to drag and drop, but that is a little clunky. It is hard to actually position since it is not a true drag and drop. You do not see where it is before dropping. You should add events for arrow, down, left, right. Then if it is a right arrow nudge it right. Add a small increment to the x position. Same for the other arrows.

However there are no events to do this with a label. You can format textboxes as labels and then you would have more events available. Here is a demo with moving with the keys. Also show how to instantiate a group of custom controls using a collection. Here is the new form code. Which makes it a lot simpler if you have lots of "labels".

Code:
Private DragBoxes As New Collection


Private Sub Form_Load()
  Dim DB As DragBox
  Dim ctr As Access.Control
  For Each ctr In Me.Controls
    If ctr.Tag = "DB" Then
      Set DB = New DragBox
      DB.Initialize ctr
      DragBoxes.Add DB
    End If
  Next ctr
End Sub

You would need to put in code to check that you do not try to move it outside the limits of the form. You can trap that error or write the code.
 

Attachments

  • DragTextBox2.accdb
    420 KB · Views: 17
Last edited:

G37Sam

Registered User.
Local time
Today, 16:12
Joined
Apr 23, 2008
Messages
456
You read my mind! I actually already setup buttons in place around each label for the tiny increments - looks something like this now. I like the keydown better though - fun times ahead!

1621401630608.png
 

G37Sam

Registered User.
Local time
Today, 16:12
Joined
Apr 23, 2008
Messages
456
Follow up question: can the class module work with more than one access control? The code you shared earlier only passes the label to the classmodule, what if i want to pass 4 more command buttons along with it?

I basically want to pass the label and its 4 neighboring arrow buttons to the class module
Once dragged, the neighboring arrows should move with reference to it
 

arnelgp

once i caught a fish alive...
Local time
Today, 20:12
Joined
May 7, 2009
Messages
13,202
you are Over complicating a simple "cheque" filling/printing?
just cut a paper with same size as your cheque.
print and adjust the position of the label/textbox
until you get it right.
 

G37Sam

Registered User.
Local time
Today, 16:12
Joined
Apr 23, 2008
Messages
456
you are Over complicating a simple "cheque" filling/printing?
just cut a paper with same size as your cheque.
print and adjust the position of the label/textbox
until you get it right.

that's what this form is for - every bank has a few cheque formats
 

Minty

AWF VIP
Local time
Today, 13:12
Joined
Jul 26, 2013
Messages
8,600
you can use Report (one report for specific bank), can do?
Where's the fun in that ;)

It looks like the OP is saving the results for each cheque type, so it's like the end-user designing the reports for you, without giving you the cheque books.

Who still writes cheques anyway?
 

arnelgp

once i caught a fish alive...
Local time
Today, 20:12
Joined
May 7, 2009
Messages
13,202
Where's the fun in that
he is doing it in the Form.
form coordinates does not translate to Paper (report) coordinate, or it is.

that part is more fun.
 
Last edited:

G37Sam

Registered User.
Local time
Today, 16:12
Joined
Apr 23, 2008
Messages
456
you can use Report (one report for specific bank), can do?

Sorry I wasn't clear enough, there are around 18 banks here, within each of these 18 there's a few different templates so we're looking at least 35 reports here. Hard coding also means that when the bank's marketing dept decides to get creative again, i'll be getting another call to create a new report.

I'm not even talking about foreign banks yet.

Where's the fun in that ;)

It looks like the OP is saving the results for each cheque type, so it's like the end-user designing the reports for you, without giving you the cheque books.

Who still writes cheques anyway?

true - hard coding is not fun. And every cheque format is saved in the database.

Rent/car payments/and various transactions in the middle east still need cheques. Banks won't give your company a facility without guarantee cheques either.

he is doing it in the Form.
form coordinates does not translate to Paper (report) coordinate, or it is.

that part is more fun.

It does more or less, but there is a trial and error element (one could get it right from the 2nd try with a ruler and a little common sense)
 

NauticalGent

CopyPaster of the First Order
Local time
Today, 08:12
Joined
Apr 27, 2015
Messages
3,860
Who still writes cheques anyway?
What are "cheques"?

At first, I thought you were talking about the demonym for Czechoslovakia but it is obvious, even to me, that you are not... :cool:
 

arnelgp

once i caught a fish alive...
Local time
Today, 20:12
Joined
May 7, 2009
Messages
13,202
within each of these 18 there's a few different templates so we're looking at least 35 reports here
count how many reports in this db alone.
rep1.png
rep2.png
rep3.png
rep4.png
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 08:12
Joined
May 21, 2018
Messages
5,081
Follow up question: can the class module work with more than one access control? The code you shared earlier only passes the label to the classmodule, what if i want to pass 4 more command buttons along with it?

I basically want to pass the label and its 4 neighboring arrow buttons to the class module
Once dragged, the neighboring arrows should move with reference to it
Yes but since you are capturing events you will have to double up the event procedures. First you make a variable for each control type.

Code:
Private WithEvents mDragTextBox As Access.TextBox
---------------- Add another control type
Private WithEvents mCombo As Access.ComboBox

Then in the initialize you modify to pass in a generic control

Code:
Public Sub Initialize(TheTextBox As Access.TextBox)
  TheTextBox.OnMouseDown = "[Event Procedure]"
  TheTextBox.OnMouseMove = "[Event Procedure]"
  TheTextBox.OnMouseUp = "[Event Procedure]"
  TheTextBox.OnKeyDown = "[Event Procedure]"
  Set Me.DragTextBox = TheTextBox
 
End Sub

------------------------- Change to
Public Sub Initialize(TheControl As Access.Control)
  TheControl.OnMouseDown = "[Event Procedure]"
  TheControl.OnMouseMove = "[Event Procedure]"
  TheControl.OnMouseUp = "[Event Procedure]"
  TheControl.OnKeyDown = "[Event Procedure]"
  Select case TheControl.controlType
  case AcTextBox
    set me.DragTextBox = theControl
  case AcCombobox
    Set Me.DragCombo = theControl
  end select
 End Sub

Now you have to add the same events in the class module for the DragCombo that you had for the DragTextBox. Basically copy/paste and change the name.

Also, I do not know if what you are doing makes sense there may be a better way with reports, but I will leave that to others.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 08:12
Joined
May 21, 2018
Messages
5,081
Here is the code. I said combo box but you meant command buttons.
Code:
Option Compare Database
Option Explicit

Private WithEvents mDragTextBox As Access.TextBox
Private WithEvents mDragcommandbutton As Access.CommandButton
Dim MoveMe As Boolean
Dim InitX As Long
Dim InitY As Long
Dim DragX As Long
Dim DragY As Long

Private Sub mDragTextBox_KeyDown(KeyCode As Integer, Shift As Integer)
  Dim x As Long
  Dim y As Long
  Dim shiftSpeed As Integer
  x = Me.DragTextBox.Left
  y = Me.DragTextBox.Top
  If Shift = acShiftMask Then
    shiftSpeed = 100
  Else
    shiftSpeed = 10
  End If
  Select Case KeyCode
    Case vbKeyUp
          Me.DragTextBox.Top = y - shiftSpeed
    Case vbKeyDown
       Me.DragTextBox.Top = y + shiftSpeed
    Case vbKeyRight
       Me.DragTextBox.Left = x + shiftSpeed
    Case vbKeyLeft
        Me.DragTextBox.Left = x - shiftSpeed
  End Select
End Sub

Private Sub mDragTextBox_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
    If Button = acLeftButton And Shift = acCtrlMask Then
        MoveMe = True
        InitX = x
        InitY = y
    Else
        MoveMe = False
    End If
End Sub


Private Sub mDragTextBox_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
If MoveMe = True Then
    DragX = x
    DragY = y
End If
End Sub
  
Private Sub mDragTextBox_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
If MoveMe = True Then
    With DragTextBox
        .Left = .Left + DragX - InitX
        .Top = .Top + DragY - InitY
    End With
    MoveMe = False
End If
End Sub
'------------------------------------------ Duplicate code

Private Sub mDragcommandbutton_KeyDown(KeyCode As Integer, Shift As Integer)
  Dim x As Long
  Dim y As Long
  Dim shiftSpeed As Integer
  x = Me.Dragcommandbutton.Left
  y = Me.Dragcommandbutton.Top
  If Shift = acShiftMask Then
    shiftSpeed = 100
  Else
    shiftSpeed = 10
  End If
  Select Case KeyCode
    Case vbKeyUp
          Me.Dragcommandbutton.Top = y - shiftSpeed
    Case vbKeyDown
       Me.Dragcommandbutton.Top = y + shiftSpeed
    Case vbKeyRight
       Me.Dragcommandbutton.Left = x + shiftSpeed
    Case vbKeyLeft
        Me.Dragcommandbutton.Left = x - shiftSpeed
  End Select
End Sub

Private Sub mDragcommandbutton_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
    If Button = acLeftButton And Shift = acCtrlMask Then
        MoveMe = True
        InitX = x
        InitY = y
    Else
        MoveMe = False
    End If
End Sub


Private Sub mDragcommandbutton_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
If MoveMe = True Then
    DragX = x
    DragY = y
End If
End Sub
  
Private Sub mDragcommandbutton_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
If MoveMe = True Then
    With Dragcommandbutton
        .Left = .Left + DragX - InitX
        .Top = .Top + DragY - InitY
    End With
    MoveMe = False
End If
End Sub
'--------------------------------------------------------------------------------------


Public Sub Initialize(TheControl As Access.Control)
  TheControl.OnMouseDown = "[Event Procedure]"
  TheControl.OnMouseMove = "[Event Procedure]"
  TheControl.OnMouseUp = "[Event Procedure]"
  TheControl.OnKeyDown = "[Event Procedure]"
  Select Case TheControl.ControlType
  Case acTextBox
    Set Me.DragTextBox = TheControl
  Case acCommandButton
    Set Me.Dragcommandbutton = TheControl
  End Select
End Sub
Public Property Get DragTextBox() As Access.TextBox
    Set DragTextBox = mDragTextBox
End Property

Public Property Set DragTextBox(ByVal objNewValue As Access.TextBox)
    Set mDragTextBox = objNewValue
End Property
Public Property Get Dragcommandbutton() As Access.CommandButton
    Set Dragcommandbutton = mDragcommandbutton
End Property

Public Property Set Dragcommandbutton(ByVal objNewValue As Access.CommandButton)
    Set mDragcommandbutton = objNewValue
End Property

The drag drop works, but the key press does not really work because it will leave the command button once pressed. So you have to mouse back in each time. Not very useful. Also in the form you are probably capturing click events on the command buttons. Those would need to be changed to mouse down events. Then check if the ctrl button is pressed or not. If not pressed do your original Click event code.

As I said, I am not sure of the utility of this. However this is a very good academic example for using a class module. This would be very cumbersome to write without a class. This demonstrates lots of interesting principals.
1. One class handling multiple controls and control types
2. Trapping multiple control events through withevents
3. Trapping the same events in the form and custom class

The only thing to add would be a Custom Collection (DragBoxes) for your custom class. This would make instantiating even simpler.
Here is a good example. Just like a regular collection your custom collection will have Add, Remove, Item, Count, Clear method/property.
Custom Collection Class – Daily Dose of Excel
 

Attachments

  • DragTextBox3.accdb
    444 KB · Views: 16

G37Sam

Registered User.
Local time
Today, 16:12
Joined
Apr 23, 2008
Messages
456
Oh man - you've given me enough learning to keep me busy for a few weeks! Will study this and get back to you if I have further questions.

You're a hidden gem of knowledge MajP, thanks again for your time and efforts.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 08:12
Joined
May 21, 2018
Messages
5,081
I would think you could still do this dynamically for each check instead of hard coding checks. I would think you could have a single Check report. Then have a table that stores the positions of each control and if that control is visible or not. Then when you load that check for that bank it positions the controls dynamically in the on open event. This way to add a new report you just add a record in the table instead of redesigning. Now you may be able to use this dynamic form to then write to the table the correction positions after moving them to where you want. Not sure if that is doable. Because currently (if I understand), if you move these controls around they do not persist. Next time you print you have to do it all over again.
 

theDBguy

I’m here to help
Staff member
Local time
Today, 05:12
Joined
Oct 29, 2018
Messages
15,542
I would think you could still do this dynamically for each check instead of hard coding checks. I would think you could have a single Check report. Then have a table that stores the positions of each control and if that control is visible or not. Then when you load that check for that bank it positions the controls dynamically in the on open event. This way to add a new report you just add a record in the table instead of redesigning. Now you may be able to use this dynamic form to then write to the table the correction positions after moving them to where you want. Not sure if that is doable. Because currently (if I understand), if you move these controls around they do not persist. Next time you print you have to do it all over again.
This is exactly what I was talking about earlier. It works for our needs; although in our case, we were using it for Award Certificates. Cheers! :)
 

pbaldy

Wino Moderator
Staff member
Local time
Today, 05:12
Joined
Aug 30, 2003
Messages
35,244
My app also saved the xy coordinates to a table so you could exit the application and when reloaded the controls would all show up where they were last placed. I'd agree with MajP that you could probably do this with one report.
 

Users who are viewing this thread

Top Bottom