How do I handle events with interfaces in VB.NET? - vb.net

I'm new to interfaces and I'm trying to understand how they work.
I wrote the following code, which works properly except the click event which is not firing.
Public Class Form1
Dim WithEvents Button As IClass = New MyButton
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Button.Parent = Me
End Sub
Public Sub ClickEventHandler(ByVal Sender As Object, ByVal E As EventArgs) Handles Button.Click
MsgBox("Piwpiw !")
End Sub
End Class
Public Interface IClass
Event Click(ByVal Sender As Object, ByVal E As EventArgs)
Property Parent
End Interface
Public Class MyButton
Inherits SimpleButton
Implements IClass
Public Event click1(ByVal Sender As Object, ByVal E As System.EventArgs) Implements IClass.click
Public Property Parent1 As Object Implements IClass.Parent
Get
Return MyBase.Parent
End Get
Set(ByVal value As Object)
MyBase.Parent = value
End Set
End Property
End Class
What's wrong with that code?
(PS: This is just an example allowing me to understand how interfaces work and doesn't have any functional meaning.)

You're missing a single method in the MyButton class to make this work.
You need this:
Private Sub MyButton_Click(sender As Object, e As System.EventArgs) Handles Me.Click
RaiseEvent click1(sender, e)
End Sub
Essentially SimpleButton already has a click method. It's being raised when you click your derived MyButton class. But the Click event on SimpleButton isn't the same event as Click on the IClass interface. You implemented that as click1. So you just need to raise the click1 method when the Click method is raised. Hence the above method.

In your sample code, I don't see a line where the event is raised. In order to be able to handle an event in the event handler, you'd have a spot in your MyButton class that raises the event, i.e.:
Public Class MyButton
Inherits SimpleButton
Implements IClass
Public Event click1(ByVal Sender As Object, ByVal E As System.EventArgs) Implements IClass.click
Public Property Parent1 As Object Implements IClass.Parent
Get
Return MyBase.Parent
End Get
Set(ByVal value As Object)
MyBase.Parent = value
End Set
End Property
Public Sub SimulateClick()
RaiseEvent click1(Me, New System.EventArgs())
End Sub
End Class
You use the RaiseEvent statement to raise an event.
Above sample, of course, assumes that at some point in your code, you call SimulateClick instead of having a real mouse click (which would involve painting the button and reacting to several mouse events). Once the event is raised, your handler will be called. You could do this from your Form1_Load method:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Button.Parent = Me
Button.SimulateClick()
End Sub
I have noticed that your MyButton class derives from a SimpleButton class. Instead of having the SimulateClick-method, you could also react to a click in the SimpleButton class and place the RaiseEvent statement there.

Here is the solution:
Replace MyButton class with:
Public Class MyButton
Inherits SimpleButton
Implements IClass
Public Shadows Event click(ByVal Sender As Object, ByVal E As System.EventArgs) Implements IClass.Click
Public Property Parent1 As Object Implements IClass.Parent
Get
Return MyBase.Parent
End Get
Set(ByVal value As Object)
MyBase.Parent = value
End Set
End Property
Public Sub ClickEventHandler(ByVal Sender As Object, ByVal E As EventArgs) Handles MyBase.Click
RaiseEvent click(Sender, E)
End Sub
End Class

Related

Event from other class

I have to "catch" an event from another class but I can't.
Project contains two forms: Form1 and Form2 and class params. Form 1 contain one button which have to change property value in class props. And Form2 have to know when props raises event.
Here is situation:
''Form1 code
Public Class Form1
Dim p As New params
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Form2.Show()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
p.demo = "mytry"
End Sub
End Class
''Form2 code
Public Class Form2
Private WithEvents p As params
Private Sub pChanged(ByVal propertyName As String) Handles p.PropChanged
MessageBox.Show(propertyName)
End Sub
End Class
''Class params code
Public Class params
Public Event PropChanged(ByVal propName As String)
Private _demo As String
Public Property demo() As String
Get
Return _demo
End Get
Set(ByVal value As String)
_demo = value
RaiseEvent PropChanged("demo")
End Set
End Property
End Class
Problem is that sub pChanged in Form2 is never fired and have to be fired when property "demo" raises an event PropChanged.
How to get this working?
You have two params variables and you are handling the wrong in Form2 if you change the property in Form1. You could handle the event in Form2 and raise it again to handle it in Form1.
One here:
Public Class Form1
Dim p As New params
and one here
Public Class Form2
Private WithEvents p As params
Note that you have to use WithEvents p As New params also in Form1 if you want to handle the event there.

Passing control from one class to an event handler from another class

I have a TextBox1 in Form1. I need to pass it to another class (in class libraries/another project). So, that instance of class can modify what inside TextBox1 within the entire class (not just a scope). For my problem i need to pass it to an event handler.
Public Class TheClass
WithEvents Timer1 As New Timer
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
End Sub
End Class
I can think of passing by reference. But, I can't find a way to pass TextBox1 to that event handler.
What should I do to make the Timer1 have access to modify TextBox1 in Form1?
If TheClass is created after Form1, you can use constructor injection in order to pass a reference of Form1 to TheClass.
Public Class TheClass
Private WithEvents Timer1 As New Timer
Private m_form1 As Form1
Public Sub New (ByVal form1 as Form1)
m_form1 = form1
End Sub
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
m_form1.TextBox1.Text = "tick"
End Sub
End Class
Note that TextBox1 must be Public or Friend.
Another way is to add a public event to TheClass.
Public Class TheClass
Public Event Tick()
Private WithEvents Timer1 As New Timer
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
RaiseEvent Tick()
End Sub
End Class
Now the form can handle this event.
' In Form1
Private WithEvents theObj As New TheClass
Private Sub theObj_Tick() _
Handles theObj.Tick
Me.Textbox1.Text = "tick"
End Sub
Now you can keep Textbox1 private and TheClass does not need to know anything about a textbox.
The event handler can also have parameters
' In TheClass
Public Event Tick(ByVal counter As Integer)
...
Counter += 1
RaiseEvent Tick(Counter)
and
' In Form1
Private Sub theObj_Tick(ByVal counter As Integer) _
Handles theObj.Tick
Me.Textbox1.Text = "counter = " & counter
End Sub

VB.NET UserControl does not receive parent form's events

I have created a usercontrol and added it to a form.
I would like to receive the form's event using Private WithEvents _Parent As Form.
But none of the events is being received.
The entire code of my usercontrol is attached.
Does anybody see what I am doing wrong?
Public Class UserControl1
Private WithEvents _Parent As Form
Public Sub New()
InitializeComponent()
_Parent = Me.Parent
End Sub
Private Sub _Parent_Activated(sender As Object, e As EventArgs) Handles _Parent.Activated
MsgBox("activated")
End Sub
Private Sub _Parent_Resize(sender As Object, e As EventArgs) Handles _Parent.Resize
MsgBox("resize")
End Sub
End Class
When the constructor is called, there is no parent yet (Windows Forms controls are added to their parents after the class is created). Me.Parent returns Nothing at this point.
Handle Me.ParentChanged to initialize _Parent:
Private Sub UserControl1_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ParentChanged
_Parent = Me.Parent
End Sub

WithEvents+Handles+Overrides

Say I have an member (e.g., Button1) of an object (e.g., Form1) that is delared using withevents (e.g., Form1.Button1_Click), and there is a handler with 'Handles' in that object.
If I override it (say, Form2.Button1_Click), will the handler call the overriden version (like me.Button1_Click) or the one with the actual handles on it (like MyClass.Button1_Click)?
Here's what I tried:
Public Class Form1
Public Overridable Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
MsgBox("Form1's Button")
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim f2 As New Form2
f2.Show()
End Sub
End Class
Public Class Form2
Inherits Form1
Public Overrides Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
MsgBox("Form2's Button")
End Sub
End Class
Specifying the Overridable modifier indicates that the method can be overridden.
To override is to reject or cancel (a decision, view, etc.).
Overrides specifies that the method will override the existing event handlers implementation. The existing method in Form1 will never be called unless you manually call it. You can manually call it by using MyBase keyword which essentially allows you to reference the base class of the current instance.
Public Overrides Sub Button1_Click(sender As Object, e As EventArgs)
MessageBox.Show("SecondForm's Button")
MyBase.Button1_Click(sender, e)
End Sub
The overridden version is called. When I click button1 on Form1, I get 'Form1's Button'. When I start the second form using button2, I click button1, and get 'form2's button'
Just so anybody tries to google this and finds nothing like I did, I killed 10 minutes of my life to test it, and now nobody else will need to!

Casting interfaces and MEF

I have the following problem with MEF:
Interface definition to be used by host:
Public Interface IExecuteDoSomething
Inherits IAddinSettings
Event DataReceived As EventHandler(Of DataReceivedEventArgs)
Function DoSomething() As Boolean
End Interface
Public Class DataReceivedEventArgs
Inherits EventArgs
Public Sub New(ByVal message As String)
Me.Message = message
End Sub
Public Message As String
End Class
extra interface needed by some other code inside the host:
Public Interface IAddinSettings
ReadOnly Property Setting() As AddinSettings
End Interface
Public Class AddinSettings
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
End Class
The class that provides the export:
<Export(GetType(SharedLibrary.IExecuteDoSomething))> Public Class Class1
Implements SharedLibrary.IExecuteDoSomething
Implements SharedLibrary.IAddinSettings
Private _Addinsettings As New SharedLibrary.Addinsettings("Test")
Public Function DoSomething() As Boolean Implements SharedLibrary.IExecuteDoSomething.DoSomething
MsgBox("i did something")
Return True
End Function
Public Event DataReceived(ByVal sender As Object, ByVal e As SharedLibrary.DataReceivedEventArgs) Implements SharedLibrary.IExecuteDoSomething.DataReceived
Public ReadOnly Property Setting() As SharedLibrary.AddinSettings Implements SharedLibrary.IAddinSettings.Setting
Get
Return _Addinsettings
End Get
End Property
End Class
The host:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim catalog As New Hosting.AggregateCatalog
Dim d As New Hosting.DirectoryCatalog("..path to dlll..")
catalog.Catalogs.Add(d)
Dim container = New Hosting.CompositionContainer(catalog)
Dim batch As New Hosting.CompositionBatch
batch.AddPart(Me)
container.Compose(batch)
For Each dd In dos
AddHandler dd.DataReceived, AddressOf testevent
Next
End Sub
<Import()> Public dos As IEnumerable(Of SharedLibrary.IExecuteDoSomething)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each d In dos
d.DoSomething()
Next
End Sub
Private Sub testevent(ByVal sender As Object, ByVal e As SharedLibrary.DataReceivedEventArgs)
MsgBox("Event received: " & e.Message)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dosomethingelse(DirectCast(dos, System.Collections.Generic.List(Of SharedLibrary.IAddinSettings)))
End Sub
Private Sub Dosomethingelse(byval settings as IEnumerable(Of SharedLibrary.IAddinSettings))
End Sub
End Class
Everything seems to work fine until the Button2_Click routine is executed, then an InvalidCastException is thrown with the info:
Unable to cast object of type 'System.Collections.Generic.List1[SharedLibrary.IExecuteDoSomething]' to type 'System.Collections.Generic.List1[SharedLibrary.IAddinSettings]'.
How can i solve this problem, because the imported object implements both of the interfaces?
I suspect you're actually running into a covariance issue - that's the typical cause of problems like this. A List<IFoo> is not a List<IBar> even if IBar extends IFoo.
If you're using .NET 3.5, the easiest way to get round this in your case is to remove the DirectCast and instead use Enumerable.Cast:
Dosomethingelse(dos.Cast(Of SharedLibrary.IAddinSettings))