Windows Develop Bookmark and Share   
 index > Windows Forms Designer > Problems serializing a collection out of Windows form Collection Editor
 

Problems serializing a collection out of Windows form Collection Editor

Hello All,

A. I have a class named Plural2SingularItem

B. I have a class named Plural2SingularItemCollection which inherits CollectionBase.

C. In a custom control, I have a property of Type Plural2SingularItemCollection

I set everything up the way I did so that I would be able to use the default ObjectEditor in design view. Everything works as I would expect EXCEPT that nothing is Persisted when I click the OK button in the Editor.

I can click Add and it will correctly add an empty Plural2SingularItem in the right pane. I can change its properties, etc. I can add multiple items and they correctly show in the right pane and also correctly display as line items in the left pane. I can use the Up and Down arrows to move them around and Click the Remove button to remove one or more of them.

The only thing it doesn't do is persist them when I click OK. I have the Serializable attribute set on Plural2SingularItemCollection.

Because everything else seems to be working properly, I am wondering if I am missing an attribute or something simple like that somewhere. I have a stripped down version if someone wants to look at the code. Thanks in advance for any ideas and/or suggestions.

j2associates  Monday, January 30, 2006 3:45 PM

I don't have the time to write asnippet at the moment, but basicly, your problem is that your TypeConverter does not give instructions on creating the InstanceDescriptor.

The CanConvertTo() method correctly allows conversion to InstanceDescriptor, but the ConvertTo() method does not include code to do it.

This article may help:
http://www.divil.co.uk/net/articles/designers/collectioncontrols.asp

Mick Doherty  Wednesday, February 08, 2006 7:16 PM
Sounds like you are missing the [DesignerSerializationVisibiltity]Attribute on your property which should be set to Content.
Mick Doherty  Thursday, February 02, 2006 10:37 AM

Hello Mick,

Thanks for your reply. I have tried that and it does not alter what I have described above. I have a stripped down version which shows the behavior if you would have any interest in seeing it. I have been pulling my hair out on this one. Everything is find except that it won't serialize.

j2associates  Thursday, February 02, 2006 6:44 PM

Maybe your class must implements a DictionaryBase instead of CollectionBase.

See the following code:

/// <summary>
/// dictionarybase implementation
/// </summary>
[Serializable]
public class QDictionary : DictionaryBase
{

/// <summary>
/// Returns an string with the key value
/// </summary>
/// <param name="key">The key to use</param>
public String this[String key]
{
get { return ((String) Dictionary[key]); }
set { Dictionary[key] = value; }
}

/// <summary>
///  A collection with the keys
/// </summary>
public ICollection Keys
{
get { return (Dictionary.Keys); }
}

/// <summary>
/// A collection with the values
/// </summary>
public ICollection Values
{
get { return (Dictionary.Values); }
}

/// <summary>
/// Add a item to the collection. Here maybe you need change the  type for your class
/// </summary>
/// <param name="key">The key of the item to add</param>
/// <param name="value">The object or item to add</param>
public void Add(String key, IComponent value)
{
Dictionary.Add(key, value);
}

/// <summary>
/// Add an object into a collection
/// </summary>
/// <param name="key">the object's key to add</param>
/// <param name="value">the object to add</param>
public void Add(String key, object value)
{
Dictionary.Add(key, value);
}

/// <summary>
/// Search a key in the collection
/// </summary>
/// <param name="key">the item's key to search</param>
/// <returns>The item's position</returns>
public bool Contains(String key)
{
return (Dictionary.Contains(key));
}

/// <summary>
/// Remove an object of the collection
/// </summary>
/// <param name="key">The item's key to remove</param>
public void Remove(String key)
{
Dictionary.Remove(key);
}

}

Your items must be placed into this class. Remebers that a DictionaryBase requires to use a DictionaryEntry to access the item.

Regards

César

Tabas  Friday, February 03, 2006 4:02 PM

Does yourPlural2SingularItem class inherit from Component? This should make things simpler, but if not then you may have to define a TypeConverter for it.

If the code is short enough then I would be willing to look at it, but I don't really have too much time at the moment, so can't wade through lots of irrelevant code, or promise to answer quickly.

Mick Doherty  Saturday, February 04, 2006 1:11 AM

Thanks to both of you for your suggestions. I found some information at this link: http://blogs.msdn.com/toub/archive/2004/10/12/241277.aspxwhich had a fully qualified EditorAttribute. When I changed to the string attribute usage, items started to show up again when I clicked the ellipses although they are still not persisting.

Here is my info class with the Collection property. The info class is marked Serializable. I know it is calling the ItemCollection class because I had to add an empty constructor. All the type converter Plural2SingularInfoTypeConverter does is make it a node view so it collapses into the + sign.

