Solved

Binding lost after refreshing

Posted on 2001-09-02
5
339 Views
Last Modified: 2008-02-20
Why do my bound controls lose binding relation to my data source control after the data source control?s recordset refreshed?

In my project, I need my own data source control instead of data control provided by MS ADO to provide special business rules control. In my test usercontrol (looks exactly like the adodc control), I successfully got my recordset bound to appropriate data consumers, among which there?s a datagrid. It looks everything is fine when I move from record to record, or do addnew, update, begintrans, committrans and rollbacktrans. But there?s a very ugly problem. When my operation to the data source involves refreshing (rs.close and rs.open), the bound controls will lose binding relation to the data source and they will act on themselves with the old recordset no matter how the data source is doing. When the datagrid moves from row to row, other bound controls will change respectively. From the debug messages printed in the data source?s events, I know the data source control itself works very normally except not controlling the bound controls any more.

I guess I need to do something like updatecontrols in an appropriate event to inform the bound controls that I have refreshed the recordset. But I don?t know how to do it. I don?t know if there?s any other way than getdatamember that can establish or reestablish the relation between a data source and a data consumer. I also consider it?s very ugly if I have to programmatically bind data consumers one by one. I hope there?s some convenient way to tell the data consumers themselves to do the binding again.

So please teach me why the binding is lost and how to resolve it.

Thanks in advance.

Thman
0
Comment
Question by:thman
5 Comments
 
LVL 3

Expert Comment

by:Bahnass
ID: 6449231
Let Us say all controls bound to myAdo will have tag property myAdo
Just REBOUND Them


for each ctrl in controls
    if ctrl.tag="myAdo" then
        set ctrl.datasource = myado
    end if
next

' Note DataField Property not affected
0
 
LVL 49

Expert Comment

by:Ryan Chong
ID: 6449438
Hi thman,

please show your codes so ppls can help

regards.
0
 
LVL 11

Author Comment

by:thman
ID: 6452021
Hi, Ryancys,

My test codes are very similar to the sample of the AxData come with the VS6.0. I haven?t added any business rules into it yet. So my problem exists in the sample too.

Hi, Bahnass,
If I was as smart as you to use the tag property, I shouldn?t have spent so much time to do the things described bellow. But when I received your hint, I had already started to work on the thoughts and couldn?t stop. Any way, I figured out a way that will not require users to make a tag while they bind a data consumer control to my data source component. It?s very tedious, though. Please keep me posted if you get any new idea. Thanks.

-----
I have a bit study on data consumer controls? DataSource property. It?s usually a string (some like datagrids are not) at design time and object at run time. When it?s an object, it?s still very special that you can know its type is the type of the data source bound to the consumer but you just can?t get access to any members of the kind of object. That is a problem if you have a few instance of that kind of data source because you will not be able to tell which exactly the data consumer is being bound to. Let?s say we have a data source control typed MyAdodc and have inserted to a form three instances of it, named MyAdodc1, MyAdodc2 and MyAdodc3 respectively. We bound them relatively to three groups of data consumers: (Datagrid1, Text1, Text2), (Datagrid2, Text3, Text4) and (Datagrid3, Text5, Text6). Each of these consumers? Data Source property is an object of MyAdodc type, we are not sure which instance it is bound to.

My solution is to try to save binding relation as a property at design time so we can get the information that helps us when rebinding at run time. Since the DataSource property is a string that contains the data source instance?s name, we can save this information to a private property (call CollectDataConsumer method) of MyAdodc when WriteProperties event rises. I tried to save them to a collection property and failed because I got an error message saying, ?object can?t save because it doesn?t support persistence?. So I have to save them as a string and then parse it to a collection when ReadProoerties event rises. To make sure we always get the property saved, we also need to raise a PropertyChanged event when getDataMember method is called. When users refresh the recordset, we need call the RebindDataConsumer method. Below are parts of the codes:

Withevents rs As adodb.Recordset
Private m_Ctls As Collection
Private m_strCtls As String
Private ctl As Control
Private myself As Object
Private isInRequery As Boolean
Private dontRaiseEvent As Boolean

