Managing Event Subscriptions with the WithEvents Keyword in VB.NET
Learn how to use the WithEvents keyword in VB.NET to simplify event handling, avoid manual AddHandler calls, and prevent common pitfalls like duplicate event invocations.
23 Jun 2026, 18:26 UTC

The Problem: Manual Event Wiring Overhead
In standard .NET development, connecting an object's event to a method requires an explicit AddHandler statement. While functional, this creates boilerplate code and separates the event declaration from the logic that handles it, making the codebase harder to audit as the number of UI components or background workers grows.
The WithEvents keyword solves this by enabling declarative event handling. Instead of manually wiring the event at runtime, you declare the variable with WithEvents and use the Handles clause on the method. The VB.NET compiler then automatically generates the necessary AddHandler and RemoveHandler calls behind the scenes whenever the variable is assigned a value.
Implementing Declarative Event Handling
To use WithEvents, the variable must be declared at the class level (member variable). It cannot be used for local variables within a method because the Handles clause requires a persistent scope to manage the subscription lifecycle.
Worked Example: Dynamic Button Handling
In this scenario, we instantiate a button programmatically. Rather than calling AddHandler in the constructor, we use WithEvents to bind the click event.
Public Class EventDemoForm
Inherits Form
' Declare the variable with WithEvents at the class level
Private WithEvents btnSubmit As Button
Public Sub New()
InitializeComponent()
' Initialize the object
btnSubmit = New Button()
btnSubmit.Text = "Click Me"
btnSubmit.Location = New Point(50, 50)
' Add the button to the form
Me.Controls.Add(btnSubmit)
End Sub
' The Handles clause tells the compiler to wire this method
' to the btnSubmit.Click event automatically
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
MessageBox.Show("Button was clicked via WithEvents!")
End Sub
End Class
How it Works Internally
When you assign an object to a WithEvents variable, the compiler injects code into the property setter or assignment operation. If the variable previously held an object, the compiler automatically calls RemoveHandler for the old object before calling AddHandler for the new one. This prevents memory leaks caused by dangling event subscriptions.
Technical Limitations
While convenient, WithEvents has specific constraints that can lead to compile-time or runtime errors if ignored:
- Scope Restrictions: It cannot be applied to
Shared(static) variables. Event handlers must be associated with a specific instance of a class. - Type Constraints: It only works on variables referencing classes that expose events. You cannot use
WithEventson arrays or generic lists (e.g.,List(Of Button)), as the keyword applies to the reference variable, not the elements within a collection. - Interface Limitations: If a variable is declared as an interface type,
WithEventswill only work if the interface itself defines the event.
Common Engineering Pitfalls
The Duplicate Handler Trap
A common mistake is mixing declarative and manual wiring. If you declare a variable WithEvents and also call AddHandler btnSubmit.Click, AddressOf btnSubmit_Click, the event will fire twice for every single click. Always choose one strategy per event.
The NullReferenceException
Because WithEvents handles the wiring but not the instantiation, the variable is Nothing until you explicitly create the object. If an event is raised by an external process before the variable is initialized, or if the handler logic assumes the object exists without a null check, the application will crash.
Removal Complexity
You cannot use RemoveHandler to detach a method that was linked via the Handles clause. To stop listening to events for a WithEvents variable, you must set the variable to Nothing:
' This removes all declarative handlers associated with this variable
btnSubmit = Nothing
Verification and Diagnostics
To verify that your event is wired correctly and not duplicated, follow these steps in Visual Studio:
- Place a breakpoint inside the method containing the
Handlesclause. - Run the application in Debug mode and trigger the event.
- Check the Call Stack window. You should see the event being raised by the .NET Framework.
- If the breakpoint is hit twice for a single action, search your codebase for a redundant
AddHandlercall. - To verify initialization, use the Immediate Window during a breakpoint and type
? btnSubmit IsNot Nothing. If this returnsFalse, the event will not fire.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.