.SetFocus from module VBA - vba

I'm trying to set the focus in a form after update. When I do this within the forms class module I have no problem. However, I need to do this in a few forms so I'm trying to write it in the module. My problem is I can't get the .SetFocus to work unless I hardcode the form name within the class module. WHno is the name of the control I'm trying to set focus.
I have attempted a number of options and none seem to work.
Here is the sub. Everything works wonderfully except the .SetFocus procedure.
Sub ValidateWHNO()
Dim EnteredWHNO As Integer
Dim actForm As String
Dim deWHNO As Variant
msg As Integer
Dim ctrlWHNO As Control
EnteredWHNO = Screen.ActiveControl.Value
actForm = Screen.ActiveForm.Name
Set ctrlWHNO = [Forms]![frmEnterBookData]![WHno]
deWHNO = DLookup("[WHno]", "tblDataEntry", "[WHno] = " & EnteredWHNO)
If EnteredWHNO = deWHNO Then
msg = MsgBox("You have already entered " & EnteredWHNO & " as a WHNO. The next number is " & DMax("[WHno]", "tblDataEntry") + 1 & ", use this?", 4 + 64, "Already Used WHno!")
If msg = 6 Then
Screen.ActiveControl.Value = DMax("[WHno]", "tblDataEntry") + 1
Else
Screen.ActiveControl.Value = Null
ctrlWHNO.SetFocus 'CODE THAT WONT RUN
End If
End If
End Sub
I've tried a number of other methods to set focus, such as:
Forms(actForm).WHno.SetFocus,
Forms(actForm).Controls(WHno).SetFocus, Screen.ActiveControl.SetFocus
The current result is that if No is selected in the MsgBox, the value is cleared, but the focus moves to the next control.
Thanks in advanced for any help that may be offered.

Does the following make a difference?
Sub ValidateWHNO(frm as Access.Form)
Dim EnteredWHNO As Integer
Dim actForm As String
Dim deWHNO As Variant
msg As Integer
EnteredWHNO = frm.ActiveControl.Value
actForm = frm.Name
deWHNO = DLookup("[WHno]", "tblDataEntry", "[WHno] = " & EnteredWHNO)
If EnteredWHNO = deWHNO Then
msg = MsgBox("You have already entered " & EnteredWHNO & " as a WHNO. The next number is " & DMax("[WHno]", "tblDataEntry") + 1 & ", use this?", 4 + 64, "Already Used WHno!")
If msg = 6 Then
frm.ActiveControl.Value = DMax("[WHno]", "tblDataEntry") + 1
Else
frm.ActiveControl.Value = Null
frm![WHno].SetFocus
End If
End If
End Sub
And your call from each form would be:
VaidateWHNO Me
Instead of using a relative reference to the form (Screen.ActiveForm), the code passes the form reference through directly and uses that reference as the parent of the .setFocus method.

Related

Can I use the same text file in two separate forms within the same project?