'Events statements omitted.

Private Sub parseCtls()
    Dim tmpstr As String
    Dim tmpstrCtls As String
    Dim tmplen As Integer
   
    If Len(m_strCtls) = 0 Then
        Set m_Ctls = Nothing
        Exit Sub
    Else
        Set m_Ctls = New Collection
    End If
   
    tmpstrCtls = m_strCtls
    tmplen = InStr(1, tmpstrCtls, ";")
    Do While tmplen > 1
        tmpstr = Mid(tmpstrCtls, 1, tmplen - 1)
        m_Ctls.Add tmpstr, tmpstr
        tmpstrCtls = Mid(tmpstrCtls, tmplen + 1, Len(tmpstrCtls) - tmplen)
        tmplen = InStr(1, tmpstrCtls, ";")
    Loop
    If Len(tmpstrCtls) > 0 Then
        m_Ctls.Add tmpstrCtls, tmpstrCtls
    End If
    If m_Ctls.Count = 0 Then Set m_Ctls = Nothing
End Sub

Private Sub CollectDataConsumers()
    Dim tmpstr As String
    Dim tmpdb As DataBinding
   
    'Clear the string because we want to do it all over
    m_strCtls = ""
   
    For Each ctl In Extender.Parent.Controls
        On Error Resume Next
        'Get the type of the datasource property.
        'if encountered an error, try next since it must not be a data consumer
        tmpstr = TypeName(ctl.DataSource)
        If Err.Number <> 0 Then
            Err.Clear
        Else
            On Error GoTo 0
            Debug.Print tmpstr & " " & ctl.Name
            If tmpstr = "String" Then
                'if the type is string, it contains the data source's instance name.
                If ctl.DataSource = Extender.Name Then
                    m_strCtls = m_strCtls & IIf(Len(m_strCtls) = 0, "", ";") & ctl.Name
                End If
            Else
                'Only when the type name is my type, it can be bound to me.
                If tmpstr = TypeName(Me) Then
                    'still we need to check whether it is bound to exact me.
                    'At design time, the databindings collection includes bound data source information.
                    'At run time, we will lose the chance.
                    For Each tmpdb In ctl.DataBindings
                        Debug.Print tmpdb.PropertyName & ";" & tmpdb.DataSourceName & ";" & tmpdb.DataMember & ";" & tmpdb.DataField & ";IsDataSource:" & tmpdb.IsDataSource & ";IsBindable:" & tmpdb.IsBindable
                        If tmpdb.PropertyName = "DataSource" And tmpdb.DataSourceName = Extender.Name Then
                            m_strCtls = m_strCtls & IIf(Len(m_strCtls) = 0, "", ";") & ctl.Name
                        End If
                    Next
                End If
            End If
        End If
    Next
    Debug.Print m_strCtls
End Sub

Private Sub RebindDataConsumers()
    Dim tmpstr As String
    'Don't want the user of this component to complain he gets too much events.
    Dim isFirstBinding As Boolean
   
    'Get the control of myself. It?s different from Me.
    If myself Is Nothing Then
        For Each ctl In Parent.Controls
            If ctl.Name = Extender.Name Then
                Set myself = ctl
                Exit For
            End If
        Next
    End If
   
    isFirstBinding = True
   
    For Each ctl In Parent.Controls
        On Error Resume Next
        tmpstr = TypeName(ctl.DataSource)
        If Err.Number <> 0 Then
            Err.Clear
        Else
            On Error GoTo 0
            Debug.Print tmpstr & " " & ctl.Name
            If tmpstr = "String" Then
                'I don't think it could be a string.
                'This is just for safe
                If ctl.DataSource = Extender.Name Then
                    If Not isFirstBinding Then
                        dontRaiseEvent = True
                    End If
                    Set ctl.DataSource = myself
                    If Not isFirstBinding Then
                        dontRaiseEvent = False
                    End If
                    isFirstBinding = False
                End If
            Else
                If tmpstr = TypeName(Me) Then
                    'Initially, it goes here.
                    'we have to check whether it's in our list.
                    If isCtlIn(ctl) Then
                        If Not isFirstBinding Then
                            dontRaiseEvent = True
                        End If
                        Set ctl.DataSource = myself
                        If Not isFirstBinding Then
                            dontRaiseEvent = False
                        End If
                        isFirstBinding = False
                    End If
                End If
                If tmpstr = Extender.Name Then
                    'Once we have set myself to the datasource property, it goes here.
                    If Not isFirstBinding Then
                        dontRaiseEvent = True
                    End If
                    Set ctl.DataSource = myself
                    If Not isFirstBinding Then
                        dontRaiseEvent = False
                    End If
                    isFirstBinding = False
                End If
            End If
        End If
    Next
   
