Part I: Setting Up the Form
Part II: Helper Procedures
Part III: Determining If the Form Needs to be Saved
Part IV: The Initialize Event
In this final part, I discuss the change event for the scrollbar. The scrollbar is the means by which the user moves between records. It doesn’t have to be a scrollbar, of course, but it’s one option. We’ll need one more module level variable:
Private mlLastScrollValue As Long
Private Sub scbContact_Change()
Dim sPrompt As String
Dim sTitle As String
Dim lResp As Long
sPrompt = "Save Changes"
sTitle = "Record Has Changed"
If Me.IsDirty Then
lResp = MsgBox(sPrompt, vbYesNo, sTitle)
If lResp = vbYes Then
SaveRecord CLng(Me.scbContact.Value > mlLastScrollValue)
End If
End If
PopulateRecord
mlLastScrollValue = Me.scbContact.Value
End Sub
This procedure checks to see if the current record needs to be saved and prompts the user if it does. Then it populates the record based on the current value of the scrollbar. The module level variable holds the prior scrollbar value so the SaveRecord procedure can determine if the user is scrolling up or down. Basically, it has to know whether to save the previous or the next record because the value of the scrollbar has already changed.
Finally, the two commandbuttons have these procedures in their click events:
Private Sub cmdClose_Click()
Unload Me
End Sub
Private Sub cmdSave_Click()
If Me.IsDirty Then
SaveRecord
End If
End Sub
Here’s what the final userform looks like
And you can download it here: LinkUserform.zip
Thanks for the zip file. The multiple posts were getting a little hard to follow, so it helps to see all the pieces put together.
Thanks, too, for your great site!
Only one ‘m’ in Jimi.
– Jon
Man, I was so worried about spelling Duluth wrong (not enough to look it up, mind you) that I didn’t even think about Jimi. I was pretty confident with Bob, though.
Hi All,
Could you please help me I wouldn’t use a scroll object but that all datas change when I change the ID/Key control change thanks in advance. Franck
Ola!
Thanx for the tips.
Till yesterday I was a nitwith in Vb.
Now I understand your program and comments.
It’s perfect for the further development of the program I want to make!
Greetz Stefan
Hi
I have been looking at your code and the responses from users. I noted that one mentioned a zip file of this code. Is this still available? Like the poster, I am having difficulty putting all this together.
Thanks…
Lamar: The link to the zip file is the last line of this post (right under the image.)
ick – Thanks for the reply. I have been busy on another project but now I am trying to get back to ‘my’ stuff!
Many thanks – I hope I can make a go of this.
Lamar
bloody sensational!
my god have I been looking for this one
I have added it to a little project of mine and straight up I get a
runtime error 1004
Method ‘Range’ of object’_Worksheet’ failed
has me stumped but will keep pluggin away at it
any advice on this is appreciated
cheers
Paul V
all good mate! vb was importing the sheets as classes
Great information. Can’t download Link UserForm.zip. It’s password protected. Is it possible to see the file for part VI as well?
Misty: Thanks for the heads-up. I had some permissions problems, but they’re sorted now.
Dick,
I’m having trouble with the enable of the Save command button. If I call up the userform using the F5 it works fine. If I create a subroutine from a general module and load the UContact form from there, the enable functionality seems to break. I can’t seem to figure out the issue. Any ideas?
Thanks,
Gary
I think I figured out the issue in the previous post. Some sort of scope / instanciation issue. I changed the references in the class module to object.parent rather than the form name and it seems to work. I guess when I used the FormName.Property syntax it must have been creating a new instance of the form and manipulating it’s properties, not the form that called it.
Gary
Gary: That’s right. Thanks to Jan Karel Pieterse and Andy Pope for helping find this answer. Change the code in CControlEvents to
gCombo.Parent.IsDirty = True
End Sub
Private Sub gTextBox_Change()
gTextBox.Parent.IsDirty = True
End Sub
You are Fantastic
After so many months I found your file – I couldn’t learn how to do it – and now your example make me incredible HAPPY.
Great THX
I love you for that ;-)
link broken, not found
Fixed. Thanks for the heads-up.
as this time 11:54am CST, link to the LinkUserform.zip file still takes you to a page not found
thanks in advance,
—Woody
link above has an error in the link, it is currently:
http://www.dicks-clicks.com/Excel/downloads/LinkUserform.zip
but needs to be:
http://www.dicks-clicks.com/excel/Downloads/LinkUserform.zip
only a difference in the capitalization of “excel/downloads” portion of the link as shown above.
thanks,
—Woody
Fixed. Thanks Woody.
Can this be applied to a panning hand the grab & pull method? or is that a totally different forum. If this is already a foruum please send link to point me in the correct direction. I am using MS 2010 32 bit, on a Win 7 x64 OS compatibilty mode set for XP (service pak 2) Disable visual themes not checked, working with Visual 2005, MS 95-97, 2003-2005, 2007-2010. I am looking how & where to set value (action) grab & pull metod the panning hand I created in VB with UI, not sure about how to administer the action, all I have found is to act with in row & columns not over the entire spreadsheet.
Thanks
A waiting your reply.
Hi can you help me with this? I’ve been doing this for a week now the add, close button and the text box are functioning the only problem is the scroll bar button is not functioning and the data does not load in the userforms. No error message is coming out..thanks in advance
Option Explicit
Private mbIsDirty As Boolean
Private mcControls As Collection
Private mlLastScrollValue As Long
Private Sub cmdClose_Click()
Unload Me
End Sub
Private Sub cmdSave_Click()
If Me.IsDirty Then
SaveRecord
End If
End Sub
Private Sub CmdAdd_Click()
Dim iRow As Long
Dim ws As Worksheet
Set ws = Worksheets("TripticketData")
''find first empty row in database
iRow = ws.Cells(Rows.Count, 1) _
.End(xlUp).Offset(1, 0).Row
''check for a Trip ticket number
If Trim(Me.txttripticketno.Value) = "" Then
Me.txttripticketno.SetFocus
MsgBox "Please enter a Trip Ticket number"
Exit Sub
End If
'Copy the data to the database
ws.Cells(iRow, 1).Value = Me.txttripticketno.Value
ws.Cells(iRow, 2).Value = Me.txttech1.Value
ws.Cells(iRow, 3).Value = Me.txttech2.Value
ws.Cells(iRow, 4).Value = Me.txtPlate.Value
ws.Cells(iRow, 5).Value = Me.TxtDateOut.Value
ws.Cells(iRow, 6).Value = Me.TxtDateIn.Value
ws.Cells(iRow, 7).Value = Me.txtTimeOut.Value
ws.Cells(iRow, 8).Value = Me.txtTimein.Value
ws.Cells(iRow, 9).Value = Me.txtKMout.Value
ws.Cells(iRow, 10).Value = Me.txtkmIn.Value
ws.Cells(iRow, 11).Value = Me.txtRugsQty.Value
ws.Cells(iRow, 12).Value = Me.txtSoapQty.Value
ws.Cells(iRow, 13).Value = Me.txtfuelQty.Value
ws.Cells(iRow, 14).Value = Me.txtPailOutQty.Value
ws.Cells(iRow, 15).Value = Me.txtPailInQty.Value
ws.Cells(iRow, 16).Value = Me.txtDRSI1.Value
ws.Cells(iRow, 17).Value = Me.txtCustomer1.Value
ws.Cells(iRow, 18).Value = Me.txtlocation1.Value
ws.Cells(iRow, 19).Value = Me.txtJobDescript1.Value
ws.Cells(iRow, 20).Value = Me.txtServRep1.Value
ws.Cells(iRow, 21).Value = Me.txtArrival1.Value
ws.Cells(iRow, 22).Value = Me.txtDEPARTURE1.Value
ws.Cells(iRow, 23).Value = Me.txtOdometerstart1.Value
ws.Cells(iRow, 24).Value = Me.txtOdometerend1.Value
ws.Cells(iRow, 25).Value = Me.txtDRSI2.Value
ws.Cells(iRow, 26).Value = Me.txtCustomer2.Value
ws.Cells(iRow, 27).Value = Me.txtlocation2.Value
ws.Cells(iRow, 28).Value = Me.txtJobDescript2.Value
ws.Cells(iRow, 29).Value = Me.txtServRep2.Value
ws.Cells(iRow, 30).Value = Me.txtArrival2.Value
ws.Cells(iRow, 31).Value = Me.txtDEPARTURE2.Value
ws.Cells(iRow, 32).Value = Me.txtOdometerstart2.Value
ws.Cells(iRow, 33).Value = Me.txtOdometerend2.Value
ws.Cells(iRow, 34).Value = Me.txtDRSI3.Value
ws.Cells(iRow, 35).Value = Me.txtCustomer3.Value
ws.Cells(iRow, 36).Value = Me.txtlocation3.Value
ws.Cells(iRow, 37).Value = Me.txtJobDescript3.Value
ws.Cells(iRow, 38).Value = Me.txtservRep3.Value
ws.Cells(iRow, 39).Value = Me.txtArrival3.Value
ws.Cells(iRow, 40).Value = Me.txtDEPARTURE3.Value
ws.Cells(iRow, 41).Value = Me.txtOdometerstart3.Value
ws.Cells(iRow, 42).Value = Me.txtOdometerend3.Value
ws.Cells(iRow, 43).Value = Me.txtDRSI4.Value
ws.Cells(iRow, 44).Value = Me.txtCustomer4.Value
ws.Cells(iRow, 45).Value = Me.txtlocation4.Value
ws.Cells(iRow, 46).Value = Me.txtJobDescript4.Value
ws.Cells(iRow, 47).Value = Me.txtservRep4.Value
ws.Cells(iRow, 48).Value = Me.txtArrival4.Value
ws.Cells(iRow, 49).Value = Me.txtDEPARTURE4.Value
ws.Cells(iRow, 50).Value = Me.txtOdometerstart4.Value
ws.Cells(iRow, 51).Value = Me.txtOdometerend4.Value
ws.Cells(iRow, 52).Value = Me.txtDRSI5.Value
ws.Cells(iRow, 53).Value = Me.txtCustomer5.Value
ws.Cells(iRow, 54).Value = Me.txtlocation5.Value
ws.Cells(iRow, 55).Value = Me.txtJobDescript5.Value
ws.Cells(iRow, 56).Value = Me.txtservRep5.Value
ws.Cells(iRow, 57).Value = Me.txtArrival5.Value
ws.Cells(iRow, 58).Value = Me.txtDEPARTURE5.Value
ws.Cells(iRow, 59).Value = Me.txtOdometerstart5.Value
ws.Cells(iRow, 60).Value = Me.txtOdometerend5.Value
ws.Cells(iRow, 61).Value = Me.txtDRSI6.Value
ws.Cells(iRow, 62).Value = Me.txtCustomer6.Value
ws.Cells(iRow, 63).Value = Me.txtlocation6.Value
ws.Cells(iRow, 64).Value = Me.txtJobDescript6.Value
ws.Cells(iRow, 65).Value = Me.txtservRep6.Value
ws.Cells(iRow, 66).Value = Me.txtArrival6.Value
ws.Cells(iRow, 67).Value = Me.txtDEPARTURE6.Value
ws.Cells(iRow, 68).Value = Me.txtOdometerstart6.Value
ws.Cells(iRow, 69).Value = Me.txtOdometerend6.Value
''clear the data
Me.txttripticketno.Value = ""
Me.txttech1.Value = ""
Me.txttech2.Value = ""
Me.txtPlate.Value = ""
Me.TxtDateOut.Value = ""
Me.TxtDateIn.Value = ""
Me.txtTimeOut.Value = ""
Me.txtTimein.Value = ""
Me.txtKMout.Value = ""
Me.txtkmIn.Value = ""
Me.txtRugsQty.Value = ""
Me.txtSoapQty.Value = ""
Me.txtfuelQty.Value = ""
Me.txtPailOutQty.Value = ""
Me.txtPailInQty.Value = ""
Me.txtDRSI1.Value = ""
Me.txtCustomer1.Value = ""
Me.txtlocation1.Value = ""
Me.txtJobDescript1.Value = ""
Me.txtServRep1.Value = ""
Me.txtArrival1.Value = ""
Me.txtDEPARTURE1.Value = ""
Me.txtOdometerstart1.Value = ""
Me.txtOdometerend1.Value = ""
Me.txtDRSI2.Value = ""
Me.txtCustomer2.Value = ""
Me.txtlocation2.Value = ""
Me.txtJobDescript2.Value = ""
Me.txtServRep2.Value = ""
Me.txtArrival2.Value = ""
Me.txtDEPARTURE2.Value = ""
Me.txtOdometerstart2.Value = ""
Me.txtOdometerend2.Value = ""
Me.txtDRSI3.Value = ""
Me.txtCustomer3.Value = ""
Me.txtlocation3.Value = ""
Me.txtJobDescript3.Value = ""
Me.txtservRep3.Value = ""
Me.txtArrival3.Value = ""
Me.txtDEPARTURE3.Value = ""
Me.txtOdometerstart3.Value = ""
Me.txtOdometerend3.Value = ""
Me.txtDRSI4.Value = ""
Me.txtCustomer4.Value = ""
Me.txtlocation4.Value = ""
Me.txtJobDescript4.Value = ""
Me.txtservRep4.Value = ""
Me.txtArrival4.Value = ""
Me.txtDEPARTURE4.Value = ""
Me.txtOdometerstart4.Value = ""
Me.txtOdometerend4.Value = ""
Me.txtDRSI5.Value = ""
Me.txtCustomer5.Value = ""
Me.txtlocation5.Value = ""
Me.txtJobDescript5.Value = ""
Me.txtservRep5.Value = ""
Me.txtArrival5.Value = ""
Me.txtDEPARTURE5.Value = ""
Me.txtOdometerstart5.Value = ""
Me.txtOdometerend5.Value = ""
Me.txtDRSI6.Value = ""
Me.txtCustomer6.Value = ""
Me.txtlocation6.Value = ""
Me.txtJobDescript6.Value = ""
Me.txtservRep6.Value = ""
Me.txtArrival6.Value = ""
Me.txtDEPARTURE6.Value = ""
Me.txtOdometerstart6.Value = ""
Me.txtOdometerend6.Value = ""
Me.txttripticketno.SetFocus
End Sub
Private Sub scbTripticket_Change()
Dim sPrompt As String
Dim sTitle As String
Dim lResp As Long
sPrompt = "Save Changes?"
sTitle = "Record Has Changed"
If Me.IsDirty Then
lResp = MsgBox(sPrompt, vbYesNo, sTitle)
If lResp = vbYes Then
SaveRecord CLng(Me.scbTripticket.Value > mlLastScrollValue)
End If
End If
PopulateRecord
mlLastScrollValue = Me.scbTripticket.Value
End Sub
Private Sub UserForm_Initialize()
Dim ctlInfo As Control
Dim clsEvents As CControlEvents
''A module level collection so the classes don't
''lose scope
Set mcControls = New Collection
''Loop through the controls on the form
For Each ctlInfo In Me.Controls
''Controls with a numeric tag are data entry controls
''and that's what we want
If IsNumeric(ctlInfo.Tag) Then
'Create a new class
Set clsEvents = New CControlEvents
'Determine the type of control, set the public
'withevents class variable to the control, and
'add the class to the collection so it won't go
'out of scope while the form is shown.
Select Case TypeName(ctlInfo)
Case "TextBox"
Set clsEvents.gTextBox = ctlInfo
mcControls.Add clsEvents, CStr(ctlInfo.Tag)
Case "ComboBox"
Set clsEvents.gCombo = ctlInfo
mcControls.Add clsEvents, CStr(ctlInfo.Tag)
End Select
End If
Next ctlInfo
'Start at the first record
'Me.TripticketData.Value = Me.TripticketData.Min
End Sub
Private Sub PopulateRecord()
Dim lRow As Long
Dim ctlInfo As Control
'Store row of current record
lRow = Me.scbTripticket.Value
With TripticketData.Range("A2")
'Loop through controls
For Each ctlInfo In Me.Controls
'If the Tag is numeric, it is a data entry
'control.
If IsNumeric(ctlInfo.Tag) Then
'Get the data from the worksheet
ctlInfo.Text = .Offset(lRow, ctlInfo.Tag).Value
End If
Next ctlInfo
End With
'Mark this record as clean
Me.IsDirty = False
End Sub
Private Sub SaveRecord(Optional ByVal lOffset As Long = 0)
Dim lRow As Long
Dim ctlInfo As Control
'Store row of current record
lRow = Me.scbTripticket.Value + lOffset
With TripticketData.Range("A1")
'Loop through controls
For Each ctlInfo In Me.Controls
'Limit to data entry controls
If IsNumeric(ctlInfo.Tag) Then
'Write the values to cells
.Offset(lRow, ctlInfo.Tag).Value = ctlInfo.Text
End If
Next ctlInfo
End With
'Re-initialize the scrollbar settings
DefineScroll
'Mark this record as clean
Me.IsDirty = False
End Sub
Private Sub DefineScroll()
Dim rBottom As Range
Dim lRecordCnt As Long
With TripticketData
'Find the last used cell in column A
Set rBottom = .Range("A" & .Rows.Count).End(xlUp)
'If the database is empty
If rBottom.Row = 1 Then
lRecordCnt = 1 'set for one record - a new one
Else
'Set for all records plus a new one
lRecordCnt = .Range("A2", rBottom).Rows.Count + 1
End If
End With
'Set the min and max
Me.scbTripticket.Min = 1: Me.scbTripticket.Max = lRecordCnt
End Sub
Property Get IsDirty() As Boolean
IsDirty = mbIsDirty
End Property
Property Let IsDirty(bDirty As Boolean)
mbIsDirty = bDirty
Me.cmdSave.Enabled = bDirty
End Property
Private Sub UserForm_Click()
End Sub