I'm working on a computer science project and I'm in the prototype stage right now. I will eventually switch this entire thing to a database once everything for it is sorted on the campus computers but for now I'm using text files. It's written on visual studio using VB (Required by my college). I'm trying to use the same text file in two different forms at different times but I keep getting the same error when I run it, saying that the file is open elsewhere. I assume this is because it reads from that file in the next form but I have made it so that the file isn't even opened until a specific button click on that other window. It reaches this error when I attempt to write data to the file from the first window, I never get to the stage where I read from the file in the next form.
Context:
It's a booking system for a youth hostel, the booking text file is written to, from the booking page and saved. This opens up a booking confirmation page where data is read from the last line of the file and necessary information is taken out (Name, checkin/checkout dates, booking reference etc.) to be inserted into the booking confirmation message so it's sort of tailored to each booking but keeps the same structure. I have a button to "save booking" which would write the data to the file but this is where the error occurs. The other form isn't open, the text file isnt open anywhere else and after writing to the file, it's closed.
Is there a workaround? should I just try using variables to hold the input data to use it in the next form? I'm not too worried about it being perfect because this is just a prototype until my college sorts out its issue with databases but I'd still like it to function the way I want it to.
Thank you :)
From booking page:
Private Sub btnBook_Click(sender As Object, e As EventArgs) Handles btnBook.Click
Dim NewBooking As Bookings
Dim errorFlag As Boolean
Dim bookingID As Integer
errorFlag = False
If txtID.Text = "" Or drpNumGuests.Text = "Please Select" Or txtCheckin.Text = "" Or txtCheckout.Text = "" Or txtRoomNo.Text = "" Then
errorFlag = True
End If
If errorFlag = True Then
MsgBox("Error with data entered, please try again.")
Else
Dim sw As New System.IO.StreamWriter("Bookings.txt", True) 'fix
Dim Booking() As String = File.ReadAllLines("bookings.txt")
If UBound(Booking) < 0 Then
bookingID = 0
Else
bookingID = Str(Int(Booking(UBound(Booking)).Substring(0, 8)) + 1) 'Automatic BookingID assignment
End If
NewBooking.GuestID = LSet(txtID.Text, 9)
NewBooking.BookingID = LSet(bookingID, 9)
NewBooking.Name = LSet(txtName.Text, 25)
NewBooking.GuestNum = LSet(drpNumGuests.Text, 2)
NewBooking.CheckIn = LSet(txtCheckin.Text, 11)
NewBooking.CheckOut = LSet(txtCheckout.Text, 11)
NewBooking.RoomNum = LSet(txtRoomNo.Text, 3)
NewBooking.Requests = txtRequests.Text
sw.WriteLine(NewBooking.GuestID & NewBooking.BookingID & NewBooking.Name & NewBooking.GuestNum & NewBooking.CheckIn & NewBooking.CheckOut & NewBooking.RoomNum & NewBooking.Requests)
FileClose()
MsgBox("Booking complete")
End If
End Sub
Private Sub btnConfirm_Click(sender As Object, e As EventArgs) Handles btnConfirm.Click
Booking_Confirmation.Show()
Me.Hide()
From booking confirmation page:
Private Sub btnGen_Click(sender As Object, e As EventArgs) Handles btnGen.Click
Dim Bookconfirm() As String = File.ReadAllLines("bookings.txt")
Dim Name As String
Dim CheckIn As String
Dim CheckOut As String
Dim NumGuests As String
Dim RoomNum As String
Dim ID As String
Dim BookingID As String
Dim Count As Integer = UBound(Bookconfirm)
ID = Mid(Bookconfirm(Count), 1, 8)
BookingID = Mid(Bookconfirm(Count), 10, 8)
Name = Mid(Bookconfirm(Count), 19, 25)
CheckIn = Mid(Bookconfirm(Count), 46, 10)
CheckOut = Mid(Bookconfirm(Count), 57, 10)
NumGuests = Mid(Bookconfirm(Count), 44, 2)
RoomNum = Mid(Bookconfirm(Count), 68, 2)
txtConfirmation.Text = ID & " Thank you " & Name & " for booking your stay with us. Your booking of " & NumGuests & " has been made for " & CheckIn & "to " & CheckOut & "
We look forward to your arrival. Your booking ID is:" & BookingID
End Sub
Here's an alternate version you can try. As suggested by Hursey in the comments, a Using statement will make sure the file is completely closed and released.
bookingID = 0
Dim lastLine As String = File.ReadAllLines("bookings.txt").LastOrDefault()
If Not IsNothing(lastLine) Then
bookingID = Str(Int(lastLine.Substring(0, 8)) + 1) 'Automatic BookingID assignment
End If
NewBooking.GuestID = LSet(txtID.Text, 9)
NewBooking.BookingID = LSet(bookingID, 9)
NewBooking.Name = LSet(txtName.Text, 25)
NewBooking.GuestNum = LSet(drpNumGuests.Text, 2)
NewBooking.CheckIn = LSet(txtCheckin.Text, 11)
NewBooking.CheckOut = LSet(txtCheckout.Text, 11)
NewBooking.RoomNum = LSet(txtRoomNo.Text, 3)
NewBooking.Requests = txtRequests.Text
Using sw As New System.IO.StreamWriter("Bookings.txt", True)
sw.WriteLine(NewBooking.GuestID & NewBooking.BookingID & NewBooking.Name & NewBooking.GuestNum & NewBooking.CheckIn & NewBooking.CheckOut & NewBooking.RoomNum & NewBooking.Requests)
End Using
MsgBox("Booking complete")
If all of the information is already in NewBooking then either PASS that to the secondary form, or simply move it's declaration out to FORM level so that it's accessible. Then you won't need to read the file again to retrieve the information you literally just had?...

Get focus on unbound textbox when form returns no records