End Sub

Private Function isCtlIn(ByVal vCtl As Control) As Boolean
    Dim tmpCtl As Variant
   
    isCtlIn = False
   
    If m_Ctls Is Nothing Then Exit Function
       
    For Each tmpCtl In m_Ctls
        If tmpCtl = vCtl.Name Then
            isCtlIn = True
            Exit For
        End If
    Next
End Function

Private Sub UserControl_GetDataMember(DataMember As String, Data As Object)
    'If strCtls Is empty, set the property dirty
    'So it will invoke WriteProperties to call CollectDataConsumers if this is at design time.
If Len(strCtls) = 0 Then strCtls = m_strCtls

'Omitted other codes.
End Sub

Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
'Omitted other codes.

    m_strCtls = PropBag.ReadProperty("strCtls", "")
    parseCtls
End Sub

Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
'Omitted other codes.

    CollectDataConsumers
    strCtls = m_strCtls
    Call PropBag.WriteProperty("strCtls", m_strCtls, "")
End Sub

Private Sub rs_WillMove(ByVal adReason As EventReasonEnum, adStatus As EventStatusEnum, ByVal pRecordset As Recordset)
    If Not dontRaiseEvent Then RaiseEvent WillMove(adReason, adStatus, pRecordset)
End Sub

Private Sub rs_MoveComplete(ByVal adReason As EventReasonEnum, ByVal pError As Error, adStatus As EventStatusEnum, ByVal pRecordset As Recordset)
    If Not dontRaiseEvent Then RaiseEvent MoveComplete(adReason, pError, adStatus, pRecordset)
    If adStatus = adStatusOK And isInRequery Then
        'The undeniable move is done.
        'Erase the mark and invoke the rebinding.
        isInRequery = False
        RebindDataConsumers
    End If
    If adStatus = adStatusOK And adReason = adRsnRequery Then
        'Requery finished. It will take an undeniable move.
        'So only make a mark here
        isInRequery = True
    End If
End Sub

'Other events omitted.

'Other properties and methods omitted.


The codes run well in my ActiveX component. But there should be better way to do it provided more kernel details of relationship between data source and consumers. MS? Adodc Control doesn?t make any change to the DataSource property and make things done. So if anyone knows how MS does it, give me a hint. I?ll appreciate it.


0
 

Accepted Solution

by:
WaterDog earned 100 total points
ID: 6473326
It's very simple, thman. You've worked around too far.

Whenever you open or requery a recordset, you get a undeniable move event and you can call a DataMemberChanged memthod to notify data consumers to get datamember again.

Cheers,
WaterDog
0
 
LVL 11

Author Comment

by:thman
ID: 6473371
Hi, WaterDog, thank you very much. I just tested it. It works fine.

I'd definitely give you an A.
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Introduction In a recent article (http://www.experts-exchange.com/A_7811-A-Better-Concatenate-Function.html) for the Excel community, I showed an improved version of the Excel Concatenate() function.  While writing that article I realized that no o…
Introduction While answering a recent question (http://www.experts-exchange.com/Q_27402310.html) in the VB classic zone, I wrote some VB code in the (Office) VBA environment, rather than fire up my older PC.  I didn't post completely correct code o…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now