VB6 how to get the selected/checked control in a control array - vb6

I have to modify a VB6 app and am repeatedly beating my head against a wall over control arrays.
I know that the event handler for the array includes its index value and I can set some variable there, but i should be able to directly access the selected radio button in an array of OptionButton. Currently I'm doing this
For i = 0 To optView.Count - 1
If optView.Item(i).value = True Then
currIndex = i
Exit For
End If
Next
Is this really my only option?

Yes, this is our only option. The control array object does not contain any selecting logic (which makes sense, as "selected" might mean different things for different controls). The only change I'd make is replacing the For with For Each.

Another way to do this that I have used. Write a function, and then call the function, passing in the control name, to return the index number. Then you can reuse this in the future especially, if you add it to a module (.bas).
Function f_GetOptionFromControlArray(opts As Object) As Integer
' From http://support.microsoft.com/KB/147673
' This function can be called like this:
' myVariable = f_GetOptionFromControlArray(optMyButtons) 'Control syntax OK
' myVariable = f_GetOptionFromControlArray(optMyButtons()) 'Array syntax OK
On Error GoTo GetOptionFail
Dim opt As OptionButton
For Each opt In opts
If opt.Value Then
f_GetOptionFromControlArray = opt.Index
Exit Function
End If
Next
GetOptionFail:
f_GetOptionFromControlArray = -1
End Function

Related

Expected Function or variable vb6.0

Hello i am coding in Visual Basic 6.0 and i have this error, i am trying to make a button inserting data in database.
Expected Function or variable
This is my code
Private Sub Command1_Click()
With ConString.Recordset.AddNew
!ID = txtID
!Emri = txtEmri
!Mbiemri = txtMbiemri
!Datelindja = txtData
!Telefon = !txtTelefon
!Gjinia = gender
!Punesuar = job
!Martese = cmbMartese
!Vendlindja = txtVendlindje
End With
End Sub
txtID is textbox
txtEmri is textbox
txtMbiemri is textbox
txtData is date picker
txtTelefon is textbox
gender is a string that takes value if a radio button is clicked
job is an integer if a checkbox is clicked
cmbMartese is combo box
txtVendlindje is textbox
Thank you in advance.
#JohnEason gave you the right answer. But there's another error in the line !Telefon = !txtTelefon, if I'm not mistaken. It should read !Telefon = txtTelefon, i.e. without the exclamation mark in front of txtTelefon.
I'm also not a big fan of that coding style. I prefer to not rely on default properties, but instead "spell out" the whole term, e.g.
With ConString.Recordset
.AddNew
!ID = txtID.Text
!Emri = txtEmri.Text
!Mbiemri = txtMbiemri.Text
!Datelindja = txtData.Text
!Telefon = txtTelefon.Text
' Since VB6 doesn't provide Intellisense for variables, I prefer to use some
' kind of hungarian notation for variable names, in this case sGender or strGender for a string variable
!Gjinia = gender
' Same here: iJob or intJob for an integer variable
!Punesuar = job
' You need to specify that you want the selected item of a combobox
!Martese = cmbMartese.List(cmbMartese.ListIndex)
!Vendlindja = txtVendlindje.Text
' If you're done setting column values and don't do so somewhere else,
' you also need to add a .Update statement here in order to finish the
' .AddNew action and actually persist the data to the database.
' .Update
End With
End Sub
As pointed out below by #JohnEason, there's also potentially an .Update statement missing within the With/End With block

How to define button value in popup?

I've create a VBScript popup with OK and Cancel buttons. How do I value the OK button?
Basically I want :
If OK button = True Then
' statement
Else
End If
I've tried to declare intbutton= 1, then
intbutton = objshell.popup"..."
but I get a syntax error.
You're getting a syntax error because in VBScript (and VBA and VB6) all Function calls must use parenthesis when capturing return values (unlike Sub calls which must not use parenthesis unless you're using Call syntax - yes, I think VBScript's syntax is dumb).
You're also missing the other parameters to the function: nSecondsToWait, strTitle, and nType. Note that those additional parameters are optional, so leave nothing in the nSecondsToWait parameter space.
VBScript has built-in constants for the arguments you're wanting, they are:
vbOK
vbOKCancel
vbAbortRetryIgnore
vbYesNoCancel
vbYesNo
vbRetryCancel
You can use them like so:
Dim result
result = shell.Popup( "Mesage text goes here", , "Window title", vbOKCancel )
If result = vbOK Then
' something here
End If

Create new method