I'm a little stumped.
I've got an MS Access front end application for an SQL Server back end. I have an orders form with a list box that, when selected and a "Notes" button is clicked will open another form of notes. This is a continuous form and has a data source (linked table - a view) from the back end database.
When the notes button is clicked in the main orders form, it passes a filter and an OpenArgs string to the Notes form in this code:
Private Sub cmdItemNotes_Click()
Dim i As Integer
Dim ordLine As Boolean
Dim line As Integer
Dim args As String
If Me.lstOrders.ItemsSelected.count = 1 Then
ordLine = False
With Me.lstOrders
For i = 0 To .ListCount - 1
If .selected(i) Then
If .Column(16, i) = "Orders" Then
ordLine = True
line = .Column(0, i)
End If
End If
Next i
End With
If ordLine Then
args = "txtLineID|" & line & "|txtCurrentUser|" & DLookup("[User]", "tblUsers", "[Current] = -1") & "|txtSortNum|" & _
Nz(DMax("[SortNum]", "dbo_vwInvoiceItemNotesAll", "[LineID] = " & line), 0) + 1 & "|"
DoCmd.OpenForm "frmInvoiceItemNotes", , , "LineID = " & line, , , args
Else
'Potting order notes
End If
Else: MsgBox "Please select one item for notes."
End If
Here is my On Load code for the Notes form:
Private Sub Form_Load()
Dim numPipes As Integer
Dim ArgStr As String
Dim ctl As control
Dim ctlNam As String
Dim val As String
Dim i As Integer
ArgStr = Me.OpenArgs
numPipes = Len(ArgStr) - Len(Replace(ArgStr, "|", ""))
For i = 1 To (numPipes / 2)
ctlNam = Left(ArgStr, InStr(ArgStr, "|") - 1)
Set ctl = Me.Controls(ctlNam)
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(ctlNam) + 1))
val = Left(ArgStr, InStr(ArgStr, "|") - 1)
ctl.Value = val
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(val) + 1))
Next i
End Sub
This code executes fine. The form gets filtered to only see the records (notes) for the line selected back in the orders form.
Because this is editing a table in the back end, I use stored procedures in a pass through query to update the table, not bound controls. The bound controls in the continuous form are for displaying current records only. So... I have an unbound textbox (txtNewNote) in the footer of the form to type a new note, edit an existing note, or post a reply to an existing note.
As stated above, the form filters on load. Everything works great when records show. But when it filters to no records, the txtNewNote textbox behaves quite differently. For instance, I have a combo box to mention other users. Here is the code after update for the combo box:
Private Sub cmbMention_AfterUpdate()
Dim ment As String
If Me.txtNewNote = Mid(Me.txtNewNote.DefaultValue, 2, Len(Me.txtNewNote.DefaultValue) - 2) Then
Me.txtNewNote.Value = ""
End If
If Not IsNull(Me.cmbMention) Then
ment = " #" & Me.cmbMention & " "
If Not InStr(Me.txtNewNote, ment) > 0 Then
Me.txtNewNote = Me.txtNewNote & ment
End If
End If
With Me.txtNewNote
.SetFocus
.SelStart = Len(Nz(Me.txtNewNote, ""))
End With
End Sub
The problem occurs with the line
.SelStart = Len(Nz(Me.txtNewNote, ""))
When there are records to display, it works. When there are no records to display, it throws the Run-time error 2185 "You can't reference a property or method for a control unless the control has the focus."
Ironically, if I omit this line and make the .SetFocus the last line of code in the sub, the control is in focus with the entire text highlighted.
Why would an unbound textbox behave this way just because the filter does not show records?
Thanks!

Include loop counter in object name [VBA]

Basically I wrote a code, which is to be used in userform. The thing is that userform is created by other macro (amount of checkboxes differs, depends how many words string strNamn contains, that is why userform must be created by macro).
I would like to, somehow, include loop counter in the line:
If UserForm1.CheckBox0.Value = True Then
to make it like this:
If UserForm1.CheckBox(i).Value = True Then
But it obviously doesn't work like this :(
Any suggestion how to declare checkbox to include the counter in the line?
Code in UserForm1 to execute macro looks like:
Private Sub cmd_1_Click()
Call clicker
End Sub
Macro code:
Sub clicker()
Dim strNamnOK As String
Dim strNamn As String
Dim strNamnA() As String
strNamn = "one, two, three, four"
strNamnA = Split(strNamn, ", ")
Dim intAmount As Integer
intAmount = UBound(strNamnA)
strNamnOK = ""
For i = 0 To intAmount
If UserForm1.CheckBox0.Value = True Then
strNamnOK = strNamnOK & " " & strNamnA(i)
End If
Next
strNamnOK = Left(strNamnOK, 12)
MsgBox strNamnOK
End Sub

passing variables from one sub-routine to another