[code]
<Serializable(), _
TypeConverter(GetType(Plural2SingularInfoTypeConverter))> _
Public Class Plural2SingularInfo
[/code]

[code]
Private m_Plural2SingularItemCollection As Plural2SingularItemCollection = _
New Plural2SingularItemCollection
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
Description("Information pertaining to specific Plural2Singular items (eg Crises to Crisis)")> _
Public Property Plural2SingularItems() As Plural2SingularItemCollection
Get
Return m_Plural2SingularItemCollection
End Get
Set(ByVal Value As Plural2SingularItemCollection)
m_Plural2SingularItemCollection = Value
End Set
End Property 'Plural2SingularItems
[/code]

Here is the Plural2SingularItem class along with 2 of the properties. There are 4 total but they are parallel, one pair for the Plural and one pair for the Singular.

[code]
<Serializable(), _
TypeConverter(GetType(Plural2SingularItemTypeConverter))> _
Public Class Plural2SingularItem
[/code]

[code]
Private m_SingularCaseSensitive As Boolean = False
<DefaultValue(False), NotifyParentPropertyAttribute(True), _
Description("Is Singular value case sensitive")> _
Public Property SingularCaseSensitive() As Boolean
Get
Return m_SingularCaseSensitive
End Get
Set(ByVal Value As Boolean)
If Value = Nothing Then Value = False
If Not Value.Equals(m_SingularCaseSensitive) Then
m_SingularCaseSensitive = Value
End If
End Set
End Property 'SingularCaseSensitive

Private Const m_SINGULAR_DEFAULT As String = ""
Private m_Singular As String = GetDefaultSingular()
<DefaultValue(m_SINGULAR_DEFAULT), NotifyParentPropertyAttribute(True), _
Description("Singular form which matches corresponding Plural item (eg Crisis and Crises)")> _
Public Property Singular() As String
Get
Return m_Singular
End Get
Set(ByVal Value As String)
If Value = Nothing Then Value = String.Empty
If Not Value.Equals(m_Singular) Then
m_Singular = Value
End If
End Set
End Property 'Singular

Private Function GetDefaultSingular() As String
Return m_SINGULAR_DEFAULT
End Function 'GetDefaultSingular

Private Sub ResetSingular()
Me.Singular = GetDefaultSingular()
End Sub 'ResetSingular
[/code]

Here is the Plural2SingularItemTypeConverter class.

[code]
<System.Security.Permissions.PermissionSetAttribute( _
System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Friend Class Plural2SingularItemTypeConverter
Inherits TypeConverter

Public Overloads Overrides Function CanConvertFrom( _
ByVal context As ITypeDescriptorContext, _
ByVal sourceType As Type) As Boolean
If sourceType Is GetType(System.String) Then Return True
Return MyBase.CanConvertFrom(context, sourceType)
End Function 'CanConvertFrom

Public Overloads Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, _
ByVal culture As CultureInfo, ByVal value As Object) As Object
If TypeOf value Is String Then
Dim temp As String() = value.ToString.Split(","c)
Return New Plural2SingularItem(CBool(temp(0)), temp(1), CBool(temp(2)), temp(3))
End If
Return MyBase.ConvertFrom(context, culture, value)
End Function 'ConvertFrom

Public Overloads Function CanConvertTo(ByVal context As ITypeDescriptorContext, _
ByVal destinationType As Type) As Boolean
If destinationType Is GetType(InstanceDescriptor) Then Return True
If destinationType Is GetType(System.String) Then Return True
Return MyBase.CanConvertTo(destinationType)
End Function 'CanConvertTo

Public Overloads Overrides Function ConvertTo( _
ByVal context As System.ComponentModel.ITypeDescriptorContext, _
ByVal culture As System.Globalization.CultureInfo, _
ByVal value As Object, _
ByVal destinationType As System.Type) As Object
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function 'ConvertTo
[/code]

And lastly, here is the Plural2SingularItemCollection class. When I used fully qualified type strings for the Editor, things started coming up correctly multiple times into the editor in design view, although it is still not persisting at all. I know this is the one being used because it shows (Collection) next to the ellipses in Design Mode.

[code]
<Serializable(), _
Editor("System.Windows.Forms.Design.CollectionEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")> _
Public Class Plural2SingularItemCollection
Inherits CollectionBase

Public Sub New()
End Sub 'New

Public Sub New(ByVal list() As Plural2SingularItem)
For Each item As Plural2SingularItem In list
Me.InnerList.Add(item)
Next
End Sub 'New

Public Overloads Overrides Function ToString() As String
Return "(Collection)"
End Function 'ToString

Default Public Property Item(ByVal index As Integer) As Plural2SingularItem
Get
Return CType(Me.InnerList(index), Plural2SingularItem)
End Get
Set(ByVal Value As Plural2SingularItem)
Me.InnerList(index) = Value
End Set
End Property 'Item

