VB6: Global Variable that it's value can be changed by different events - variables

I wanted to create a shopping cart form in visual basic 6.0 and I created this form using listBox and other buttons to add or remove tools. The tools list is found in another separate form. The add and remove functions are working as intended but my main problem is that when the shopping cart form is closed and then reopened, the information isn't retained and rolls back to its empty state.
What could I possibly use or do, I am new to using visual basic 6.0 and doesn't know any of its basic syntax, I am only a little knowledgeable with python or c++.
I have tried using variables using dim temp as string and then using if statements during form_load of the shopping cart form that checks if the temp variable is not equal to "" which means is that it has something in it and then add it to the listbox of the shopping cart form.
I have checked that the if statement is working and the one that is not working is the variable itself, so I researched about it and figured that I needed the variable to be a global variable that is why instead of dim temp as string, I did public temp as string and yet it still doesn't store the text value of the listbox of the tool list form.
I also considered using an array of string but it turns out you cannot use a public dynamic array in VB6
What wanted to happen is that i'll have a temporary storing mechanism that will store all the added tools to the cart and when the shopping cart form is closed and reopened, a simple if statement in form_load or lostfocus or any event that could be used, will return the stored information.

Related

Form reference technique in Access VBA

As per a suggestion in the comments from here, I am starting a thread to learn the reference technique for passing values and variables from form to form, also discussed here and here. As a side note, to the best of my knowledge, it's not the technique referred to here and here.
Those previous questions demonstrated that the following code in the called form, sets up this technique.
Dim prevForm As Form
Private Sub Form_Load()
Set prevForm = Screen.ActiveForm
End Sub
For an Access beginner such as myself, the commenter from the first link also suggests using a dialog form for the form being called, for simplicity. Should it already be set that way in the form's Property sheet, or handled on-the-fly using the DoCmd.OpenForm's acDialog argument?
Ok, there is several issues here.
As always, one particular suggestion applies to a particular case and goal you are trying to achieve.
So, for example:
I need to get a simple yes/no from the user.
So, for a simple yes/no, or “do you want to do this” type of prompt?
Then you can use this:
Eg this:
If MsgBox("Do you want to create a invoice", vbQuestion + vbYesNo, _
"Create") = vbYes Then
'
' code here to create a invoice, or maybe launch a invoice form.
Else
' user cancled - clikced no
' code here to do what you want if user answers no,
' or maybe we do nothing
End If
And the prompt is like this:
So, for some simple types of UI interaction you don’t need a form.
Ok, the next scenario?
I want to pass some values?
Well, in some cases, if you use a sub form, then you don’t really have to pass values, since you can with ease reference both forms.
For code in the sub form?
You can go:
Me.parent!InvoiceNumber
So, in a sub form, you can “always” reference the parent form with “me.Parent”. This is built in, and VERY nice, since you NEVER thus hard code the forms name.
So, if you copy the form, rename it etc., then your code will not break. So in this example, we don’t’ have to pass values that the sub form might need, since there is already a built in means and approach to get at, and reference values/controls in the parent form.
Not necessary – only if you need to!!!
It will depend on what you are doing.
Say, I am on a customer form, and I now want to add a new invoice for that customer.
I can do this two ways.
Simply launch a new “create/edit invoice” form to a new record. However, in this case I would have to pass to the new form WHICH customer ID the new record will be attached to (we assume that the invoice form is a child table of the customer form).
So in this case:
We will launch the invoice form in “add mode”.
We will have to pass to the form the correct customer ID that this new invoice record belongs to.
In this case? VERY little need to make this a dialog form.
And very little need exists to have calling code wait.
So, in the main form, we would have a button called create new invoice.
It would/could go:
DoCmd.OpenForm "frmEditInvoice", , , , acFormAdd, , Me!ID
So, we simply launch the next form. And there is no real other code in that “main” customer form that has to run. Or any code that has to “wait” until the user is done adding/creating invoices. So, there is little or no need to use a dialog form.
But, we did need ONE very important bit of information passed to the invoice create form. That is the primary key “ID” of the customer that the invoice will be attached to (related to).
So, I used the “open Args” of the open form method. This is a nice a simple option to pass a value to a form you open. The limitation of course is that “open args” is ONLY good for passing one value. But, it often will suffice.
So, now in the invoice forms on-load event, we have to setup the related record. We could go:
Me!Customer_id = me.OpenArgs
However, there is ONE tip I would suggest:
Make the invoice form “modal”. What modal means is that the user MUST close the invoice form when done, and thus user returns to the main customer form upon closing the invoice working form?
If you don’t make the invoice form modal? Well, a user might start typing into the invoice form, decide they don’t like or want the invoice form, and will simply go back to the customer form, and edit, or even search/find a different customer. Now you have an open invoice form, one that not completed, and not saved, and if the user starts jumping around to different customers? Well, we don’t want that.
So, if you set the invoice edit form as “modal”, then the user must close that form to return to the previous form. So using modal forms (not dialog – very different), is a great way to control the user and “flow” of your application. If you don’t have “some” control, then you and your code will fast spiral out of control, since you have a customer form open, and the user might well be trying to add/edit an invoice that does not even belong to that customer. So, using modal will force the user to finish or close the form to return back to where he came from.
Also, the above is “air code”, I VERY MUCH STRONG recommend you don’t use the on-load event to set (connect) the record using the on-open, or on-load event. You actually want to use the on-insert event. But this post is too long, and I’ll need 2-3 pages of explain as to why this makes sense.
Anyway, so, the above case?
We did not need a dialog form (usually for answering yes/no kinds of questions).
We ONLY has to pass one little value – so, passing that value we used the “open args” parameter of the open form.
And, we set that form as “modal”, since we do not want the user to jump and sneak back to the customer form until such time they are “done” entering the invoice, and they MUST close that invoice form to return back to the customer form.
However, this “concept” of separate windows forms being launched and users having to “move around” re-size and worry about messing with a window? We don’t’ do that much anymore!
Due to smartphone, and tablets? And how all the web browsers work? Well, a separate window is fast becoming “old school”. And that is why Access has the new “tabbed” interface – so it works much like a browser now.
As a result of tablets and UI trends in our industry? (And what users now expect how software works?). Well, then the new “tabbed” interface option in access is a common choice and this choice will often effect how you cobble together forms flow for the UI.
Also, I don’t recommended dialog forms unless you wanting to prompt the user, and then take action as a result.
Ok, so in our above example? Let’s say when we create the invoice, we want and need a FEW more values from that customer form. Say we want the default shipping address (a single foreign key value to the shipping address table), maybe the default invoice terms (you know, net 30 days etc. – again a FK value to some table), and of course the PK customer ID value.
So, now we need to pass say 3 values.
Well, we can’t use openargs, since that is quite much only good for one value (some people do pass a delimited string, and parse out the values – so you can with some hoops pass multiple values with openargs, but it tends to be messy for anything more than one value).
So, so, now let’s pass 3 values to that invoice form we open.
Well, first, we can ask do we really need to pass values.
We could do this code:
Dim f As String
f = "frmEditInvoice"
DoCmd.OpenForm f, , , , acFormAdd
Forms(f)!customer_id = Me!ID
Forms(f)!ShippingAddress = Me!ShipID
Forms(f)!InvoiceTerms = Me!InvoiceTermsID
So, in above, the main form (calling form) launched the new form, and then simply set some values in that target (2nd) form.
So, in many cases you really don’t and did not need to pass all that stuff. Just open the form, and set the textboxes or values you want right after you launch/open that form.
However, for an example, let’s pass the values to the invoice form.
In this case, we are now going to use the “frmPrevious” trick. In effect, we don’t pass all the values, but ONLY pass the calling form, and thus the invoice form is free to reference and use “any thing” from that calling form. (Welcome to the world of using objects here! So, the old school idea of passing a whole bunch of values? You really don’t need to. Once that invoice form has a reference (a pointer) to the previous form? Well then it can grab/use/see as many values from that calling form. And this INCLUDES the user of variables you declared in your main form!
So, if you can get/grab controls, values, and even variables from the calling form? Well, then you really don’t have to pass much of anything, and there is little need.
So, to save world poverty and not have to try and pass a gazillion values? Let’s use the suggestion of screen active.
So, our code becomes this:
DoCmd.OpenForm "frmEditInvoice", , , , acFormAdd
And in the form invoice?
At the main module level, we have this:
Option Compare Database
Option Explicit
public frmPrevious as form
And the forms on-load:
we can go:
Dim frmPrevious As Form
Set frmPrevous = Screen.ActiveForm
Me!customer_id = frmPrevious!ID
Me!ShippingAddress = frmPrevious!ShipID
Me!InvoiceTerms = Me!InvoiceTermsID
So, note again, by grabbing a reference to the calling form? Well now I can grab 2 or 20 values. So, in object programming, you don’t pass a whole bunch of values, but a reference to the object in question. While Access is not a full OO language? It has good use of objects, and you can even create custom objects.
So, up to this point, we not needed, nor wanted, nor even warranted the use of a dialog form.
I tend to only suggest a dialog form WHEN you need to prompt the user and you need “several” things for the user to enter as opposed to a simple yes/no.
So, the idea here about passing values? Well, the whole idea is that you don’t need to pass values when you can get a reference to the previous “object”, and thus grab as many values as you please.
So, adopting an object approach will require some change from traditional programming where we think of everything as some subroutine we call and we pass values to that sub. In this case, we are in effect picking up the previous forms object, and once we do that, then we can quite much grab anything from that previous form – including VBA variables if you wish.
Keep in mind, that what you reference in that previous form does not necessary have to be controls. You can also reference variables. So, you could setup 3 variables, set their values, and then in the form you called/opened? It can get/grab reference those values. So, you might have a case in which one special type of form is called by MANY forms, and those forms will have all kinds of different controls and field names, but as long as ANY of the calling forms follows your new “made up” standard of having say 3 known variables declared in the calling form? Then the receiving form can reference those VBA values.
I would certainly admit that in most cases the values you are passing are going to be values in controls on the form, but you are not limited to just controls – variables declared at the module level and as public in the calling form can be used in the target form.
Eg;
frmPrevous.SomeVBAVariableNameGoesHere
Final notes and clearing up the use of screen.ActiveForm.
You do NOT want willy nilly to just use screen.ActiveForm all over the place, as it can change without you really controlling when and how.
However, VERY important:
You PICK UP, GET/GRAB the screen.ActiveForm in the on-open event of the target form. You wnat to do this first, and fast.
You can even wait and do this previous form pick up trick in the on-load event. The current form DOES NOT become active until such time both the on-open event, and the on-load event have 100% completed.
I have used this approach for about 20 years and I am not aware of ONE failure in regards to having picked up the previous calling form by grabbing the previous form reference in the on-load, or on-open event.
It is VERY reliable. Once that reference has been picked up, then screen focus changes etc. will NOT matter, since you grabbed a working reference ONE TIME and only one time in the forms on-open event, or as I noted, you can even do this as late in the on-load event.
So a HUGE difference of a suggesting here. I am not suggesting or advocating using screen.ActiveForm any old place, but I am suggesting a ONE TIME grabbing of screen.ActiveForm to get the calling form's reference - I have found this approach to be dead 100% reliable.
Try to avoid Screen.ActiveForm: it is very dangerous, because asynchronous events can generate wrong information, even errors due to the form not being open yet
Try, in the second form, to declare a public variable, or better a property declared as Form (example FrmOldForm as Access. Form)
Private m_objFrmForm As Access.Form
Public Property Get FrmForm() As Access.Form
Set FrmForm = m_objFrmForm
End Property
Public Property Set FrmForm(ByVal objNewValue As Access.Form)
Set m_objFrmFormu = objNewValue
End Property
Open the new form, and set FrmOldForm in new Form as the old Form.
set Forms("NewForm").frmForm = me.form
Now, and whenever the old form is open, you will have a "window" to it, and you can call the old form from the new one.
I use this technique with classes, but it should work directly

MS access event load form

I have about 20 MS-Access databases with about 400 Forms and wish to perform an action whenever any form is loaded.
I would need, perhaps, an event at database or application level that would trigger at any form being opened/loaded. Then I might need only to add code once to each of the 20 databases, but not to each of the 400 forms.
And: it has to be in VBA (Access >= 2010)!
My Question: Is this even possible? And if so, does someone have a hint?
Thanks,
Pete
There are multiple ways to go about this, I think.
What I'd probably do is the following:
Create a hidden form that opens whenever your database opens (using an autoexec macro) with a timer.
When loading the hidden form, initialize a collection/multi-level array of all available forms in the database, and set their status to closed (somewhat difficult, probably would go with multilevel array or a collection inside a collection to be able to store form name + boolean open or closed).
Periodically iterate the collection, check for changes, so you can see whenever a form closes and trigger your event.
You could also use VBA to iterate through all the forms, add a module to them if there is none, and then add your desired Form_Load code to that module. (Would probably be wise to simultaneously create code to undo that action, so you can actually maintain the code). While refining that, you could check if the form already has a Form_Load action, and append code to that if it exists.
Alltogether, possible, but difficult. If you want pointers on some of the steps, I can give them, but for major issues on the implementation, I'd ask a separate question.

Can't change code copied from form in different database

Building a Maintenance log book in access. Had to redo a data entry form so I started over with a new form in a new database then copied some working code from the old form to the new. In doing so I changed the names of a couple of combo boxes in the new form in order to keep things consistent. Problem is, the code I copied has the old name of some of the combo boxes and I can't change those names to the new names. The new names don't present when using autofill, just the old ones. Is there a way to correct that.(Easy, I Hope)
Example: in the old form the name of a combo box was actionCB. In the new form the box is named ActionCB, but now any code I write uses the old name.
This might be happening because you have compilation errors. Try compiling the application and fixing any errors. Or possibly this could happen if you happened to be stepping through the code at the same time (but I doubt it from your description)? Also VBA is beautiful in the fact that almost always you can make code changes while stepping through the code... but I've experienced the changes not saving from time to time.
MODIFIED FOR NEW QUESTION
As to your second question below in the comments. You can view and select a Form or Reports controls by using the Forms Property Sheet. If you look at the combobox at the very top of the property sheet it lists all Form or Report controls. You can select these objects by simply selecting them from the combobox.
Access is not case sensitive
"Example: in the old form the name of a combo box was actionCB. In the
new form the box is named ActionCB, but now any code I write uses the
old name."
If you're code is not updating to reflect the "ActionCB" name automatically, then you're probably trying to reference it where it's not available. This isn't going to generate an error if you don't have Option Explicit statement at the top of your module.
It'll just assume you know what you're doing and assign it to a Variant data type.
Put Option Explicit at the top of all your modules, and the debug | compile untill all errors are fixed.

How to pass arguments to function that will be executed later?

I have a windows form that executes a few stored procedures, loads some data and binds textboxes, comboboxes, etc THEN AFTER the form has loaded everything a button is displayed which allows you to execute a function on click.
My question is, as my form is executing and binding data can I pass certain values I need ( Id's, which are primary keys in my database ) to this function that will be called by the button press.
I have it currently functioning using hidden labels and I am going through my code now to correct this poor design.
EDIT: I have this functioning now using public properties, is doing this too loose of a design ?
Using public properties I was able to achieve my goal and remove the poorly designed hidden label quickfix.

How can a custom web part read the list item fields in a display/edit form?

I need to create a custom web part and programmatically attach to the display and edit forms of a list. The web part is meant to display some information (coming from another list) about the list item being displayed on the form.
I know how to add the web part to the list programmatically, but I'm having an issue trying to get the web part to read the list item being displayed on from. I know the SPForm object exposes its parent list but I cannot or don't know how to convert the form to an SPForm object. The parent of the web part is the web part manager and the parent of that is the HTML form object. But when I try convert that to an SPForm object it throws an error.
Also, even if I manage to get hold of the SPForm and its parent list, how can I get hold of the list item being displayed?
Please bear in mind that I need to avoid overwriting the default forms as it will significantly increase the development time.
Any help or idea is appreciated.
Regards