I'm going to be working through the book and making the fixes. I got the code and text for the book last night and the first thing I looked at and fixed was the " Exception Details: System.ArgumentException: A ConsumerConnectionPoint has already been added with ID 'default'.
Parameter name: connectionPoints" exception. This means that the declaration of the three callback methods in ConsumerPart.
vb lacked a unique ID. The overload used in the book left the default name in place by only passing in a string for the displayName eg:
<WebControls.WebParts.ConnectionConsumer("IBookInf o Consumer")> _
Public Sub IBookInfoConsumer(ByVal bk As IBookInfo)
In the corrected code below we've used the overload that assigns an ID, too:
<ConnectionConsumer("IBookInfo Consumer", "MyUniqueIBookInfoConsumerName")>....
Below is the ConsumerConnectionPointCollection constructor and we can see on line 16 the Contains() test being run to see if there is a duplicate key (representing as ConsumerConnectionPoint) already in the HybridDictionary. If it returns true then code throws the exception that we finally see (line 17). Again, the code below is framework code and isn't something we'd need to deal with, it's just here to illustrate the "why" behind the exception. I make a habit of looking several layers below the root cause of an error or exception just so I can understand what consequences each line of code I write has within the framework:
1. Public Sub New(ByVal connectionPoints As ICollection)
2. If (connectionPoints Is Nothing) Then
3 Throw New ArgumentNullException("connectionPoints")
4 End If
5 Me._ids = New HybridDictionary(connectionPoints.Count, True)
6 Dim V_0 As Object
7 For Each V_0 In connectionPoints
8 If (V_0 Is Nothing) Then
9 Throw New ArgumentException(SR.GetString("Collection_CantAdd Null"), "connectionPoints")
0 End If
11 Dim V_1 As ConsumerConnectionPoint = TryCast(V_0,ConsumerConnectionPoint)
12 If (V_1 Is Nothing) Then
13 Throw New ArgumentException(SR.GetString("Collection_Invalid Type", New Object() { "ConsumerConnectionPoint" }), "connectionPoints")
14 End If
15 Dim V_2 As String = V_1.ID
16 If Me._ids.Contains(V_2) Then
17 Throw New ArgumentException(SR.GetString("WebPart_Collection _DuplicateID", New Object() { "ConsumerConnectionPoint", V_2 }), "connectionPoints")
18 End If
19 MyBase.InnerList.Add(V_1)
20 Me._ids.Add(V_2, V_1)
21 Next
22 End Sub
Here is the code for the ConsumerPart.
vb class that correct this particular problem. It is not in it's final form, I still haven't gone through it in detail:
Imports System.ComponentModel
Imports System.Web.UI
<ToolboxData("<{0}:ConsumerPart runat=server></{0}:ConsumerPart>")> Public Class ConsumerPart
Inherits System.Web.UI.WebControls.WebParts.WebPart
Private ibk As IBookInfo
Dim typ As Object
Dim strTitle As String
Dim pcSchema As PropertyDescriptorCollection
'This method should have used the constuctor that accepts a string to set the ID like this:
<WebControls.WebParts.ConnectionConsumer("IBookInf o Consumer", "MyUniqueIBookInfoConsumerName")> _
Public Sub IBookInfoConsumer(ByVal bk As IBookInfo)
Dim strProviderValue As String
Dim bolSuccess As Boolean
ibk = bk
strProviderValue = "10345329"
bk.ISBN = strProviderValue
bk.UpdateCache(True)
bolSuccess = bk.SortData("Desc")
End Sub
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
If ibk Is Nothing Then
writer.Write("<b>No book information available.</b>")
Else
writer.Write("ISBN: <b>" & ibk.ISBN & "</b>")
End If
End Sub
Private Sub ConsumerPart_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
Me.Title = "Consumer"
End Sub
Dim ifld As WebControls.WebParts.IWebPartField
'This method should have used the constuctor that accepts a string to set the ID like this:
<WebControls.WebParts.ConnectionConsumer("IWebPart Field Consumer", "MyUniqueIWebPartFieldConsumerName")> _
Public Sub IWebPartFieldConsumer(ByVal fld As WebControls.WebParts.IWebPartField)
ifld = fld
ifld.GetFieldValue(AddressOf ReceiveData)
End Sub
Sub ReceiveData(ByVal BookTitle As Object)
Dim pd As PropertyDescriptor
pd = ifld.Schema
If pd.PropertyType.Name = "String" Then
strTitle = BookTitle.ToString
End If
End Sub
'This method should have used the constuctor that accepts a string to set the ID like this:
<WebControls.WebParts.ConnectionConsumer("IWebPart Parameters Consumer", "MyUniqueIWebPartParametersConsumerName")> _
Public Sub IWebPartParametersConsumer( _
ByVal prm As WebControls.WebParts.IWebPartParameters)
Dim pdc As PropertyDescriptorCollection
Dim iprm As WebControls.WebParts.IWebPartParameters
iprm = prm
pdc = TypeDescriptor.GetProperties(Me)
iprm.SetConsumerSchema(pdc)
If iprm.Schema.Count = pdc.Count Then
iprm.GetParametersData(AddressOf ReceiveData)
End If
End Sub
Sub ReceiveData(ByVal BookInfo As IDictionary)
Dim entry As DictionaryEntry
For Each entry In BookInfo.Keys
Select Case entry.Key.ToString
Case "BookTitle"
Me.BookTitle = entry.Value.ToString
Case "BookAuthor"
Me.BookAuthor = entry.Value.ToString
End Select
Next
End Sub
Property BookTitle() As String
Get
Return "Custom Controls and Web Parts"
End Get
Set(ByVal value As String)
End Set
End Property
Property BookAuthor() As String
Get
Return "Peter Vogel"
End Get
Set(ByVal value As String)
End Set
End Property
End Class