Public Function Add(ByVal value As Plural2SingularItem) As Integer
Return Me.InnerList.Add(value)
End Function 'Add
[/code]

Thanks again for all of your help, I really appreciate it!

j2associates  Tuesday, February 07, 2006 2:40 AM

I think I ammaking some progress, albeit painfully and slowly. I think the problem may be in the type converter for the Plural2SingularItem class. When I did some design time debugging, the TypeConverer methods for the above class are firing. When I saved the form and then tried to reedit it, I got the following Error Message:

C:\GaryWiney\TestProject\SiteAnalysis_Test.resx Resource transformation for file 'SiteAnalysis_Test.resx' failed. Could not load type j2associates.Tools.j2aChainedComboBoxes.Plural2SingularItem from assembly j2aChainedComboBoxes, Version=1.0.2229.32160, Culture=neutral, PublicKeyToken=null.

When I opened up the Form's resx file, I found 4 empty entries which correspond, at least in number to the 4 entries I added to the object browser. They are as follow:

<data name="resource" type="j2associates.Tools.j2aChainedComboBoxes.Plural2SingularItem, j2aChainedComboBoxes, Version=1.0.2229.32160, Culture=neutral, PublicKeyToken=null">
<value />
</data>
<data name="resource1" type="j2associates.Tools.j2aChainedComboBoxes.Plural2SingularItem, j2aChainedComboBoxes, Version=1.0.2229.32160, Culture=neutral, PublicKeyToken=null">
<value />
</data>
<data name="resource2" type="j2associates.Tools.j2aChainedComboBoxes.Plural2SingularItem, j2aChainedComboBoxes, Version=1.0.2229.32160, Culture=neutral, PublicKeyToken=null">
<value />
</data>
<data name="resource3" type="j2associates.Tools.j2aChainedComboBoxes.Plural2SingularItem, j2aChainedComboBoxes, Version=1.0.2229.32160, Culture=neutral, PublicKeyToken=null">
<value />
</data>

My item class has 2 pairs of parallel properties, Boolean SingularCaseSensitive and String Singular and then equivalent plural properties. So False, Crisis, False, Crises would be valid for converting the plural Crises to singular Crisis. Similarly, False, Parenthesis, False, Parentheses would be valid for converting plural Parentheses to singular Parenthesis. Should I store them in a string like "False,Crisis,False,Crises" or do I need to use a Binary or Soap serializer?

I can read C# code, so feel free to post in C# if it is easier. I can do the translation. Thanks in advance for any help and/or suggestions!


j2associates  Wednesday, February 08, 2006 12:07 AM

I don't have the time to write asnippet at the moment, but basicly, your problem is that your TypeConverter does not give instructions on creating the InstanceDescriptor.

The CanConvertTo() method correctly allows conversion to InstanceDescriptor, but the ConvertTo() method does not include code to do it.

This article may help:
http://www.divil.co.uk/net/articles/designers/collectioncontrols.asp

Mick Doherty  Wednesday, February 08, 2006 7:16 PM

Did you finally find the solution? I've got the same problem under ASP.NET. Any help would be highly appreciated.

Cheers,
Mehdi

mehdi mousavi  Saturday, May 06, 2006 9:12 AM

Hello Medhi,

Here is some code that seems to work that I found inside a response to a question on CodeProject. Unfortunately, I can't find the link. I have not used it in the actual class upon which the original post was made.

' Property in Main Class

Private m_Columns As SampleColumnHeaderCollection

<DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _

Category("My Custom Items Collection")> _

Public ReadOnly Property Columns() As SampleColumnHeaderCollection

Get

If m_Columns Is Nothing Then m_Columns = New SampleColumnHeaderCollection

Return m_Columns

End Get

End Property

' Separate Class Code

#Region " ----- SampleColumnHeaderCollection -----"

Public Class SampleColumnHeaderCollection

Inherits CollectionBase

Private iColumnWidth As Integer = 0

Public Property ColumnWidth() As Integer

Get

Return iColumnWidth

End Get

Set(ByVal Value As Integer)

iColumnWidth = Value

End Set

End Property

Default Public ReadOnly Property Item(ByVal index As Integer) As SampleColumnHeader

Get

Return CType(List.Item(index - 1), SampleColumnHeader)

End Get

End Property

Public Sub New(ByVal sourceList As IList)

InnerList.AddRange(sourceList)

End Sub 'New

Public Sub New()

End Sub 'New

Public Sub Add(ByVal objItemToAdd As SampleColumnHeader)

'MessageBox.Show(objItemToAdd.Text) 'glw4

If Not IsDBNull(Me.List) Then

Me.List.Add(objItemToAdd)

ProcessColumnWidth()

End If

End Sub

Public Sub AddRange(ByVal CHeader() As SampleColumnHeader)

Dim i As Integer

