moving focus from subform to main form - ms-access

i have main form MAINF and two subforms SUBONE and SUBTWO
I want to be able to move focus (cursor) between them.
From MAINF to SUBONE or SUBTWO, I can call Me![SUBONE].SetFocus or Me![SUBTWO].SetFocus. This seems to work.
BUT:
1) From SUBONE to SUBTWO, I have no idea. what is the correct way of programatically moving focus?
2) From SUBONE to one of MAINF's control say "Customer ID", how do i do it?
Edit: Here's the code that rtochip provided.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyF5
Me.Parent![Item - Order Subform].SetFocus
DoCmd.GoToControl "Supplier ID NUM"
Case vbKeyF6
Me.Parent.[Item ID].SetFocus
End Select
End Sub

If SUBTWO is a child of SUBONE, then it's the same way. However, if they are siblings thenyou have to reference it as an object on the parent first.
There are two ways to reference objects on your parent:
You can reference the parent
Me.Parent.[Customer ID].SetFocus
(btw, change than control's name to Customer_ID - it makes it easier to use, and you won't require the []'s)
You can reference it directly
Forms!MAINF.[Customer ID].SetFocus
Update: The KeyDown event is probably being caught later on the main form. You could always clear it out before you finish with moving focus.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyF5
Me.Parent![Item - Order Subform].SetFocus
DoCmd.GoToControl "Supplier ID NUM"
KeyCode = 0 'Trap F5
Case vbKeyF6
Me.Parent.[Item ID].SetFocus
keyCode = 0 'Trap F6
End Select
'keyCode = 0 'Note: you can't do it here because it will trap ALL your
'KeyCodes. Not just F5 and F6.
End Sub

Related

Why won't the ForeColor of this label visibly change when altered in a loop?

I am making a form that has groups of controls that I want to visibly enable/disable with a related toggle button (let's call them Group Toggles). Each group has a different variety of control types, so I made a common procedure to handle the toggling:
'constants for control ForeColors
Public Enum LabelForeColor
Default = 8355711
Off = 14277081
End Enum
Public Enum ListForeColor
Default = 4210752
Off = 12566463
End Enum
Public Sub EnableControl(Ctrl As Control, Enabled As Boolean)
With Ctrl
Select Case Ctrl.ControlType
Case acLabel
If Enabled Then .ForeColor = LabelForeColor.Default Else .ForeColor = LabelForeColor.Off
Debug.Print "LABEL", .ForeColor
Case acListBox
If Enabled Then .ForeColor = ListForeColor.Default Else .ForeColor = ListForeColor.Off
.Enabled = Enabled
Debug.Print "LIST", .ForeColor
Case acCommandButton
.Enabled = Enabled
Debug.Print "BUTTON", "NA"
Case acCheckBox
.Enabled = Enabled
Debug.Print "CHECK", "NA"
Case Else
Debug.Print "Control [" & .Name & "] is not of a type that EnableControl can handle."
End Select
End With
End Sub
Each group of controls is represented by a collection. When the form is loaded, every control with a particular tag property is added to the corresponding collection. The Group Toggles are not added to any collection and instead have event procedures that look like this:
Private Sub ToggleGroup1_AfterUpdate()
Dim State As Boolean
'a public function that converts the toggle button's value to a boolean
State = FormCommon.ToggleButtonState(ToggleGroup1.Value)
Dim iCtrl As Control
For Each iCtrl In Controls_ByPlant
FormCommon.EnableControl iCtrl, State
Next iCtrl
End Sub
When I click on a GroupToggle, all of the controls in the corresponding group visibly change appropriately, except for the labels. After an hour of troubleshooting, here's what I know:
The ForeColor property of the label does change, but not visibly.
When I call EnableControl on a label outside of a loop, the label visibly changes.
It doesn't matter if I pass the label object specifically to the subroutine or if I pass it from its group collection; the change is visible in both cases
If I toggle-disabled a label as part of the Group Toggle event and then call EnableControl specifically on that label to try to disable it again, there is no visible change (probably because the ForeColor property is already set to the "off" color)
Turning screen updating off with Application.Echo while the Group Toggle event runs and then turning it back on at the end of the event does not make a difference.
Making the Group Toggle event run with a For i = 1 to .Count instead of a For Each does not make a difference.
This problem also occurs when changing a different visual property instead, such as ForeTint.
(Per comments) Repaint does not make a difference
(Per comments) DoEvents does not make a difference
Why is this happening?
(First ever question, so apologies if I messed something up in the post)
This was interesting, but somewhat anticlimactic.
Your code does work for the labels, but what happens is this:
All labels are associated with input controls (as it is usual)
When you deactivate a group, you disable the input controls (.Enabled = Enabled)
This automatically sets the associated labels to a (system defined) light gray text color which cannot be changed.
This "disabled label" color is very similar to your LabelForeColor.Default color, so it is hard to see the change when toggling. But it does change.
Change your color constants to make the effect more visible:
Public Enum LabelForeColor
Default = vbRed ' 8355711
' the "Off" color is never visible, unless you add an un-associated label to a group
Off = vbBlue ' 14277081
End Enum
Edit: your test code FormCommon.EnableControl iCtrl, False works, because it only affects the label, but doesn't disable its associated list box.

Button click unclick ms-access

In access form, I'm hoping to click on a button and add information to an existing record using an update query. Ideally, when this happens, the button will change colors and appear 'activated'.
Then, if the user decides that the information that has been added needs to be removed, they can click the same button again. This removes the previously added information from the table and changes the button appearance to 'inactive'
You can't change the colour of a command button if you are using Accesss 2003 or earlier, but you can simulate a button using a label, and obviously you can change a labels caption and colours.
If you are using 2007 on-wards then substitute the Label name to your Command button name.
Using the On_current property of your form use something like
If Me.AddData = 1 Then
Me.YourLabel.Caption = "Remove Data"
Me.YourLabel.BackColor = VbRed
Else
Me.YourLabel.Caption = "Add Data"
Me.YourLabel.BackColor = VbGreen
End If
Then use a similar logic to run your update code from the On_click property of the label, based on the value of AddData.
If you're willing to have -1 and 0 in AddDate these can be easily converted to TRUE/FALSE.
This is taking a few liberties with the info you've given, but it's getting late in the day.
Your form has a command button (Command7) and a textbox (Text8).
Text8 has a control source linked to AddData (so it shows -1 or 0).
This code executes whenever you move to a different record. It checks the value in Text8 and changes the colour of the command button accordingly:
Private Sub Form_Current()
With Me
Select Case .Text8
Case -1
.Command7.BackColor = RGB(0, 255, 0)
Case Else
.Command7.BackColor = RGB(0, 0, 255)
End Select
End With
End Sub
This code on the click event of the command button will change the value in Text8 from True to False and vice versa.
It will then requery the form, forcing the Form_Current event to fire.
A requery moves the recordset back to the first record, so the bookmark moves it back to the record you were looking at.
Private Sub Command7_Click()
Dim bkmrk As String
bkmrk = Me.Bookmark
With Me
.Text8 = Not CBool(.Text8)
.Requery
Me.Bookmark = bkmrk
End With
End Sub
Edit:
Scrap that -1 and 0 malarkey....
Change the Select Case to Case 1 in the Form_Current event.
Change the .Text8 = Not CBool(.Text8) in the Command7_Click event to
.Text8 = Abs(Not CBool(.Text8 * (-1)))

Incrementing textbox value up and down in vba does not cancel evenly

I can't figure out why my code isn't maintaining significant digits when incrementing a textbox value.
I have a spin control (textbox plus two small command buttons to move textbox value up or down in value) on a form.
The textbox default value is zero.
The up arrow command button should increment the textbox value by + 0.1. Here is the code:
Private Sub cmdIndexSpinUp_Click()
If Me!txtIndexSpin >= 1.5 Then
MsgBox "The maximum Index adjustment has been reached."
Exit Sub
Else
Me!txtIndexSpin = Me!txtIndexSpin + 0.1
End If
End Sub
The down arrow command button should increment the textbox value by -0.1. Here is the code:
Private Sub cmdIndexSpinDown_Click()
If Me!txtIndexSpin <= -1.5 Then
MsgBox "The minimum Index adjustment has been reached."
Exit Sub
Else
Me!txtIndexSpin = Me!txtIndexSpin - 0.1
End If
End Sub
So I would expect that from the default value of 0, if I spin up once and down once, I should return to 0. That works fine. If I spin up twice and then down twice, my textbox value suddenly becomes 2.77555756156289E-17 instead of 0.
After more testing, it does not consistently happen based on the number of clicks, but it may be related to the time between clicks. The more rapid, the more prone to this error it seems.
How could this be happening?
I am going to code around it, since I see nothing wrong, but am curious what I am missing.
As #HansUp says floating points aren't precise. Source. An easy solution that you could use is to round the number before putting it in the text box.
Example :
Private Sub cmdIndexSpinDown_Click()
Dim value As Double
value = Me!txtIndexSpin
If value <= -1.5 Then
MsgBox "The minimum Index adjustment has been reached."
Exit Sub
Else
value = Round(value - 0.1, 1)
Me!txtIndexSpin = value
End If
End Sub
Private Sub cmdIndexSpinUp_Click()
Dim value As Double
value = Me!txtIndexSpin
If value >= 1.5 Then
MsgBox "The maximum Index adjustment has been reached."
Exit Sub
Else
value = Round(value + 0.1,1)
Me!txtIndexSpin = value
End If
End Sub
Round() is a reasonable solution for your floating point precision problem. And it may well be exactly what you want. However, be aware that you will be using "banker's rounding", sometimes called "round to even". So you might not get what you expect from rounding to 1 decimal place when the second decimal place is 5:
? Round(1.15, 1)
1.2
? Round(1.25, 1)
1.2
If that is not what you want, you could use a different rounding strategy. Or you could switch to integer math instead of floating point math ... and then the floating point precision challenge goes away. That might sound challenging, but it's actually simple to implement. Add a hidden text box to your form and use it like this ...
Private Sub cmdIndexSpinDown_Click()
With Me!txtHidden
If .Value <= -15 Then
MsgBox "The minimum Index adjustment has been reached."
Else
.Value = .Value - 1
Me!txtIndexSpin.Value = .Value / 10
End If
End With
End Sub
If txtIndexSpin is bound to a field in the form's Record Source, you can load txtHidden from the form's Current event:
Me!txtHidden.Value = Me!txtIndexSpin.Value * 10
And if you also allow the users to edit txtIndexSpin directly (not just via those command buttons), do that again from its After Update event.

VBA Add combo box values together

I have 3 combo boxes that contain numbers:
Me.Authorized
Me.Officer
Me.Enlisted
What I'm trying to do is add the values of Me.Officer and Me.Enlisted and make sure it equals Me.Authorized. I have all the other statements/commands figured out, but I can't wrap my head around this one.
The combo box selected value is a string, even when that string contains only digits. You can use Val() to convert that string to a number.
So your required condition can be expressed as ...
Val(Me.Officer) + Val(Me.Enlisted) = Val(Me.Authorized)
You can enforce that requirement in the form's Before Update event ...
Private Sub Form_BeforeUpdate(Cancel As Integer)
If (Val(Me.Officer) + Val(Me.Enlisted) <> Val(Me.Authorized)) Then
MsgBox "Officer plus Enlisted must be equal to Authorized."
Cancel = True
End If
End Sub
That event procedure will abort a record save when your requirement is not satisfied.

Assiging a 'User Stamp' when saving a record

So the first thing is that every table has 4 fields. Created_By, Created_Date, Last_Updated_By and Last_Updated_Date. This will allow me to keep a track of who saved what records and when. I want a public sub (or function?) to update these fields when any record is saved.
Ideally I don't want to have to copy/call my code from every form, but I will if that's what is necessary. I imagine there is a way of doing this globally.
I currently have the following:
Public gLoggedIn As Integer
Public Function get_global(Global_name As String) As Variant
Select Case Global_name
Case "Employee_ID"
get_global = gLoggedIn
End Select
End Function
My next thing is to add vba code to fill the 2 of the 4 fields. The following is something I have in one of my forms, but like I say, I want to able to achieve this globally on every save.
Private Sub Form_BeforeUpdate(Cancel As Integer)
If Me.NewRecord = True Then
Me.[Created_By] = gLoggedIn
Me.[Created_Date] = Now()
Else
Me.[Last_Updated_By] = gLoggedIn
Me.[Last_Updated_Date] = Now()
End If
End Sub
How can I do this globally?
I'm not sure if 'me' is the correct reference to us if this is global?
Exactly for your demand, you could use a VBA modul with following sub
Public Sub setTimestamp(objForm As Form)
If objForm.NewRecord = True Then
objForm![Created_By] = gLoggedIn
objForm![Created_Date] = DateTime.Now
Else
objForm![Last_Updated_By] = gLoggedIn
objForm![Last_Updated_Date] = DateTime.Now
End If
End sub
You need to call this method in every form when the Timestamp Update should be fired (BTW: I would recommend an event when a save button is clicked or field update, because an edit in the dataset of the form at form update event will probably cause another form update event and could lead to an endless loop).
call setTimestamp(Me)
But you should alternatively consider using a macro like #E Mett said in comment.
Another hint: If you set the variable gLoggedIn to public, you can refer directly without using a function. (You did this even in your example) A function makes sense when you want to hide the variable inside a modul as private. This is called encapsulation, if you want to learn more about it.
EDIT
Changed code by andre451 ' s suggestion.