How to properly unbind a ComboBox that's bound to a NameValueListBase obj

How to properly unbind a ComboBox that's bound to a NameValueListBase obj

Old forum URL: forums.lhotka.net/forums/t/5283.aspx


Flatulus posted on Thursday, August 21, 2008

Hello all,

After executing the following piece of code, the object remains in memory... the garbage collector won't collect the memory:

Dim tempObj as new MyExampleClass()

tempObj.Dispose( )

tempObj = Nothing

System.GC.Collect( )

System.GC.WaitingForPendingFinalizers( )

 

It seems to be due to "Handles SelectedValueChanged" because if i remove it, the object gets properly disposed and the memory collected.

Ways i found to "fix" this:

So i'm asking the CSLA Experts... what would be the proper way to unbind the combobox so that it's not linked to the NVList in any way?

Thanks,

   -Flat

PS: I'm using CSLA 2.1.

_____________________________

<Serializable()> _

Public Class MyNameValueList

Inherits NameValueListBase(Of Integer, String)

Private Shared _list As MyNameValueList

Public Shared Function GetMyNameValueList() As MyNameValueList

If CanReadObject() = False Then

Throw New AccessViolationException("You are not permitted to view Securitization Programs.")

End If

If _list Is Nothing Then

_list = DataPortal.Fetch(Of MyNameValueList)(GetType(MyNameValueList))

End If

Return _list

End Function

Public Shared Sub InvalidateCache()

_list = Nothing

End Sub

.......

End Class

_______________________________________________________________________

Partial Class MyControl

Friend WithEvents myComboBox As System.Windows.Forms.ComboBox

Sub New()

myComboBox.DisplayMember = "Value"

myComboBox.ValueMember = "Key"

myComboBox.DataSource = MyNameValueList.GetMyNameValueList()

myComboBox.DataBindings.Add("SelectedValue", _obj, "MyID", True, DataSourceUpdateMode.OnPropertyChanged)

End Sub

Private Sub myComboBox_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles myComboBox.SelectedValueChanged

End Sub

End Class

ajj3085 replied on Friday, August 22, 2008

What problem exactly are you trying to solve?  The best way to work with the garbage collector is to not worry about it at all.

Flatulus replied on Friday, August 22, 2008

ajj3085:
What problem exactly are you trying to solve?  The best way to work with the garbage collector is to not worry about it at all.

My problem... an avg of ~2MB memory leak each time i'm trying to dispose of 1 instance of the MyExampleClass. It doesn't show here, but my real class is *much* more complex than that. Each instance of that class takes ~2MB in memory and we have multiple instances of that class in memory at once. So each time we close our form (which contains anywhere from 1 to 13 instances of that control), we have a ~2-26MB memory leak and overtime and we end up receiving a OutOfMemoryError exception message.

richardb:
I think the recommendation is to unbind anything that you DataBindings.Add'ed in the first place as the form closes down.

I forgot to add the following line in a Form_Closing method in my above example class: myComboBox.DataBindings.Clear( )

But i already had this line in my real class and made sure it was called properly we the control is being closed.

Thanks for your replies,

    -Flat

richardb replied on Friday, August 22, 2008

We had similar issues with a .Net 1.1 project - out Winform app never seemed to garbage collect during the day and although that wasn't necessarily a problem, some users complained when they switched to another application the PC was "a bit slow".  Out winform exe was slowly using all the lovely memory available.

A combination of doing some manual GC calls and unbinding all controls (and sub controls) helped.

Private Sub ClearBindings(ByVal c As Control)
        Dim bindings(c.DataBindings.Count) As Binding
        c.DataBindings.CopyTo(bindings, 0)
        c.DataBindings.Clear()
        For Each bind As Binding In bindings
            System.ComponentModel.TypeDescriptor.Refresh(bind.DataSource)
        Next

        For Each cc As Control In c.Controls
            ClearBindings(cc)
        Next
    End Sub

Flatulus replied on Friday, August 22, 2008

Thanks for trying to help Richard!

I gave your piece of code a try but in my case, it didn't work. Somehow, the Shared member from MyNameValueList and my instances of MyExampleClass still seem to be linked via an event even after running your code.

Thanks again

ajj3085 replied on Friday, August 22, 2008

Flatulus:
My problem... an avg of ~2MB memory leak each time i'm trying to dispose of 1 instance of the MyExampleClass. It doesn't show here, but my real class is *much* more complex than that. Each instance of that class takes ~2MB in memory and we have multiple instances of that class in memory at once. So each time we close our form (which contains anywhere from 1 to 13 instances of that control), we have a ~2-26MB memory leak and overtime and we end up receiving a OutOfMemoryError exception message.


26MB and you get an OOM exception?  Seems odd.  Anyway, in your control's Dispose I would just set the myComboBox.DataSource = typeof( SecuritizationProgramNameValueList )That's C# code... but it should be easy enough to translate to VB. 