I am making for fun, a small program in VB.net Console, that allows users to enter films, the director and the genre, and then can sort/order them however the user wants. Essentially the problem I am hitting is that I can't pass a variable between sub routines. I thought this was achieved by using aa an example: (ByRef CurrentIndex As Integer), which would be referencing the variable that was declared in another sub-routine. This isn't working though, and I think I am not understanding some of the theory behind this.
This is the block where the variable is declared:
Sub AddDvdToDatabase()
Dim choice As String = ""
Dim i As Integer
Dim CurrentIndex As Integer = 1
i = CurrentIndex
Do Until choice = "exit"
choice = ""
Console.WriteLine("Add the title of the film")
dvd(i).Title = Console.ReadLine()
Console.WriteLine("Add the director of the film")
dvd(i).Director = Console.ReadLine()
Console.WriteLine("Add the genre of the film")
dvd(i).Genre = Console.ReadLine()
Do Until choice = "yes" Or choice = "exit"
Console.WriteLine("Would you like to add another fim to the database? <yes/exit>")
choice = Console.ReadLine()
Loop
i += 1 'Save the value of i to a separate text file to read from when opening again. This stops previuos elements from the array being overwritten.
Loop
CurrentIndex = i
Menu(CurrentIndex)
End Sub
And then here is where I am trying to pass the value of CurrentIndex to:
Sub ViewDatabase(ByRef CurrentIndex As Integer)
Console.Clear()
Dim i As Integer
For i = 1 To CurrentIndex
Console.WriteLine(i & ". " & dvd(i).Title & " " & dvd(i).Director & " " & dvd(i).Genre)
Next
Console.ReadLine()
End Sub
edit
I am calling it in the Sub Menu(), when the user selects to view the database after entering films.
I am getting the error "Argument not specified for parameter 'CurrentIndex' of 'Public Sub ViewDatabase(ByRef CurrentIndex As Integer)'"
Sorry, I am very new to Stack Overflow and I'm still learning the conventions etc.
I see that this post is old however it appears that you are tying to get the value of CurrentIndex at the end of your sub routine AddDvdToDatabase and use the output in ViewDatabase. To do this you could simply make the variable global outside of both sub routines.
Dim CurrentIndex as Integer
Your code would look like this:
Dim CurrentIndex As Integer
Sub AddDvdToDatabase()
Dim choice As String = ""
Dim i As Integer
CurrentIndex = 1
i = CurrentIndex
Do Until choice = "exit"
choice = ""
Console.WriteLine("Add the title of the film")
dvd(i).Title = Console.ReadLine()
Console.WriteLine("Add the director of the film")
dvd(i).Director = Console.ReadLine()
Console.WriteLine("Add the genre of the film")
dvd(i).Genre = Console.ReadLine()
Do Until choice = "yes" Or choice = "exit"
Console.WriteLine("Would you like to add another fim to the database? <yes/exit>")
choice = Console.ReadLine()
Loop
i += 1 'Save the value of i to a separate text file to read from when opening again. This stops previuos elements from the array being overwritten.
Loop
CurrentIndex = i
Menu(CurrentIndex)
End Sub
Sub ViewDatabase()
Console.Clear()
Dim i As Integer
For i = 1 To CurrentIndex
Console.WriteLine(i & ". " & dvd(i).Title & " " & dvd(i).Director & " " & dvd(i).Genre)
Next
Console.ReadLine()
End Sub

Why does Microsoft Visual Basic skip over part of this code

I'm trying to make some labels on my Form to be visible, but i don't want to use a lot of if statements, but for some reason whenever i put Me.Controls(lbl).Visbel = True in a for or do loop it skips the whole loop. The code worked perfectly the way I wanted it until i got an error for calling Dim lbl = Controls("Label" & counter_3) for the whole class instead of in the From_load private sub. Sometimes i can get it to work, but only one label is visible
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl = "Label" & counter_3
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
Me.Controls(lbl).Visible = True
MsgBox(item & " " & counter_3)
End If
Next
I've also tried. In both the loops are completely skipped over. I know this because the MsgBox's don't appear.
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl = Controls("Label" & counter_3)
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
lbl.Visble = True
MsgBox(item & " " & counter_3)
End If
Next
The thing that I am noticing is that you are creating a Char array based on a random word returned from your word_list, you then iterate through the Char array using the count of the character in the array as an index into your word_list, if the amount of characters in your word exceeds the amount of words in your list you will get an error and since this error is in the Forms Load event it will be swallowed and all the code after it will be aborted. There are also other issues that I would change like making sure all declarations have a type and I would probably use the Controls.Find Method instead and check that it has an actual object. But what I would probably do first is move your code to a separate Subroutine and call it after your IntializeComponent call in the Forms Constructor(New) Method.
Something like this.
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
YourMethod
End Sub
Public Sub YourMethod()
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl As Control() = Controls.Find("Label" & counter_3, True)
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
If lbl.Length > 0 Then
lbl(0).Visible = True
Else
MsgBox("Control not Found")
End If
MsgBox(item & " " & counter_3)
End If
Next
End Sub