I'm creating a program in which I have a publicly defined boolean value
Public boolOverallStatus As Boolean = True
and I need to execute some code whenever the boolean value changes. In previous applications, an actual form item change handled this, but it can be changed by several different subs.
How would I handle this? I'm looking through msdn, but it's rather confusing.
In a nutshell: How to execute code when the event of a boolean value changing occurs.
Make it a property instead.
Private _boolOverallStatus As Boolean = True
Property boolOverallStatus As Boolean
Get
Return _boolOverallStatus
End Get
Set(ByVal value as Boolean)
If value <> _boolOverallStatus Then
_boolOverallStatus = value
'// handle more code changes here.'
End If
End Set
End Property
I use the following pattern which resembles what Microsoft do for most of their Changed events.
Class MyClass
Public Property OverallStatus As Boolean
Get
Return _OverallStatus
End Get
Set (value As Boolean)
If _OverallStatus = value Then Exit Property
_OverallStatus = value
OnOverallStatusChanged(EventArgs.Empty)
End Set
End Property
Private _OverallStatus As Boolean = False
Protected Overridable Sub OnOverallStatusChanged(e As EventArgs)
RaiseEvent OverallStatusChanged(Me, e)
End Sub
Public Event OverallStatusChanged As EventHandler
End Class
In VB, you can handle the event using the WithEvents and Handles keywords:
Class MyParent
Private WithEvents myObject As New MyClass()
Private Sub myobject_OverallStatusChanged(sender As Object, e As EventArgs) Handles myObject.OverallStatusChanged
' TODO: Write handler.
End Sub
End Class
The OnOverallStatusChanged function is useful for inheriting classes to get first shot at responding to the change.
Class MyOtherClass
Inherits MyClass
Protected Overrides Sub OnOverallStatusChanged(e As EventArgs)
' TODO: Do my stuff first.
MyBase.OnOverallStatusChanged(e)
End Sub
End Class
Use public properties instead of public variables. You can then put logic in the Set method of the property to execute whenever the property is.. well set.
http://msdn.microsoft.com/en-us/library/65zdfbdt%28v=VS.100%29.aspx
Private number As Integer = 0
Public Property MyNumber As Integer
' Retrieves number.
Get
Return number
End Get
' Assigns to number.
Set
CallLogicHere()
number = value
End Set
End Property
You could also define an event, which is fired each time the status changes. The advantage is, that changes can be handled by the parts of the application that depend on this status. Otherwise the logic would have to be implemented together with the status.
Other parts of the application can subscribe the event with AddHandler.
Public Class OverallStatusChangedEventArgs
Inherits EventArgs
Public Sub New(newStatus As Boolean)
Me.NewStatus = newStatus
End Sub
Private m_NewStatus As Boolean
Public Property NewStatus() As Boolean
Get
Return m_NewStatus
End Get
Private Set
m_NewStatus = Value
End Set
End Property
End Class
Module modGlobals
Public Event OverallStatusChanged As EventHandler(Of OverallStatusChangedEventArgs)
Private m_boolOverallStatus As Boolean = True
Public Property BoolOverallStatus() As Boolean
Get
Return m_boolOverallStatus
End Get
Set
If Value <> m_boolOverallStatus Then
m_boolOverallStatus = Value
OnOverallStatusChanged(Value)
End If
End Set
End Property
Private Sub OnOverallStatusChanged(newStatus As Boolean)
RaiseEvent OverallStatusChanged(GetType(modGlobals), New OverallStatusChangedEventArgs(newStatus))
End Sub
End Module
Related
Using a Class I am trying to hide the DoubleBuffered property from form's property window but without make it nonfunctional. So I did something like this in code example below... Ηowever, DoubleBuffered property still appears. So, can we really hide DoubleBuffered property and if yes, how can we do that?
Imports System.ComponentModel
Imports System.ComponentModel.Design
Public Class MyForm
Inherits Form
<Browsable(False)>
Public Overloads Property DoubleBuffered As Boolean
Get
Return MyBase.DoubleBuffered
End Get
Set(ByVal value As Boolean)
MyBase.DoubleBuffered = value
End Set
End Property
Public Sub New()
Me.DoubleBuffered = True
End Sub
End Class
You could create a custom component designer for your Form, but that is a daunting task to just recreate the functionality of the inaccessible System.Windows.Forms.Design.FormDocumentDesigner. The simpler way is use the Form's Site property as I have shown you before to access the designer services.
In this case, you need to override the ITypeDescriptorFilterService service of the designer host. This service is used by the designer for all type discovery/filtering operations and is not limited to a specific component.
The first step is to create a class that implements ITypeDescriptorFilterService. The following is one such implementation. It is a generic implementation that allows it to filter components of the specified type and takes list of property names that you want to exclude from the PropertyGrid display. The final item it requires is a reference to the existing service used by the designer host.
Friend Class FilterService(Of T) : Implements ITypeDescriptorFilterService
Private namesOfPropertiesToRemove As String()
Public Sub New(baseService As ITypeDescriptorFilterService, ParamArray NamesOfPropertiesToRemove As String())
Me.BaseService = baseService
Me.namesOfPropertiesToRemove = NamesOfPropertiesToRemove
End Sub
Public ReadOnly Property BaseService As ITypeDescriptorFilterService
Public Function FilterAttributes(component As IComponent, attributes As IDictionary) As Boolean Implements ITypeDescriptorFilterService.FilterAttributes
Return BaseService.FilterAttributes(component, attributes)
End Function
Public Function FilterEvents(component As IComponent, events As IDictionary) As Boolean Implements ITypeDescriptorFilterService.FilterEvents
Return BaseService.FilterEvents(component, events)
End Function
Public Function FilterProperties(component As IComponent, properties As IDictionary) As Boolean Implements ITypeDescriptorFilterService.FilterProperties
' ref: ITypeDescriptorFilterService Interface: https://msdn.microsoft.com/en-us/library/system.componentmodel.design.itypedescriptorfilterservice(v=vs.110).aspx
'
' The return value of FilterProperties determines if this set of properties is fixed.
' If this method returns true, the TypeDescriptor for this component can cache the
' results. This cache is maintained until either the component is garbage collected or the Refresh method of the type descriptor is called.
' allow other filters 1st chance to modify the properties collection
Dim ret As Boolean = BaseService.FilterProperties(component, properties)
' only remove properties if component is of type T
If TypeOf component Is T AndAlso Not (properties.IsFixedSize Or properties.IsReadOnly) Then
For Each propName As String In namesOfPropertiesToRemove
' If the IDictionary object does not contain an element with the specified key,
' the IDictionary remains unchanged. No exception is thrown.
properties.Remove(propName)
Next
End If
Return ret
End Function
End Class
Example Usage in Form:
Imports System.ComponentModel
Imports System.ComponentModel.Design
Public Class TestForm : Inherits Form
Private host As IDesignerHost
Private altTypeDescriptorProvider As FilterService(Of TestForm)
' spelling and character casing of removedPropertyNames is critical
' it is a case-sensative lookup
Private Shared removedPropertyNames As String() = {"DoubleBuffered"}
Public Overrides Property Site As ISite
Get
Return MyBase.Site
End Get
Set(value As ISite)
If host IsNot Nothing Then
UnwireDesignerCode()
End If
MyBase.Site = value
If value IsNot Nothing Then
host = CType(Site.GetService(GetType(IDesignerHost)), IDesignerHost)
If host IsNot Nothing Then
If host.Loading Then
AddHandler host.LoadComplete, AddressOf HostLoaded
Else
WireUpDesignerCode()
End If
End If
End If
End Set
End Property
Private Sub HostLoaded(sender As Object, e As EventArgs)
RemoveHandler host.LoadComplete, AddressOf HostLoaded
WireUpDesignerCode()
End Sub
Private Sub WireUpDesignerCode()
AddFilter()
End Sub
Private Sub UnwireDesignerCode()
If host IsNot Nothing Then
RemoveFilter()
End If
host = Nothing
End Sub
Private Sub AddFilter()
Dim baseFilter As ITypeDescriptorFilterService = CType(host.GetService(GetType(ITypeDescriptorFilterService)), ITypeDescriptorFilterService)
If baseFilter IsNot Nothing Then
' remove existing filter service
host.RemoveService(GetType(ITypeDescriptorFilterService))
' create our replacement service and add it to the host's services
altTypeDescriptorProvider = New FilterService(Of TestForm)(baseFilter, removedPropertyNames)
host.AddService(GetType(ITypeDescriptorFilterService), altTypeDescriptorProvider)
TypeDescriptor.Refresh(Me.GetType) ' force a type description rescan
End If
End Sub
Private Sub RemoveFilter()
If altTypeDescriptorProvider IsNot Nothing Then
host.RemoveService(GetType(ITypeDescriptorFilterService))
host.AddService(GetType(ITypeDescriptorFilterService), altTypeDescriptorProvider.BaseService)
altTypeDescriptorProvider = Nothing
End If
End Sub
End Class
Now when you create a form that inherits from TestForm, the DoubleBuffered property will be excluded from the PropertyGrid display.
I have a problem with ShouldSerialize function.
I defined an usercontrol with a label (named Label1) on it.
Now i put the code below in the usercontrol class :
Imports System.ComponentModel
Public Class UserControl1
Dim _Range As UShort = 100
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = _Range
End Sub
Public Function ShouldSerializeTBValueRange() As Boolean
Return _Range <> 200
End Function
Public Sub ResetTBValueRange()
_Range = 200
Label1.Text = _Range
End Sub
Public Property TBValueRange As UShort
Get
Return _Range
End Get
Set(ByVal Steps As UShort)
_Range = Steps
Label1.Text = _Range
End Set
End Property
End Class
Now in a new form include the usercontrol.
In the properties grid of the usercontrol u can find the property TBValueRange.
If you right click in the property name you can Reinit the property.
After reinit u can see value 200 in the property.
Now, regenerate the project will reset the property to the initial value (id 100).
Why the value 200 didn't stay?
If i replace the line Return _Range <> 200 in the function ShouldSerializeTBValueRange() by
Return Not _Range.Equals(200)
it will work.
i don't understand.
Anyone could explain this?
The methods are working as coded. The thing which seems to not work as expected is the Not _Range.Equals(200) portion.
You initialize _Range to 100: Dim _Range As UShort = 100
Then, your ShouldSerializeTBValueRange method tells VS to only save the range value when it is not 200: Return _Range <> 200
So, when you reset it to 200 in the IDE, it wont save a value of 200, and the initial value of 100 displays.
The code uses one value for the default, 100 but use a different value for ShouldSerialize test. You should either change it to use a default of 200:
Dim _Range As UShort = 200
Or change the ShouldSerialize test to use 100:
Public Function ShouldSerializeTBValueRange() As Boolean
Return _Range <> 100
End Function
Treating the default value as the default, and using only one value for the default everything works as expected using just ShouldSerializexxx and Resetxxx with no need for anything else.
' THIS is the default value -
' ie the value that need not be saved because the
' control automatically starts with that value
Dim _Range As UShort = 200US
' Controls:
' - displays the prop value in Bold in the property window
' when the value is NOT the default
' - saves the value when it is NOT the default
' - enables/disables the RESET function
Public Function ShouldSerializeTBValueRange() As Boolean
Return (_Range <> 200US)
End Function
Using one value for the actual default (100) and then returning T/F based on a different value (200) results in
100 being saved when it need not be
200 not being saved when it should be
the Reset menu item and Bold value being in the incorrect state
The oddity is that Not _Range.Equals(200) seems to fail. It returns False after a reset (to 200) which causes the value to be saved when it really should not. There are 2 overloads for this:
UInt16.Equals(obj As Object) As Boolean
UInt16.Equals(v As UShort) As Boolean
If you pass anything other than an actual UInt16/UShort, the value will be boxed as Object. So, Not _Range.Equals(200) is using the first because 200 is Int32. The ILCode for that version is:
public override bool Equals(object obj)
{
return obj is ushort && this == (ushort)obj;
}
The test will fail the first test because obj contains an Int32. If you pass a UShort, it will work:
UShort.Equals(_Range, 200US)
'or:
_Range.Equals(200US)
All good reasons to understand the different data types, avoid boxing (As Object) and always and forever set Option Strict On.
Thanks you all to have spent time on my question.
I found my answer : when ShouldSerialize function return false the property value isn't save in the file Form1.designer.vb, so it gets the original value.
For my needed the ShouldSerialize function should always return true.
Public Function ShouldSerializeTBValueRange() As Boolean
Return true
End Function
So the property value will always be saved.
Now, what i'm looking for is how to control the context menu of the property grid.
I want the option "Reinit" greyed when the value is good.
Finally, i found something, with the use of PropertyDescriptor.
For whose are interested, we can save properties in the designer.vb file and test the value to reset.
here is my program, based on the article from MSDN - Microsft:
Imports System.ComponentModel
Imports System.Windows.Forms.Design
<Designer(GetType(DemoControlDesigner))>
Public Class UserControl1
Dim _MyProperty1 As String = "Test1"
Dim _MyProperty2 As Integer = 100
Dim _MyProperty3 As UShort = 200
Public Sub New()
InitializeComponent()
End Sub
Public Function CanResetMyProperty1() As Boolean
Return _MyProperty1 <> "Test"
End Function
Public Sub ResetMyProperty1()
_MyProperty1 = "Test"
End Sub
Public Property MyProperty1 As String
Get
Return _MyProperty1
End Get
Set(ByVal Value As String)
_MyProperty1 = Value
End Set
End Property
Public Function CanResetMyProperty2() As Boolean
Return _MyProperty2 <> 150
End Function
Public Sub ResetMyProperty2()
_MyProperty2 = 150
End Sub
Public Property MyProperty2 As Integer
Get
Return _MyProperty2
End Get
Set(ByVal Value As Integer)
_MyProperty2 = Value
End Set
End Property
Public Function CanResetMyProperty3() As Boolean
Return _MyProperty3 <> _MyProperty2
End Function
Public Sub ResetMyProperty3()
_MyProperty3 = _MyProperty2
End Sub
Public Property MyProperty3 As UShort
Get
Return _MyProperty3
End Get
Set(ByVal Value As UShort)
_MyProperty3 = Value
End Set
End Property
End Class
Friend NotInheritable Class SerializePropertyDescriptor
Inherits PropertyDescriptor
Private _pd As PropertyDescriptor = Nothing
Public Sub New(ByVal pd As PropertyDescriptor)
MyBase.New(pd)
_pd = pd
End Sub
Public Overrides ReadOnly Property Attributes() As AttributeCollection
Get
Return Me._pd.Attributes
End Get
End Property
Protected Overrides Sub FillAttributes(ByVal attributeList As IList)
MyBase.FillAttributes(attributeList)
End Sub
Public Overrides ReadOnly Property ComponentType() As Type
Get
Return Me._pd.ComponentType
End Get
End Property
Public Overrides ReadOnly Property Converter() As TypeConverter
Get
Return Me._pd.Converter
End Get
End Property
Public Overrides Function GetEditor(ByVal editorBaseType As Type) As Object
Return Me._pd.GetEditor(editorBaseType)
End Function
Public Overrides ReadOnly Property IsReadOnly() As Boolean
Get
Return Me._pd.IsReadOnly
End Get
End Property
Public Overrides ReadOnly Property PropertyType() As Type
Get
Return Me._pd.PropertyType
End Get
End Property
Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
Try
Return CallByName(component, "CanReset" & _pd.Name, CallType.Get, Nothing)
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return False
End Function
Public Overrides Function GetValue(ByVal component As Object) As Object
Return Me._pd.GetValue(component)
End Function
Public Overrides Sub ResetValue(ByVal component As Object)
Me._pd.ResetValue(component)
End Sub
Public Overrides Sub SetValue(ByVal component As Object, ByVal val As Object)
Me._pd.SetValue(component, val)
End Sub
Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
Return True
End Function
End Class
Class DemoControlDesigner
Inherits ControlDesigner
Dim PropertiesToSerialize As String() = {"MyProperty1", "MyProperty2", "MyProperty3"}
Protected Overrides Sub PostFilterProperties(ByVal properties As IDictionary)
Dim original As PropertyDescriptor
For Each PropName As String In PropertiesToSerialize
If properties.Contains(PropName) Then
original = properties(PropName)
properties(PropName) = New SerializePropertyDescriptor(original)
End If
Next
MyBase.PostFilterProperties(properties)
End Sub
End Class
For each property that should be serialized and tested for reset, we should write a sub "CanResetPropertyName" which test the value to reset (see sample).
And the values stay now, even if we regenerate the project.
It's working fine for me, maybe it can be improved.
Regards.
I am trying to change the background color of a button (cmdLogQry) from a Set-procedure of a public property (LogQry) - according to the new value of the property.
It works if the property is being changed in the code belongig to the form containing the button (in the Click method of the same or even another button). But it does not work if the property is being changed from another module (handler procedure for COM ports DataReceived event). No error message or anything - the LogQry gets its value changed all right, but the color of the button does not change.
What do I do wrong?
Public Class Handler
Private _logQry As Boolean = False
Public Property LogQry() As Boolean
Get
Return _logQry
End Get
Set(ByVal value As Boolean)
_logQry = value
If value Then
frmMain.cmdLogQry.BackColor = Color.Red
Else
frmMain.cmdLogQry.BackColor = Color.Blue
End If
End Set
End Property
Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
...
LogQry = Not LogQry ' does NOT change color
...
End Sub
End Class
Public Class frmMain
Private comm As New Handler()
...
Private Sub cmdLogQry_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLogQry.Click
comm.LogQry = Not comm.LogQry ' does change color
End Sub
...
End Class
This problem is caused by the default instance of the form class created by the VB.NET implementation. More on default instances could be found here and in this answer from Hans Passant.
Essentially when you define a form class, VB.NET compiler creates a default instance of that class named with the same name of the class, but this creates a lot of misunderstanding in an object oriented environment like NET.
To fix your problem you need to implement a constructor in your Handler class that receives the actual instance of frmMain, store it inside a class variable and use that instance when you want to modify something on the actual displayed form
Public Class Handler
Private _logQry As Boolean = False
Private _mainInstance As frmMain
Public Sub New(mainInstance as frmMain)
_mainInstance = mainInstance
End Sub
Public Property LogQry() As Boolean
Get
Return _logQry
End Get
Set(ByVal value As Boolean)
_logQry = value
If value Then
_mainInstance.cmdLogQry.BackColor = Color.Red
Else
_mainInstance.cmdLogQry.BackColor = Color.Blue
End If
End Set
End Property
....
End Class
Now, when you create the Handler instance pass the reference to the current frmMain
Public Class frmMain
Private comm As Handler
...
Private Sub cmdLogQry_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLogQry.Click
comm = new Handler(Me)
comm.LogQry = Not comm.LogQry ' does change color
End Sub
...
End Class
Keep in mind that this solution creates also problems. It couples the class Handler to your frmMain and the two are now inseparable. Probably a better approach is to create an Event in the Handler class so, every form that wants to be notified could subscribe to the event and receieves the information when needed.
Conceptually, I am a little bit lost on Events themselves, so any information here would be helpful.
Specifically, I have a very simple Property Setter which raises an event, and I want to confirm
that this event is raised and also
that the proper parameters have been passes to said event. (although 2. may be unnecessary for this particular event.)
I am trying to contain the overhead in my Unit Testing project, that is, avoid any additional coding in the project being tested.
The code is as follows.
Public Class myItem
Shared Event ItemOpened As EventHandler(Of EventArgs)
.........
Public Property Open() As Boolean
Get
Return mOpen
End Get
Set(ByVal value As Boolean)
mOpen = value
RaiseEvent ItemOpened(Me, New EventArgs)
End Set
End Property
All code is being done in VB.net, which is primary reason why I have not found a good enough resource online for this yet. And also, I am not using a third-party mocking framework such as Nunit or RhinoMock, only MS built in Unit Testing frameworks in VS2012.
Also, similarly, I would like to test FirePropertyChangedNotification() on certain setter methods such as follows....
Public Property myBool() As Boolean
Set(ByVal Value As Boolean)
FirePropertyChangedNotification("myBool")
mViewOnly = Value
End Set
End Property
In which FirstPropertyChangedNotification() is as follows.....
Protected Sub FirePropertyChangedNotification(ByVal propName As String)
If Not Initializing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
RaiseEvent EntityChanged(Me, New PropertyChangedEventArgs(propName))
End If
End Sub
I'm not sure why you want to use Fakes here... You can just use a delegate in you test to subscribe to your event. Now change the property from the test and set a boolean or even place the assert in the delegate.
This is what I'd do in C#, autoconverted to VB.NET (which in my case is very rusty...) This works on my machine. Any improvements from a VB.NET expert are welcome:
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.ComponentModel
Namespace UnitTestProject2
Public Class Sut
Implements INotifyPropertyChanged
Public Property StringProperty() As String
Get
Return String.Empty
End Get
Set(value As String)
OnPropertyChanged("StringProperty")
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
End Class
<TestClass> _
Public Class UnitTest1
<TestMethod> _
Public Sub TestMethod1()
Dim sut As New Sut()
Dim triggered = False
AddHandler sut.PropertyChanged, Sub(o, e)
Assert.AreEqual("StringProperty", e.PropertyName)
triggered = True
End Sub
sut.StringProperty = "test"
Assert.IsTrue(triggered)
End Sub
<TestMethod> _
Public Sub TestMethod2()
Dim sut As New Sut()
Dim triggered = False
AddHandler sut.PropertyChanged, Sub(o, e)
Assert.AreSame(sut, o)
triggered = True
End Sub
sut.StringProperty = "test"
Assert.IsTrue(triggered)
End Sub
End Class
End Namespace
I read some articles and blogs on Implementation if IDisposable and GC working set. However, I could not understand the core areas of differentiation like:
Following is code of my test class:
Imports System.ComponentModel
Namespace Classes
Public Class BaseClass
Implements INotifyPropertyChanged
Implements IDisposable
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Friend Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
End If
Me.disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Public Class GenreClass
Inherits BaseClass
#Region "Private Variables"
Private _GenreValue As String
Private _IconValue As String
Private _IsSelectedValue As Boolean
Private _IsExpandedValue As Boolean
#End Region
#Region "Property Variables"
Property Genre As String
Get
Return _GenreValue
End Get
Set(Value As String)
If Not _GenreValue = Value Then
_GenreValue = Value
NotifyPropertyChanged("Genre")
End If
End Set
End Property
Property Icon As String
Get
Return _IconValue
End Get
Set(Value As String)
If Not _IconValue = Value Then
_IconValue = Value
NotifyPropertyChanged("Icon")
End If
End Set
End Property
Property IsSelected As Boolean
Get
Return _IsSelectedValue
End Get
Set(Value As Boolean)
If Not _IsSelectedValue = Value Then
_IsSelectedValue = Value
NotifyPropertyChanged("IsSelected")
End If
End Set
End Property
Property IsExpanded As Boolean
Get
Return _IsExpandedValue
End Get
Set(Value As Boolean)
If Not _IsExpandedValue = Value Then
_IsExpandedValue = Value
NotifyPropertyChanged("IsExpanded")
End If
End Set
End Property
#End Region
Protected Overrides Sub Dispose(disposing As Boolean)
Genre = Nothing
MyBase.Dispose(disposing)
End Sub
Public Overrides Function ToString() As String
Return Genre
End Function
End Class
End Namespace
My Test scenarios are as follows:
Test1:
Dim list1 As New List(Of HODLib.Classes.GenreClass)
For i = 0 To 4
Using z As New HODLib.Classes.GenreClass
With z
.Genre = "asdasd"
.Icon = "asdasdasdasdasd"
.IsExpanded = True
.IsSelected = True
End With
list1.Add(z)
End Using
Next
For Each z In list1
MessageBox.Show(z.ToString)
Next
result of test1 is that GC is called immediately and I loose access to resources, I get null message.
Test2:
Dim list1 As New List(Of HODLib.Classes.GenreClass)
For i = 0 To 4
Dim z As New HODLib.Classes.GenreClass
With z
.Genre = "asdasd"
.Icon = "asdasdasdasdasd"
.IsExpanded = True
.IsSelected = True
End With
list1.Add(z)
Next
For Each z In list1
MessageBox.Show(z.ToString)
Next
Result is Dispose is never called, even for z in forloop, I dont understand this, why is z not disposed, is it waiting because the list has reference to its values?
Test3:
Dim list1 As New List(Of HODLib.Classes.GenreClass)
For i = 0 To 4
Dim z As New HODLib.Classes.GenreClass
With z
.Genre = "asdasd"
.Icon = "asdasdasdasdasd"
.IsExpanded = True
.IsSelected = True
End With
list1.Add(z)
z.Dispose()
Next
For Each z In list1
MessageBox.Show(z.ToString)
Next
Result: Test2 vs test3 is calling the dispose manually after adding to the list. Result is that I lost access to resource, I get null message. Why did this happen although I added the object to list before calling the dispose method.
Thank you.
Unfortunately, the Dispose implementation that the Visual Basic IDE auto-generates is wrong in 99.9% of all cases. You should only use it if your class has a Finalize() method or the base class has a protected Dispose(Boolean) method. Which is extremely rare, finalizers are implemented by .NET Framework classes. It is their job to wrap unmanaged resources that should be released early.
It becomes 100% wrong when you find out that you can't write any meaningful code in the Dispose() method. Like this case, your class has no fields of a type that is disposable. Setting a field to Nothing has no effect.
You are adding a reference to the instance z which you create. When that instance is destroyed, the reference is no longer pointing to anything. The Using construct automatically calls Dispose for you. You can make a new copy of the object z by adding a method to the class:
Public Function Clone() As GenreClass
Return DirectCast(Me.MemberwiseClone(), GenreClass)
End Function
and use list1.Add(z.Clone).
See Object.MemberwiseClone Method for more information and if you need to create a deep copy.