I'm working with HP UFT. This tool use VBScript to automatics test.
Is it possible to create new method in function library.
I mean something like this.
Browser("Browser").Page("Page").WebEdit("login").MyMethod()
How can I do that?
Go to the Design menu, and select Function Definition Generator...
You'll get the following UI
Basically this is a helper for RegisterUserFunc.
By selecting the Register to a test object check-box (and which type of test object) the function you add will be available as a regular test object function.
Note that you can override an existing function, for example you can change Set to see if the value is "~today~" add today's date.
Adding User Defined Functions is actually pretty easy: define the function, decide on the object type you wish to add the function to, and use RegisterUserFunc to put it all together:
Function BrokenImage(ImageObject)
BrokenImage = True
ImageObject.WaitProperty "visible", True, 1000
ImageLoaded = False
StartTime = Now()
Do
If ImageObject.Object.naturalHeight > 0 And ImageObject.Object.naturalHeight > 0 Then
BrokenImage = False
Exit Do
End If
If DateDiff("s", StartTime, Now()) > 10 Then Exit Do
Loop While ImageLoaded = False
End Function
RegisterUserFunc "Image", "BrokenImage", "BrokenImage"
Once you've got the function defined, you call it as you would any other object method:
If Browser().Page().Image().ImageBroken() = true Then Print "Broken Image"
You can also add additional parameters which will then be passed to the new method.

Passing array of classes to parameter of action in QTP/VBScript

My question involves the use of QTP / VBScript.
Goal: From the qtp main starting file, initialize an array of classes, and pass that array as a parameter to a re-usable action via a parameter.
Problem: I am not able to pass an array of classes to my re-usable action.
Details:
I have two files: “application_main” and “personal_action”.
application_main is the entry point into qtp/vbscript.
personal_action is a re-usable action
Inside application_main, we have a call to InvokeApplication, proceeded by a few other declarations.
I am able to initialize an array and proceed to pass it as a parameter from my application_main to my personal_action:
From application_main:
Dim myArray
myArray = new Array(object1, object2, object3)
RunAction “personal_action”, oneIteration, myInteger, myBoolean, myArray
On the personal_action page, I edit the parameter properties via:
Edit->Action->ActionProperties. I select the Parameters tab.
In it, I have the option to define the amount of incoming parameters and each individual type. These available types seem to be restricted to:
String, Boolean, Date, Number, Password, Any
I set my 1st parameter as: Number
I set my 2nd parameter as: Boolean
I set my 3rd parameter as: Any
Upon running, I am prompted with this:
The type you specified for the ‘myArray’ parameter in your RunAction
statement does not match the type defined in the action.
Question: I am able to pass the Number and Boolean fine, but when an array is involved, qtp/vbscript doesn't seem to handle it well. Why am I not able to pass an array to an action via parameters from the main startup file? This seems like a common and simple task. Could I be so wrong?
Any help is appreciated. Thank you.
As per my knowledge, QTP will NOT allow this. There is no parameter type that can be used to represent an Array. This might be a limitation of QuickTest Professional.
Rather than passing array you can pass the Array elements as a string separated with delimiters.
Example:
"Item1^Item2^............" where "^" is the delimiter
then you can use split function of vb script, to get your array back.
Again doing the same thing with object,we have to give try for this
use lib file in your action ...
Create array public in lib
but in end for any case test or interation vararray=null
rodrigonw.
Sugestion... use function for include your lib in your actions (lib path)
Lib soluction
''######################################LIB"
'lib Passsagem de valores entre array
Dim arrayyy()
Sub setArrayyy(strvalores,redimencionaArray)
On error resume next
tamanho=UBound(arrayyy,1)
If Err.Number=9 then
ReDim arrayyy(0)
redimencionaArray=false
end if
err.Clear
On error goto 0
If redimencionaArray Then
tamanho=tamanho+1
ReDim preserve arrayyy(tamanho)
end if
arrayyy(tamanho)=strvalores
'arrayyy=arrayyy
End Sub
function getArrayyy() getArrayyy=arrayyy End function
''######################################"'Action X
call setArrayyy("X",false)
call setArrayyy("A",true)
call setArrayyy("D",true)
call setArrayyy("B",true)
''######################################'Action y
x=getArrayyy()
for countx=0 to ubound(x)
msgbox x(countx)
next

How to gracefully exit from the middle of a nested subroutine when user cancels?

