{"id":107,"date":"2005-09-26T00:00:00","date_gmt":"2006-05-09T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/custom-events-in-vb-net-2005\/"},"modified":"2021-05-17T18:36:52","modified_gmt":"2021-05-17T18:36:52","slug":"custom-events-in-vb-net-2005","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/custom-events-in-vb-net-2005\/","title":{"rendered":"Custom Events in VB.NET 2005"},"content":{"rendered":"<div id=\"pretty\">\n<h2>Creating custom events with Visual Basic .NET 2005<\/h2>\n<h2>Events model in VB.NET<\/h2>\n<p>Events are defined as actions or occurances of interest. An interested object can subscribe to an event and be notified when it happens. For example, a button on a form can generate a click event and the form can subscribe and get notified when the event occurs. This click event is processed through the form code.<\/p>\n<p>VB.NET 2005 provides a range of flexible event models to address simple or very complex events. Below is an example of a simple event using a <i>Worker <\/i>class with a <i>DoWork<\/i> method:<\/p>\n<pre>&#160; Public Class Worker&#160; \n&#160;&#160;&#160; Public Sub DoWork()&#160; \n&#160;&#160;&#160;&#160;&#160; For completedWork As Integer = 1 To 100&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Threading.Thread.Sleep(1000) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(completedWork) \n&#160;\n&#160;&#160;&#160;&#160;&#160; Next \n&#160;&#160;&#160; End Sub&#160; \n&#160; End Class <\/pre>\n<p>The <i>DoWork<\/i> method has a <i>For <\/i>loop repeating 100 times, waiting one second between loops, and printing out the completed work to the console. But a client such as a form using the object of the above class, for example, should provide visual feedback on how much work has been completed when the <i>DoWork <\/i>method is called. Adding an event to the <i>Worker <\/i>class and altering the <i>DoWork<\/i> method to call the event can provide this information to the client.<\/p>\n<pre>&#160;Public Class Worker \n&#160;\n&#160;&#160;&#160; Public Event WorkDone(ByVal completedWork As Integer)&#160; \n&#160;&#160;&#160; Public Sub DoWork() \n&#160;\n&#160;&#160;&#160;&#160;&#160; For completedWork As Integer = 1 To 100&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Threading.Thread.Sleep(1000)\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; RaiseEvent WorkDone(completedWork) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(completedWork) \n&#160;\n&#160;&#160;&#160;&#160;&#160; Next&#160; \n&#160;&#160;&#160; End Sub&#160; \n&#160; End Class&#160; <\/pre>\n<p>An event called <i>WorkDone<\/i> has been identified with an integer argument through which the amount of work completed can be passed. And in the <i>DoWork<\/i> method, the event passing the actual amount of work completed is raised. Now the above class can be consumed by a client by declaring and creating a <i>Worker <\/i>class object with events and handling the <i>WorkDone <\/i>event to provide feedback on progress.<\/p>\n<pre>Private WithEvents mWorker As New Worker \nPrivate Sub work_WorkDone(ByVal completedWork As Integer) Handles mWorker.WorkDone \nEnd Sub<\/pre>\n<p>In the above code, the <i>work_WorkDone <\/i>method will be called each time the <i>WorkDone <\/i>event is raised. The <i>Handles <\/i>clause after the method signature links the event-handling code with the actual event. <\/p>\n<p>A method can also dynamically link to an event using the <i>AddHandler <\/i>method and be removed from handling an event using the <i>RemoveHandler<\/i> method as seen below. This enables more flexibility by relating a method to an event and removing the binding required at runtime. Even when the <i>Handles <\/i>keyword is used to bind a method to an event internally, a call to the <i>AddHandler <\/i>method is done to perform the actual binding.<\/p>\n<p>&#8216;We dont require to use WithEvents since we are binding to events dynamically <br \/>Private mWorker As New Worker <\/p>\n<pre>'Binds a method(handler) to an event \nAddHandler mWorker.WorkDone, AddressOf work_WorkDone \n'Unbinds a method(handler) from an event \nRemoveHandler mWorker.WorkDone, AddressOf work_WorkDone \n<\/pre>\n<h2>Delegates in .NET events<\/h2>\n<p>VB.NET implements events through a construct called delegates. Delegates point to a method and can hold the address of a method, enabling a method to be invoked through an instance of its delegate. The <i>Worker <\/i>class can be implemented without events using a delegate as follows:<\/p>\n<pre>&#160;Public Class Worker \n&#160;\n&#160;&#160;&#160; Public Delegate Sub WorkDone(ByVal completedWork As Integer ) \n&#160;&#160;&#160; Public Handler As WorkDone \n&#160;&#160;&#160; Public Sub DoWork()&#160; \n&#160;&#160;&#160;&#160;&#160; For completedWork As Integer = 1 To 10 \n&#160;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Threading.Thread.Sleep(1000) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; If handler IsNot Nothing Then\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; handler(completedWork * 10) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; End If \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(completedWork) \n&#160;\n&#160;&#160;&#160;&#160;&#160; Next&#160; \n&#160;&#160;&#160; End Sub&#160; \n&#160; End Class&#160;<\/pre>\n<p>In the code above, a delegate named <i>WorkDone <\/i>that accepts an integer argument has been defined. This delegate can point to any method with a similar signature (a sub-procedure that accepts an integer). After the delegate is defined, a public variable delegate named <i>Handler <\/i>has been defined, and instead of raising the event in the <i>DoWork<\/i> method, the delegate instance is invoked. <\/p>\n<p>In the <i>Worker <\/i>class client, the delegate instance is set to point to the method that will be called when reporting the amount of work done.<\/p>\n<pre>Private mWorker As new Worker \n'Sets the delegate instance to the method implementation \nmWorker.Handler = AddressOf work_WorkDone <\/pre>\n<p>The above code uses delegates directly, not the event construct, so neither the <i>AddHandler<\/i> nor <i>RemoveHandler<\/i> methods can be used. This exposes only one public variable, so there can be only one method to handle the invocation of the delegate. <\/p>\n<p>Directly using the delegates method eliminates the setting of multiple event handlers for one event source, which would notify all handlers when the event occurs. But an event type can be set as a delegate and use the event-handling mechanism, resulting in cleaner code as follows:<\/p>\n<pre>&#160; Public Class Worker \n&#160;\n&#160;&#160;&#160; Public Delegate Sub WorkDone(ByVal completedWork As Integer ) \n&#160;&#160;&#160; Public Handler As WorkDone \n&#160;&#160;&#160; Public Sub DoWork()&#160; \n&#160;&#160;&#160;&#160;&#160; For completedWork As Integer = 1 To 10 \n&#160;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Threading.Thread.Sleep(1000) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; RaiseEvent WorkEvent(completedWork * 10) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(completedWork) \n&#160;\n&#160;&#160;&#160;&#160;&#160; Next&#160; \n&#160;&#160;&#160; End Sub&#160; \n&#160; End Class <\/pre>\n<h2>Custom events in VB.NET 8.0<\/h2>\n<p>Custom events provide flexibility and control in creating and managing events. When using custom events, it is the responsibility of the developer to bind and unbind handlers, as well as to specify the actual calls to raise the events. Although this requires a bit more code than the earlier implementations, it provides more control over the handlers and the invocation.<\/p>\n<p>A custom event is defined by using the Custom keyword in front of the event declaration, and by providing the delegate type for the event. The custom event has three sections: The <i>AddHandler<\/i> section, which is invoked whenever a handler is added using the Handles keyword or the <i>AddHandler<\/i> method; <i>RemoveHandler<\/i> section, which is called whenever a handler is removed using the <i>RemoveHandler <\/i>method; and the <i>RaiseEvent <\/i>section that will be invoked whenever an event is requested. The developer should write the necessary code in each of these sections to manage the handlers, as well as specify how handlers will be notified when an event is requested.<\/p>\n<p>Below, the Worker class is implemented using the custom event construct in VB.NET 2005. <\/p>\n<pre>&#160; Public Class Worker \n&#160;\n&#160;&#160;&#160; Public Delegate Sub WorkDone(ByVal completedWork As Integer ) \n&#160;&#160;&#160; Private handlers As New ArrayList() \n Public Custom Event WorkCompleted As WorkDone \n&#160;\n&#160;&#160;&#160;&#160;&#160; AddHandler (ByVal value As WorkDone)\n&#160;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; If handlers.Count &lt;= 5 Then \n&#160;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; handlers.Add(value)&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; End If&#160; \n&#160;&#160;&#160;&#160;&#160; End AddHandler \n&#160;&#160;&#160;&#160;&#160; RemoveHandler(ByVal value As WorkDone) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; handlers.Remove(value)&#160; \n&#160;&#160;&#160;&#160;&#160; End RemoveHandler \n&#160;&#160;&#160;&#160;&#160; RaiseEvent (ByVal completedWork &gt;As Integer) \n&#160;\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; If completedWork &gt; 50 Then&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; For Each handler As WorkDone In handlers&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; handler.Invoke(completedWork)&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Next&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; End If&#160; \n&#160;&#160;&#160;&#160;&#160; End RaiseEvent&#160; \n&#160;&#160;&#160; End Event \n\n\n\n Public Sub DoWork() \n&#160;\n&#160;&#160;&#160;&#160;&#160; For completedWork As Integer = 1 To 10&#160; \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Threading.Thread.Sleep(1000) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; RaiseEvent WorkCompleted(completedWork * 10) \n&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(completedWork) \n&#160;\n&#160;&#160;&#160;&#160;&#160; Next&#160; \n&#160;&#160;&#160; End Sub&#160; \n&#160; End Class <\/pre>\n<p>Using custom events enables more control over the handlers. In the above code, for example, there is a condition in the AddHandler section limiting the number of handlers for which events are provided. The RaiseEvent method has been changed to notify the handlers only when the completed Work is more than 50. Another advantage to using custom events is a central unit of code containing all of the handlers of the event within the event construct.<\/p>\n<p>Using RaiseEvent twice, once in the DoWork method and then in the custom event construct, can lead to confusion. RaiseEvent in the DoWork method will raise the event, but the RaiseEvent section in the custom event construct specifies the code to be executed when the event is raised. In this case, all of the handlers are looped through and delegates are invoked.<\/p>\n<p>Events and delegates are important parts of the .NET language. VB.NET 2005 gives developers control and flexibility in handling simple event models and custom events.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Visual Basic .NET is well known for its event-driven programming capabilities. VB.NET 2005 adds new functionality for custom events that provides flexibility in handling and controlling events.&hellip;<\/p>\n","protected":false},"author":124595,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4229,4178,4330,4331,4179,4270,4194],"coauthors":[],"class_list":["post-107","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-net-framework","tag-bi","tag-custom","tag-events","tag-source-control","tag-vb-net","tag-visual-basic"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/124595"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=107"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107\/revisions"}],"predecessor-version":[{"id":42702,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107\/revisions\/42702"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=107"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=107"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=107"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}