Do you have to show every tab before all textboxes actually populate? - vb.net

I have a vb.net form that uses multiple textboxes across several different tabs. Within one of those tabs, I have a sub set of tabs. My save functionality calls stored procs for each tab and cycles through the values on each page to either do an update or a "add new". I noticed that while testing, some of the pages do not save or update any of the values in the textboxes. After a few days of investigating, I realized that if I edit something, then physically click through the other tabs, it all saves/updates properly. If I don't click through them, they don't all save. Is there a reason for this that I am missing? When you enter a search value, I cycle through the pages and populate them all at the same time so I was assuming it wrote those values BEFORE it physically rendered...I guess I am wrong?

From the TabPage documentation Remarks section
Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown.
So the answer to your question is "Yes the tabpage must be shown".
However, the definition of "shown" is subject to interpretation. In reality, all you need to do set the TabPage.Visible property to True and not actually cycle through and display each TabPage.
A recursive scan of the form for TabPage controls will work:
Private Shared Sub TabPagesVisible(parent As Control)
For Each c As Control In parent.Controls
If TypeOf c Is TabPage Then c.Visible = True
TabPagesVisible(c)
Next
End Sub
Example usage:
Sub SaveFormTabData()
TabPagesVisible(Me) ' Me refers to the containing form
' code to save control data
End Sub

Related

Test if Controls/fields are on the visible tab ms Access

I have a MS Access Form with various controls organised into tabs.
There are some text fields among them, and I need the text field to update (that is, the vba to update them) when their tab is opened. The text fields aren't bound, the updating is a bit more customised.
I've tried the below code but it returns True for text fields that aren't on the visible tab
Private Sub TabCtl2_Change()
Dim Ctl As Control
For Each Ctl In Me.Form.Controls
If Ctl.ControlType = acTextBox Then
'Want to do stuff here
Debug.Print Ctl.Visible 'This is returning 'True' for all text fields
'not just the ones on the active tab.
Debug.Print Ctl.Name
End If
Next
End Sub
As far as I can see the Tab objects don't have a Controls collection or whatever you call it. Can someone help me how to loop through controls on a tab? I don't want to code it all in manually if I can help it.
The Page object in the Pages collection in the tabcontrol does have a Controls property.
This means, if you want to do something for all controls on the active page, you can use the following:
For Each control In Me.MyTabcontrol.Pages(Me.MyTabcontrol.Value).Controls
'Do Stuff
Next

How can I simulate a keyPress event on a combo box when I click a buton on the same form im MS Access

I'm creating a form in MS Access with 10 butons simulatin a numeric keypad (only the values 0..9).
I would like to change the value of a combobox control in a subform each time I click on one of these butons. The combo box control is named "projectID"
I tried this but it is not the same as pressing the same key on a keypad.
Private Sub Buton3_Click()
Call Me.frmServDedicacion_Subformulario.Form.projectID_KeyPress(51) 'Ansii code for 3
end sub
I put this procedure as keypress event in order to verifiy the combobox method is executed, and it does (msgbox is ok) but the combobox doesn't receive the KeyAscii value.
Public Sub projectID_KeyPress(KeyAscii As Integer)
MsgBox Chr(KeyAscii)
End Sub
This looks like a problem referring to a control on a sub form from the main form. I still have trouble getting this right so I use a cheat sheet:
http://access.mvps.org/access/forms/frm0031.htm
I created a main form called main and put 10 buttons named button0 - button9 on the form and I dragged a form called mysubform onto the main form to create a subform. mysubform has a textbox named projectID. Then just set the click event to button0 to:
Private Sub button0_Click()
Me!mysubform.Form!projectID = 0
End Sub
Don't forget similar click events for buttons 1-9
some things that may be helpful:
! is the bang operator see:
Bang Notation and Dot Notation in VBA and MS-Access
by default, when you drag a form to create a subform control on another form, access gives the subform control the same name as the dragged form. so here mysubform refers to the subform control and not the original form used to make the subform.
Then .Form gets the form wrapped by the subform control.
I hope this answers your question

How to get values from controls on tabstrip in VBA?

In different tabs of a tabstrip I have input values which are different in each tab. I need to write a code which takes all these values and do some work like sums up the values of each tab on button click.
Can anyone help me do this? In my code when I input value at a textbox of one tab it also changes the value of all other tabs and hence cannot receive different values of each tab. Any idea, please?
Multipages vs Tabstrips
A multipage is an object with "pages". Each page can hold it's own collection of controls which can then be referenced either directly or through the containing page object.
A tab strip is an object with "tabs". Unlike a "page" object, a tab does not have it's own controls. Instead, there are only the original controls, visible for all "tabs".
Programmatic differences
Since a multipage has a different set of controls for each page, there is very little housekeeping required. The selection of a page affects which controls are visible, and the controls automatically retain values assigned to them (as expected).
For a tabstrip, since there is only the initial set of controls, there is a lot of housekeeping required in the code. The selection of a tabstrip does not have any automatic effect on the values in the controls. Instead the controls act as would be expected if they were not in the tab strip at all.
Tabstrip Solution
Set up a variable (array, collection, dictionary) that can be used to hold the various values of the controls. Then, in the TabStrip_Change() event, store the previous value, and reset the control for the new tab (or fill in the value the new tab last held).
I recommend adding a userform level variable Dim old_tab as Long which you can set to the current page at the end of the TabStrip_Change() event. (This is useful for retrieving previously filled values for the correct tab).
For my sample code, I will be using an array. However, since arrays are not very flexible with changing lengths, you can also look into using either a dictionary or collection, if desired.
For the userform pictured below, the following code causes the single textbox to act as though there is a different textbox per tab. It also saves the values whenever the tabstrip changes. (Note: if you then use the saved values for the calculation, remember to update the value for the current tabstrip first.)
Option Explicit
Dim old_tab As Long
Dim textValues As Variant
Private Sub TabStrip1_Change()
textValues(old_tab) = TextBox1.Value 'Saves the old value
TextBox1.Text = textValues(TabStrip1.Value) 'Updates value to reflect tab change
old_tab = TabStrip1.Value 'updates tab # variable
End Sub
Private Sub UserForm_Initialize()
ReDim textValues(0 To TabStrip1.Tabs.Count - 1) 'tabs are zero-based, so count is always one more than the maximum tab value
old_tab = TabStrip1.Value 'Ensures that the first value will be saved to correct location at the tab change
End Sub