(I'm using VB6 but I imagine this comes up in most other languages.)
I've got a GUI button that calls a routine that takes a minute or two to complete. I want impatient users to be able to click on the button a second time to have it gracefully exit out of the routine at any point.
I used a static variable to make this work pretty well (see code below), but I'm cleaning up the project and I want to put the For/Next loop into its own function, since it's required in several different places in the project.
But doing that would break my static flag embedded in the for/next, so I need to make some changes. Before I do something hare-brained with public (global) variables, I thought I'd ask what other (smarter, perhaps actually CS educated) people have done when faced with this problem.
So basically my question is how do I replicate this:
Private Sub DoSomething_Click()
Static ExitThisSub As Boolean ' Needed for graceful exit
If DoSomething.Caption = "Click To Stop Doing Something" Then
ExitThisSub = False ' this is the first time we've entered this sub
Else ' We've re-entered this routine (user clicked on button to stop it)
ExitThisSub = True ' Set this so we'll see it when we exit this re-entry
Exit Sub '
End If
DoSomething.Caption = "Click To Stop Doing Something"
For i = 0 To ReallyBigNumber
Call DoingSomethingSomewhatTimeConsuming
If ExitThisSub = True Then GoTo ExitThisSubNow
DoEvents
Next
' The next line was missing from my original example,
' prompting appropriate comments
DoSomething.Caption = "Click To Do Something"
Exit Sub
ExitThisSubNow:
ExitThisSub = False ' clear this so we can reenter later
DoSomething.Caption = "Click To Do Something"
End Sub
When I move the for/next loop to its own function?
I'm thinking I'll change ExitThisSub to a public variable QuitDoingSoManyLongCalculations that will exit the new for/next sub and then the DoSomething_Click in the same way.
But I always feel like an amateur (which I am) when I use global variables - is there a more elegant solution?
Well you could declare the variable at module level in the forms as private.
That is not a global but a module level variable.
Then you could pass it to the function you create and check it in the function.
But be careful with the DoEvents. It basically means allow the windows message loop to process messages. This means that not only can the user click your button again, they can close the form and do other things. So when you are in this loop you'll need to set a module level variable anyway as you'll need to check for it in a QueryUnload of the form and in any event handlers.
You can also use the Tag property of the control itself to store a flag of sorts. But I don't consider that more elegant.
I also prefer to use two different buttons. Just hide one and show the other. That way your cancel code and run code are separated in different event handlers.
To expand on my answer here is some sample code that handles the unloading aspect.
Here if you stop via the x it prompts you. If you kill via task manager, it dies gracefully.
Option Explicit
Private Enum StopFlag
NotSet = 0
StopNow = 1
StopExit = 2
End Enum
Private m_lngStopFlag As StopFlag
Private m_blnProcessing As Boolean
Private Sub cmdGo_Click()
Dim lngIndex As Long
Dim strTemp As String
m_lngStopFlag = StopFlag.NotSet
m_blnProcessing = True
cmdStop.Visible = True
cmdGo.Visible = False
For lngIndex = 1 To 99999999
' check stop flag
Select Case m_lngStopFlag
Case StopFlag.StopNow
MsgBox "Stopping - Last Number Was " & strTemp
Exit For
Case StopFlag.StopExit
m_blnProcessing = False
End
End Select
' do your processing
strTemp = CStr(lngIndex)
' let message loop process messages
DoEvents
Next lngIndex
m_lngStopFlag = StopFlag.NotSet
m_blnProcessing = False
cmdGo.Visible = True
cmdStop.Visible = False
End Sub
Private Sub cmdStop_Click()
m_lngStopFlag = StopFlag.StopNow
End Sub
Private Sub Form_Load()
m_blnProcessing = False
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Select Case UnloadMode
Case vbFormControlMenu, vbFormCode
If m_blnProcessing Then
Cancel = True
If MsgBox("Unload Attempted - Cancel Running Process?", vbOKCancel + vbDefaultButton1 + vbQuestion, "Test") = vbOK Then
m_lngStopFlag = StopFlag.StopExit
End If
End If
Case Else
m_lngStopFlag = StopFlag.StopExit
Cancel = True
End Select
End Sub
You need some kind of shared variable so that your for loop and your button can communicate. I'd put the for loop (and its associated code) in a command object. My VB is rusty but I think you can declare Modules with their own 'global' variables and functions. You can move all the code into a module and just check the global variable as you do now.
My main concern with the code sample you've posted has nothing to do with user-cancelling but rather everything else: you check your running state by reading the button text instead of doing that the other way around (set the button text because of the running state, which should be stored in a variable); you use a GOTO to exit your for loop instead of a break (does VB have breaks?) and you put your cleanup code outside the normal flow, when it seems to me that it could be run regardless of whether the user cancelled or not.
One possible alternative is to offload your heavy-work function to a new Thread. Then you can either directly kill that thread if the user wants to cancel, or you can send a message to the thread.
Toggling via button name, as you are doing above, is a pretty commonly seen trick though, and pretty safe if you only have a couple of button states.
I've always used a global boolean variable like bUserPressedCancel, along with DoEvents within a loop. Elegant, smelegant, it works.
I agree with Mr Shiny that testing against the value of a caption is not a great idea. If you change the text on the button in the designer, you'll break the code. Better not to rely on the wording of the text for your code to work.
Works until you need to do localization or any other thing else that require the UI to be changed independently of the logic. I would use the Tag property or a private module level variable . Then you can vary the caption independently from the logic.
If it was me I'd use 2 buttons - one to GO and one to STOP. The STOP button is made visible when you click GO. The Click event for STOP simply hides itself - that's it.
Your loop can then simply check to see if the STOP button is still visible. If it's not, that means it was clicked and you should break out.
Your controls are static objects with form scope...

Resources