So try that and see if it helps... but I kinda think you're not disposing of other unmanaged resources properly.. that's usually where OOMEs come into play.  If you're cleaning up unmanged resources properly, there shouldn't even be a need for you to do anything with the System.GC class.

Also, here's a random blog entry that might be helpful.  It may or may not apply, if you're doing Async stuff or not.

richardb:
I think the recommendation is to unbind anything that you DataBindings.Add'ed in the first place as the form closes down.

I forgot to add the following line in a Form_Closing method in my above example class: myComboBox.DataBindings.Clear( )

But i already had this line in my real class and made sure it was called properly we the control is being closed.

Well, I think that clears databindings of the control, such as Value or Text to your business object... I don't think it will clear the combobox's DataSource, which I recommened you try above. 

Flatulus replied on Friday, August 22, 2008

26MB is for 1 form that contains 13 instances of the MyExampleClass control. So if during the day the user opens and closes that form 10 times, that a nice 260MB leak. Also, we use the same technic throughout our code... the leak probably occurs elsewhere.

I tried your "DataSource = typeof" thing... and i receive the following error:

Complex DataBindings accept as data source either an IList or IListSource.

And now i'm trying to assign a "Type" object. I don't know what this was supposed to achieve...

Thanks again for the reply!

NOTE: We are *not* using any System.GC call in our real code. I simply added the 2 System.GC lines above to show you that after i got rid of the MyExampleClass object there was a mem leak at that point.

ajj3085 replied on Friday, August 22, 2008

Flatulus:
26MB is for 1 form that contains 13 instances of the MyExampleClass control. So if during the day the user opens and closes that form 10 times, that a nice 260MB leak. Also, we use the same technic throughout our code... the leak probably occurs elsewhere.


Well, if your control is no longer referenced anywhere, it should "go away" automatically.  I have tons of dropdowns bound to NVL subclasses all over my application without problem.  Do you have any static events on your forms, controls or what-have-you?  I think they can cause references to hang around longer if you don't unhook static event handlers properly.

If the code I gave isn't work, dig into your .designer.vb file for your control, and find how the designer setup the datasource for your combobox.  Maybe you should try setting it to Nothing.

Flatulus:
And now i'm trying to assign a "Type" object. I don't know what this was supposed to achieve...


Well, I usually using BindingSource components, so that's how you set the BindingSource.DataSource when you don't want it bound to an actual value.  Setting it to Nothing causes an exception.. but it doesn't seem like you're using a BS component, so that was my fault.

Flatulus:
NOTE: We are *not* using any System.GC call in our real code. I simply added the 2 System.GC lines above to show you that after i got rid of the MyExampleClass object there was a mem leak at that point.


Understood, but trying to use the GC in that way can lead you down the wrong path when debugging.  I'm willing to bet something is holding a reference to your control or form, and that's the source of your problems.

Flatulus replied on Friday, August 22, 2008

ajj3085:
Well, if your control is no longer referenced anywhere, it should "go away" automatically.  I have tons of dropdowns bound to NVL subclasses all over my application without problem.  Do you have any static events on your forms, controls or what-have-you?  I think they can cause references to hang around longer if you don't unhook static event handlers properly.

That's exactly what i think is happening... if i completely remove the event handler below:

Private Sub myComboBox_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles myComboBox.SelectedValueChanged

End Sub

Then my objects' memory is getting properly collected after disposal.

ajj3085 replied on Friday, August 22, 2008

Hmm... this may be some kind of vb thing then.  Once your user control goes away, nothing should reference the combobox, or your user control, and the event only references your user control... so it should be fine.  Unless you're letting something else wire an event to one of your internal controls?

I really don't know how else to help you here.. sorry.

Flatulus replied on Monday, August 25, 2008

Found another way to fix that problem which i think is the one we're going to use in the future:

<In New()>
Dim myBS As New BindingSource
myBS.DataSource = MyNameValueList.GetMyNameValueList()
myComboxBox.DataSource = myBS

<In Dispose()>
DirectCast(myComboxBox.DataSource, BindingSource).DataSource = Nothing

The leak seems gone if i bind/unbind using that method.

Thanks all for your replies

ajj3085 replied on Monday, August 25, 2008

I was wondering of that could be one of the issues... you should always have a BindingSource component between your control and the actual BO.  My only recommendation would be to remove the BindingSource as you have it setup and do everything via the form's Designer.  Then when your control is loaded in the constructor you just need the line:

myBs.DataSource = MyNameValueList.GetMyNameValueList()

And you don't need to have any of the other code.

richardb replied on Friday, August 22, 2008

I think the recommendation is to unbind anything that you DataBindings.Add'ed in the first place as the form closes down.

Copyright (c) Marimer LLC