For i = 0 To UBound(CHeader)

Me.Add(CHeader(i))

Next

ProcessColumnWidth()

End Sub

Public Sub ProcessColumnWidth()

Dim CH As SampleColumnHeader

Dim iCWidth As Integer = 20

For Each CH In Me.List

iCWidth += CH.Width

Next

Me.ColumnWidth = iCWidth

End Sub

Public Function IndexOf(ByVal value As SampleColumnHeader) As Integer

Return List.IndexOf(value)

End Function 'IndexOf

Public Sub Insert(ByVal index As Integer, ByVal value As SampleColumnHeader)

List.Insert(index, value)

End Sub 'Insert

Public Sub Remove(ByVal index As Integer)

' Check to see if there is a columnheader at the supplied index.

If index > Count - 1 Or index < 0 Then

MessageBox.Show("Index not valid!")

Else

' Invokes the RemoveAt method of the List object.

List.RemoveAt(index)

End If

ProcessColumnWidth()

End Sub

Public Function Contains(ByVal value As SampleColumnHeader) As Boolean

' If value is not of type Int16, this will return false.

Return List.Contains(value)

End Function 'Contains

Protected Overrides Sub OnInsert(ByVal index As Integer, ByVal value As [Object])

' Insert additional code to be run only when inserting values.

End Sub 'OnInsert

Protected Overrides Sub OnRemove(ByVal index As Integer, ByVal value As [Object])

' Insert additional code to be run only when removing values.

End Sub 'OnRemove

Protected Overrides Sub OnSet(ByVal index As Integer, ByVal oldValue As [Object], ByVal newValue As [Object])

' Insert additional code to be run only when setting values.

End Sub 'OnSet

'Protected Overrides Sub OnValidate(ByVal value As Object)

'If Not value.GetType() Is Type.GetType("SampleColumnHeader") Then

'Throw New ArgumentException("value must be of type SampleColumnHeader.", "value")

'End If

'End Sub

End Class

#End Region

#Region "----- SampleColumnHeader -----"

<TypeConverter(GetType(SampleColumnHeaderConverter))> _

Public Class SampleColumnHeader

Inherits Component

Private iWidth As Integer = 100

<DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _

Public Property Width() As Integer

Get

Return iWidth

End Get

Set(ByVal Value As Integer)

iWidth = Value

End Set

End Property 'Width

Private strText As String

<DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _

Public Property Text() As String

Get

Return strText

End Get

Set(ByVal Value As String)

strText = Value

End Set

End Property 'Text

Public Sub New(ByVal myText As String, ByVal m_Width As Integer)

strText = myText

iWidth = m_Width

End Sub 'New

Public Sub New()

End Sub 'New

Public Overrides Function ToString() As String

Return Me.Text

End Function

End Class

#End Region

#Region "----- SampleColumnHeaderConverter -----"

Friend Class SampleColumnHeaderConverter

Inherits TypeConverter

' Determine if object can be converted to specific type

Public Overloads Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, _

ByVal destinationType As Type) As Boolean

If destinationType Is GetType(InstanceDescriptor) Then Return True

Return MyBase.CanConvertTo(context, destinationType)

End Function 'CanConvertTo

' Method used to do the conversion

Public Overloads Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, _

ByVal culture As CultureInfo, _

ByVal value As Object, _

ByVal destinationType As Type)

' We want to create an InstanceDescriptor

If destinationType Is GetType(InstanceDescriptor) Then

Dim mMyItems As SampleColumnHeader = CType(value, SampleColumnHeader)

' If we have defined different value to our instance than the default values

' then lets use the overloaded constructor

If mMyItems.Text.CompareTo("") <> 0 Or mMyItems.Width <> 0 Then

Return New InstanceDescriptor( _

GetType(SampleColumnHeader).GetConstructor( _

New Type() {GetType(String), _

GetType(Int32)}), _

New Object() {mMyItems.Text, mMyItems.Width})

' We will use the default one with the default values

Else

Return New InstanceDescriptor(GetType(SampleColumnHeader).GetConstructor(New Type() {}), Nothing)

End If

End If

' For all other conversions we will use the base ConvertTo method.

Return MyBase.ConvertTo(context, culture, value, destinationType)

End Function

End Class

#End Region

j2associates  Friday, May 19, 2006 9:39 PM

You can use google to search for other answers

Custom Search

More Threads

• Tooltip can't display in MDI form design.
• constrict properties at runtime
• Warning in the .net 2005 designer
• Create Control Programatically in Design Time
• Unable to run a VB 2008 program after changes are made without being forced to save it.
• Protected control not select-able (single click) at design time on an inherited form
• [Visual Studio Professional 2005]Can't switch to another control...
• Classes not rebuilding in Visual Studio Project
• Looking for control suggestions
• How to give Validations for Controls in C#.net Windows Aplications