Quote Originally Posted by rjv_rnjn
@jmcilhinney, does that mean we can define a event handler accepting three arguments?
As I said, the signature of the event handler must match the signature of the event. If you've declared the event yourself then you can make the signature anything you want.

Having said that, I very, VERY strongly suggest that you follow the convention and declare EVERY event with two arguments only: the first ALWAYS an Object named "sender" and the second ALWAYS an EventArgs (or a type that inherits EventArgs) named "e". If you want to pass data to the event handler then you should be deriving, either directly or indirectly, your own type from EventArgs and passing an instance of that to the second argument. The following is VB code but gives you the idea of what I'm talking about. Here's my custom EventArgs type:
VB Code:
  1. ''' <summary>
  2. ''' Provides data for an event raised to indicate that a text value is changing.
  3. ''' </summary>
  4. ''' <remarks>
  5. ''' Provides an opportunity to cancel the change if the new value is not appropriate.
  6. ''' </remarks>
  7. Public Class StringChangeCancelEventArgs
  8.     Inherits System.ComponentModel.CancelEventArgs
  9.  
  10. #Region " Variables "
  11.  
  12.     ''' <summary>
  13.     ''' The current value of the text.
  14.     ''' </summary>
  15.     Private _currentValue As String
  16.     ''' <summary>
  17.     ''' The new value of the text.
  18.     ''' </summary>
  19.     Private _proposedValue As String
  20.  
  21. #End Region 'Variables
  22.  
  23. #Region " Properties "
  24.  
  25.     ''' <summary>
  26.     ''' Gets the current value of the text.
  27.     ''' </summary>
  28.     ''' <value>
  29.     ''' A <b>String</b> containing the current value of the text.
  30.     ''' </value>
  31.     Public ReadOnly Property CurrentValue() As String
  32.         Get
  33.             Return _currentValue
  34.         End Get
  35.     End Property
  36.  
  37.     ''' <summary>
  38.     ''' Gets the new value of the text.
  39.     ''' </summary>
  40.     ''' <value>
  41.     ''' A <b>String</b> containing the new value of the text.
  42.     ''' </value>
  43.     Public ReadOnly Property ProposedValue() As String
  44.         Get
  45.             Return _proposedValue
  46.         End Get
  47.     End Property
  48.  
  49. #End Region 'Properties
  50.  
  51. #Region " Constructors "
  52.  
  53.     ''' <summary>
  54.     ''' Initialises a new instance of the <see cref="StringChangeCancelEventArgs"/> class.
  55.     ''' </summary>
  56.     ''' <param name="proposedValue">
  57.     ''' The value the text will have after the change.
  58.     ''' </param>
  59.     ''' <param name="currentValue">
  60.     ''' The current value of the text.
  61.     ''' </param>
  62.     Public Sub New(ByVal proposedValue As String, ByVal currentValue As String)
  63.         Me._proposedValue = proposedValue
  64.         Me._currentValue = currentValue
  65.     End Sub
  66.  
  67. #End Region 'Constructors
  68.  
  69. End Class
and here's my event that uses it:
VB Code:
  1. ''' <summary>
  2.     ''' Occurs when the <see cref="Name"/> property value is changing.
  3.     ''' </summary>
  4.     ''' <remarks>
  5.     ''' Provides an opportunity to cancel a name change if the proposed name is already in use.
  6.     ''' </remarks>
  7.     Public Event NameChanging As EventHandler(Of StringChangeCancelEventArgs)
Here's the method that raises the event:
VB Code:
  1. ''' <summary>
  2.     ''' Raises the <see cref="NameChanging"/> event.
  3.     ''' </summary>
  4.     ''' <param name="e">
  5.     ''' A <see cref="StringChangeCancelEventArgs"/> object containing the data for the event.
  6.     ''' </param>
  7.     Protected Overridable Sub OnNameChanging(ByVal e As StringChangeCancelEventArgs)
  8.         RaiseEvent NameChanging(Me, e)
  9.     End Sub
and here's the property that calls that method:
VB Code:
  1. ''' <summary>
  2.     ''' Gets or sets the name of the button layout.
  3.     ''' </summary>
  4.     ''' <value>
  5.     ''' A <b>String</b> containing the name of the button layout.
  6.     ''' </value>
  7.     Public Property Name() As String
  8.         Get
  9.             Return Me._name
  10.         End Get
  11.         Set(ByVal value As String)
  12.             If Me._name <> value Then
  13.                 Dim e As New StringChangeCancelEventArgs(value, Me._name)
  14.  
  15.                 Me.OnNameChanging(e)
  16.  
  17.                 'Update the value if and only if the new value is different and the change is not cancelled.
  18.                 If Not e.Cancel Then
  19.                     Me._name = value
  20.                     Me.OnNameChanged(EventArgs.Empty)
  21.                 End If
  22.             End If
  23.         End Set
  24.     End Property
Now when the NameChanging event is raised the event handlers receive a StringChangeCancelEventArgs object. Here's how I use that object:
VB Code:
  1. ''' <summary>
  2.     ''' Handles the <see cref="ButtonLayout.NameChanging">NameChanging</see> event for all items in the collection.
  3.     ''' </summary>
  4.     Private Sub ButtonLayout_NameChanging(ByVal sender As Object, ByVal e As StringChangeCancelEventArgs)
  5.         Dim item As ButtonLayout = TryCast(sender, ButtonLayout)
  6.  
  7.         If item IsNot Nothing Then
  8.             Dim name As String = e.ProposedValue
  9.  
  10.             'Cancel the change if an existing item already has the proposed name.
  11.             If Me.IndexOfName(name) <> -1 Then
  12.                 e.Cancel = True
  13.                 Me.OnDuplicateNameRejected(New DuplicateNameRejectedEventArgs(name))
  14.             End If
  15.         End If
  16.     End Sub