VB.Net Form DirectCast on Forms.Panel

I need to loop through the textboxes of my form, which are located inside a panel. I'm trying to do it like this....
For Each Cntrl As Control In DirectCast(Me.Controls(PanelName), Panel).Controls
next
The reason I need to feed the panel's name to the loop is because I have two panels that exist in 2 different TabPages. A button exists on each TabPage that performs the same function, but the panels and textboxes have different names. I have the loop inside a function that handles both TabPage button clicks, therefore I need the loop to point to the right panel.
The "PanelName" matches the target panel's name, and the panel is of System.Windows.Form.Panel.
I get the "Object reference not set to an instance of an object." message.
Is this not possible?
No need to cast:
For Each Cntrl As Control In PanelName.Controls
Next
If all you have is the name of the panel and not the control reference, then you at least need to know which TabPage has the panel:
For Each ctrl As Control In TabControl1.TabPages(1).Controls("panel1").Controls
Next
Alternatively, you can use:
Me.Controls.Find(PanelName, True)
to return the panel by name while searching child controls recursively.

Save record in subform

I have a main form with a tab control containing multiple subforms. I need to be sure that the data in a subform is saved when the user switches tabs. The problem is that DoCmd.RunCommand acCmdSaveRecord seems only applies to the current form so it doesn't save the data in the subform.
I have tried different events on the subform such as deactivate, OnLostFocus etc but they don't fire until another field somewhere else gets the focus.
The ideal solution would seem to be to put something on the OnChange event of the tab control to be sure that all the data is saved. That is my question, how to do I save the record in a subform?
In Access, the default is to save, so unless you have done something pretty complicated to prevent this, moving the focus from a subform will automatically save the record. You can test this by adding a record, moving from the subform, and then checking the table.
You don't have to do anything at all, as the subform is saved as soon as it loses focus (when the tab control changes).
But as an exercise, I've outlined the code you'd write if you needed to.
You can save any form by setting it's .Dirty property to False. For something like this that's going to run a lot, I think I'd write a sub to walk through the subforms, check if any are dirty, and save the dirty ones. Something like this:
Public Sub SaveSubFormsOnTab()
Dim pge As Control
Dim ctl As Control
For Each pge In Me!ctlTab.Pages
Debug.Print pge.Name
For Each ctl In pge.Controls
If ctl.ControlType = acSubform Then
If ctl.Form.Dirty Then
ctl.Form.Dirty = False
End If
Debug.Print ctl.Name
End If
Next ctl
Next pge
Set ctl = Nothing
Set pge = Nothing
End Sub
Now, that's actually quite inefficient in cases where you have lots of controls on your tab control that aren't subforms. If your tab has nothing but subforms, it will be fairly efficient. In either case, it's much more efficient to use a custom collection populated in the form's OnLoad event, and then you'd walk that collection that includes nothing but your tab control's subforms, and save any that are dirty.
Either of these is preferable to using the OnChange event of the tab, because each time you add a tab page with a subform or change the name of a subform control, you'd have to alter the OnChange event.
I was having a similar issue where I needed various code to run in the subform and values in the main form - that were based on values in the subform - to be recalculated. Here is what finally worked:
This is an example with a main form named 'frmCustomers' containing a subform 'sfmTransaction', which in turn has a subform called 'sfmOrderLine'. The forms have a button 'cmdSave' that is only visible when the user clicks the 'cmdEdit' button (the purpose of which is to lock all the controls until the user clicks the edit button, and then to re-lock them when he clicks save):
On the main form ('frmCustomer') go the subform's exit event, and add 'me.recalc' twice, like this:
Private Sub sfmTransaction_Exit(Cancel As Integer)
If (Not Form_sfmTransaction.NewRecord And Form_sfmTransaction.cmdSave.Visible) Or (Not Form_sfmOrderLine.NewRecord And Form_sfmOrderLine.cmdSave.Visible) Then
'To save subforms
Me.Recalc
Me.Recalc
End If
End Sub
In the subform ('sfmTransaction') go the subform's subform's exit event, and add 'me.recalc' twice, like this:
Private Sub sfmOrderLine_Exit(Cancel As Integer)
If Not Form_sfmOrderLine.NewRecord And Form_sfmOrderLine.cmdSave.Visible Then
'To save subform
Me.Recalc
Me.Recalc
End If
End Sub
Hope this helps.
Setting the dirty property to false may force Access to save the data to the database, but it bypasses the before_update event. This means that if you've used this event for validation or other purposes, you can now have bad data in your database.