views:

3396

answers:

4

Hi, I've got a problem with VB6. I have a form with several ComboBox objects on it. I wish to populate the ComboBoxes via a function that takes a SQL query as a parameter. So the code looks like this

Private Function FillComboBoxFromMDB(ByVal sDBName As String, _
                                     ByVal sSQL As String) As ComboBox
    '/*
    ' * Execute SQL in MDB and fill the ComboBox with the results
    ' * Returns filled ComboBox
    ' */
    Dim DB As Database
    Dim DBRecordset As Recordset

    On Error GoTo FillComboBoxFromMDB_ErrHandler

    Set DB = OpenDatabase(sDBName, False, False)

    If Not DB Is Nothing Then
        Set DBRecordset = DB.OpenRecordset(sSQL)
        If Not DBRecordset Is Nothing Then
            If DBRecordset.RecordCount > 0 Then
                Call FillComboBoxFromMDB.AddItem(DBRecordset.Fields(0).Value)
                ' ^^ This row gives the "Object variable or With block variable not set"
            End If
        Else
            Call WriteLog("Unable to execute " & sSQL)
        End If
        DB.Close
    Else
        Call WriteLog("Unable to open " & sDBName)
    End If

    Exit Function
FillComboBoxFromMDB_ErrHandler:
    Call WriteLog("FillComboBoxFromMDB() error: " & Err.Number & " " & Err.Description)
End Function

I call the function like this.

Private Function Test()
    ' Fill the combobox
    frmMyForm.cmbMyCombo = FillComboBoxFromMDB("Database.mdb", _
                                               "SELECT MyTable.MyText FROM MyTable")
End Function

So basically I understand that this comes down to instantiation, but I haven't found anything useful about it online. The New keyword doesn't work like it works in VB.Net. How do I instantiate the FillComboBoxFromMDB combobox so that the function will work? Is it even possible?

Thanks in advance!

+5  A: 

You code expresses the belief that the identifier FillComboBoxFromMDB has acquired a reference to the combobox on the left hand side of the assignment in the Test procedure.

This is not the case the function will execute first with FillCombBoxFromMDB being Nothing once it it would attempt (and fail) to assign the result to the left hand side.

You need to pass the combobox as a parameter.

Private Sub FillComboBoxFromMDB(ByVal sDBName As String, _
                                     ByVal sSQL As String, ByVal cbo As ComboBox)
    '/*
    ' * Execute SQL in MDB and fill the ComboBox with the results
    ' * Returns filled ComboBox
    ' */
    Dim DB As Database
    Dim DBRecordset As Recordset

    On Error GoTo FillComboBoxFromMDB_ErrHandler

    Set DB = OpenDatabase(sDBName, False, False)

    If Not DB Is Nothing Then
        Set DBRecordset = DB.OpenRecordset(sSQL)
        If Not DBRecordset Is Nothing Then
            If DBRecordset.RecordCount > 0 Then
                Call cbo.AddItem(DBRecordset.Fields(0).Value)
                ' ^^ This row gives the "Object variable or With block variable not set"
            End If
        Else
            Call WriteLog("Unable to execute " & sSQL)
        End If
        DB.Close
    Else
        Call WriteLog("Unable to open " & sDBName)
    End If

    Exit Sub
FillComboBoxFromMDB_ErrHandler:
    Call WriteLog("FillComboBoxFromMDB() error: " & Err.Number & " " & Err.Description)
End Sub

Call it like this:-

 Private Function Test()
 ' Fill the combobox
 Call FillComboBoxFromMDB("Database.mdb", _
                          "SELECT MyTable.MyText FROM MyTable", _
                          frmMyForm.cmbMyCombo ) 
 End Function
AnthonyWJones
OK, but this basically defeats the idea of having a function. Might as well convert it to a Sub and pass the combobox ByRef.
Gert
No point having a function if you can't do anything with its return value. ComboBoxes in VB6 aren't things you can create and assign like other values. As you can see I've already converted it to a Sub
AnthonyWJones
Yeah, I just wanted to know if there was some way. I had a hunch that this wouldn't play out. Thanks for clearing it up.
Gert
Although I agree you can't create a ComboBox as you can a normal object (with a New statement), why can't you pass them?
Ant
@Ant: There is no reason you can't pass them, I'm passing in one in the code above.
AnthonyWJones
Oh ok. I was a bit confused by your above comment "ComboBoxes in VB6 aren't things you can... assign like other values." Thanks for the clarification! :)
Ant
+1  A: 

Q: What's FillComboBoxFromMDB set to before you call AddItem?
A: Nothing, that is why you get the error

Try defining a variable like

Dim Value as ComboBox

Then calling the AddItem on this

Value.AddItem(...)

then at the end of the function have

FillComboBoxFromMDB = Value

Or as the other answer if you don't want to use a return type like you were trying to use.

RoguePlanetoid
Unfortunately that does not work.
Gert
This won't work as `Dim Value As ComboBox` does not instantiate a new `ComboBox`.
0xA3
Yep, that's what I meant.
Gert
Yes sorry about that example there just was not meant to be a working example but point in the right direction as I didn't have VB to hand when answering. Thanks other commenters for noticing this, hope it was somewhat helpful!
RoguePlanetoid
+1  A: 

You have a function which claims that its return type is ComboBox, but I can't see anywhere where you ever actually set the return value. Since the return value is never set, it will be Nothing, hence your error when you access it.

From the use case you supply, I think what you want is a helper subroutine that works on an existing combobox. So you would call it like this:

' Fill the combobox
FillComboBoxFromMDB(frmMyForm.cmbMyCombo, _
                    "Database.mdb", _
                    "SELECT MyTable.MyText FROM MyTable")

and the subroutine itself would have a signature like this:

Private Sub FillComboBoxFromMDB(ByVal cbo As ComboBox, _ ByVal sDBName As String, _ ByVal sSQL As String)

(note that it is a Sub not a Function). Within the body of the subroutine, where you have

 Call FillComboBoxFromMDB.AddItem(DBRecordset.Fields(0).Value)

instead have

 cbo.AddItem(DBRecordset.Fields(0).Value)

to act on the ComboBox that was passed into the subroutine.

AakashM
+1  A: 

That the problem with working with vb6 form controls, they can only be instantiated in a form. What freakin' horseshit! Oh yeah you can register the DLL that the controls reside in. Have fun with that! I ran into this with the tcp/ip socket.

My solution was to create a SocketDriver interface. Create a form and put the socket on the form. Make the form invisible. Implement the SocketDriver interface on the form. Now you can pass the SocketDriver around.

I like Anthony's answer, except that I would have created an interface named 'DataFiller' with one method.

Public Sub AddItem(item As String)
End Sub

Then implement on your form.

Public Sub AddItem(item As String)
   cmbMyCombo.AddItem(item)
End Sub

Now use Signature

Private Sub FillComboBoxFromMDB(ByVal sDBName As String, _
                                 ByVal sSQL As String, ByVal injectWith As DataFiller)
   'yada yada code

   injectWith.AddItem(DBRecordset.Fields(0).Value)

   'yada yada code

End Sub

Private Function Test()
   ' Fill the combobox
   FillComboBoxFromMDB("Database.mdb", _
                                           "SELECT MyTable.MyText FROM MyTable", frmMyForm)
End Function

By using the interface you can have some separation of concerns. Your data access knows nothing about forms or controls and your froms and controls does not know where that data came from because the dependency is on an interface

Gutzofter