Property assignment causes strange designer-generated code

M

michael sorens

Yesterday Visual Studio gave me a strange error both at compiletime and at
designtime that had no obvious connection to anything I had changed recently.
After some effort tracking down the problem I discovered first a workaround,
then the real cause of the problem. I would like to understand why what I am
doing is frowned upon by Visual Studio and how to do this properly.

My application is in one solution; supporting libraries including custom
controls in another. I am using C# in VS2008 and compiling to .NET 2.0 on
WinXP.

These are the compiletime errors:
###########################
Error 1 Invalid Resx file. Type could not be read from the data in line
169, position 5. The type's internal structure may have changed. Either
implement ISerializable on the type or provide a type converter that can
provide a more reliable conversion format, such as text or an array of bytes.
The conversion exception was: The constructor to deserialize an object of
type 'CleanCodeControls.Support.ConnectionDetails' was not found. File:
MainForm.resx

Error 2 SerializationException: Type could not be read from the data in
line 169, position 5. The type's internal structure may have changed.
Either implement ISerializable on the type or provide a type converter that
can provide a more reliable conversion format, such as text or an array of
bytes. The conversion exception was: The constructor to deserialize an
object of type 'CleanCodeControls.Support.ConnectionDetails' was not found.
XmlException: Type could not be read from the data in line 169, position 5.
The type's internal structure may have changed. Either implement
ISerializable on the type or provide a type converter that can provide a more
reliable conversion format, such as text or an array of bytes. The
conversion exception was: The constructor to deserialize an object of type
'CleanCodeControls.Support.ConnectionDetails' was not found. Line 169,
position 5.
###########################

The visual designer manifests essentially the same error:
###########################
The constructor to deserialize an object of type
'CleanCodeControls.Support.ConnectionDetails' was not found.
###########################

These errors appeared because of this designer-generated statement in
MainForm.Designer.cs...
###########################
new
CleanCodeControls.Support.ConnectionDetailsCollection().Add(((CleanCodeControls.Support.ConnectionDetails)(resources.GetObject("leftSqlEditor.ConfigurationList"))));
###########################

That line was embedded in a set of statements initializing a custom control
inside InitializeComponent; here's some context. Instead of assigning to the
ConfigurationList property it created this unusual line.
###########################
this.leftSqlEditor.AutoExecuteMode = false;
this.leftSqlEditor.AutoHighlightMode = false;
this.leftSqlEditor.CommandTimeout = 60;
new
CleanCodeControls.Support.ConnectionDetailsCollection().Add(((CleanCodeControls.Support.ConnectionDetails)(resources.GetObject("leftSqlEditor.ConfigurationList"))));
this.leftSqlEditor.ContainerTest = false;
this.leftSqlEditor.CsvPath = ".";
this.leftSqlEditor.DbConfigurationName = "--";
this.leftSqlEditor.DiagLabel = "LeftSqlEditor";
this.leftSqlEditor.Dock = System.Windows.Forms.DockStyle.Fill;
this.leftSqlEditor.LocalMode = false;
###########################

Corresponding to this was a section in the MainForm.resx file:
###########################
<data name="leftSqlEditor.ConfigurationList"
mimetype="application/x-microsoft.net.object.binary.base64">
<value>--- encoded binary data here --- </value>
</data>
###########################

By deleting the line in MainForm.Designer.cs and deleting the <data> element
in the MainForm.resx file, I could then compile successful and open the form
in visual designer. However, everytime I re-display the form in the visual
designer those code chunks are inserted again.

I digged deeper to determine why those lines were being generated all of a
sudden, because I had not changed anything related to the serialization or
structure of ConnectionDetails. I traced it to the line indicated with arrows
below (the ConfigurationList assignment inside this ContainerTest property)
of my SqlEditor custom control. leftSqlEditor is an instance of SqlEditor.
Removing the assignment to the ConfigurationList property removes the
generation of the above offending code by the visual designer.

###########################
public bool ContainerTest
{
get { return containerTest; }
set
{
containerTest = value;

// signal to fill unbound DataGridView
dataGridView.ContainerTest = containerTest;

// provide a default connection to get bound data as wellnew ConnectionDetails
{
ConfigName = "sqlExpress",
ConnectionString =
@"Data Source=.\SQLEXPRESS;Initial
Catalog=AdventureWorks;"+
@"Integrated Security=True;Persist Security
Info=True",
DbType = ConnectionStringManager.DBTypes.SqlServer
}
};

DbConfigurationName = "sqlExpress";
}
}
private bool containerTest = false;

public ConnectionDetailsCollection ConfigurationList
{
get { return configurationList; }
set
{
configurationList = value;
UpdateForChangedConfigList();
}
}
private static ConnectionDetailsCollection configurationList;

###########################


Here are the relevant code highlights of the object that is apparently
having serialization problems, though this code has been working fine even
with the Properties/Settings page for over 6 months. I can, for instance, go
to the Properties/Settings page of the application, click on the ellipsis for
this setting, and VS invokes a collection editor to allow manipulation of the
collection without complaint. My understanding was that this uses the
serialization in question.

###########################
namespace CleanCodeControls.Support
{
public class ConnectionDetailsCollection : Collection<ConnectionDetails>
{
// empty
}


[Serializable]
public class ConnectionDetails : ICloneable, ISerializable
{

public ConnectionDetails(string configName) { this.configName =
configName; }

public ConnectionDetails() { }

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("ConfigName", configName);
info.AddValue("ConnectionString", connectionString);
info.AddValue("DbType", dbType);
info.AddValue("RememberPassword", rememberPassword);
info.AddValue("Description", description);
}
}
}
###########################


So why does my particular sequence of code generate these misbehaving lines
of code, and why are they misbehaving? Or to cut to the chase: what could I
do to fix this?
 
L

Linda Liu[MSFT]

Hi Michael,

I performed a test based on your description and sample code and did
reproduce the problem on my side.

Firstly, you need to add a DesignerSerializationVisibilityAttribute with
the value of Content to the ConfigurationList property in order to correct
the serialization of the ConfigurationList property in the form's
InitializeComponent method. For example,

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ConnectionDetailsCollection ConfigurationList
{
get { return configurationList; }
set
{
configurationList = value;
UpdateForChangedConfigList();
}
}

Secondly, the ISerializable interface implies a constructor with a
signature constructor(SerializationInfo information, StreamContext
context). In general, this constructor should be protected if the class is
not sealed. For example,

[Serializable]
public class ConnectionDetails : ICloneable, ISerializable
{
...
protected ConnectionDetails(SerializationInfo information,
StreamContext context)
{
configName = info.GetString("ConfigName");
connectionString = info.GetString("ConnectionString");
dbType = info.GetString("DbType");
rememberPassword = info.GetString("RememberPassword");
description = info.GetString("Description");
}
}

At last, you need to call the SetType method of the SerializationInfo class
in the ISerializable.GetObjectData method to set the type of the object to
serialize. For example,

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.SetType(typeof(ConnectionDetails));
info.AddValue(...);
...
}

Please try my suggestion and let me know the result.

Sincerely,
Linda Liu
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

michael sorens

Thanks for the information, but it did not fix the issue.

Adding the first item:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

changed the generated code from:
new
CleanCodeControls.Support.ConnectionDetailsCollection().Add(((CleanCodeControls.Support.ConnectionDetails)(resources.GetObject("leftSqlEditor.ConfigurationList"))));
to:

this.leftSqlEditor.ConfigurationList.Add(((CleanCodeControls.Support.ConnectionDetails)(resources.GetObject("leftSqlEditor.ConfigurationList"))));

Next, I added this serialization constructor (with a couple modifications to
your code to get it to compile):

protected ConnectionDetails(SerializationInfo info, StreamingContext
context)
{
configName = info.GetString("ConfigName");
connectionString = info.GetString("ConnectionString");
dbType = (ConnectionStringManager.DBTypes)Enum.Parse(
typeof(ConnectionStringManager.DBTypes),
info.GetString("DbType"));
rememberPassword = info.GetBoolean("RememberPassword");
description = info.GetString("Description");
}

And finally I added the SetType at the top of GetObjectData.

Now attempting to open the form in the designer yields this error:

"Method 'CleanCodeControls.Support.ConnectionDetailsCollection.Add' not
found. "

My ConnectionDetailsCollection class, as a reminder is just this empty class
definition:

public class ConnectionDetailsCollection : Collection<ConnectionDetails>
{
// empty
}

So are we getting closer to a solution? My understanding of the connections
for serialization is weak.
 
L

Linda Liu[MSFT]

Hi Michael,

Thank you for your reply!
Next, I added this serialization constructor (with a couple modifications
to your code to get it to compile):

I didn't know the type of the DbType property and I assumed it as the type
of String. Thank you for showing us how you correct this line of code!
Now attempting to open the form in the designer yields this error:
"Method 'CleanCodeControls.Support.ConnectionDetailsCollection.Add' not
found. "

Please delete the custom control from the Form and delete the item
"leftSqlEditor.ConfigurationList" from the form.resx file first. Re-build
the project and re-open the form in the designer and add the custom control
onto the form to see if the problem still exists.

Sincerely,
Linda Liu
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

michael sorens

Rebuilding as you indicated seems to have cleared up the issue.
As a final question on this topic, could you point me to the MSDN pages that
describe these pieces necessary for proper serialization?

I looked through "Introducing XML Serialization"
(http://msdn.microsoft.com/en-us/library/182eeyhh(VS.80).aspx)
but I cannot find where it talks about any of the three points you itemized
to fix my problem.

Thanks!
 
L

Linda Liu[MSFT]

Hi Michael,

Thank you for your reply!

As for the first point, properties that do not have a
DesignerSerializationVisibilityAttribute will be treated as though they
have a DesignerSerializationVisibilityAttribute with a value of Visible.
For a property of type collection, a private variable of type collection
will be created and initialized with the items configured at design time
first and then assigned to the public collection property within the
InitializeComponent method.

In your scenario, you configure the ConfigurationList property within the
set accessor of the ContainerTest property, so the form designer doesn't
know which property the private variable should be assigned to. At this
time, we can specify a DesignerSerializationVisibilityAttribute with a
value of Content to address the problem.

The following MSDN document may help:
'How to: Serialize Collections of Standard Types with the
DesignerSerializationVisibilityAttribute'
http://msdn.microsoft.com/en-us/library/ms171833.aspx

As for the second point, you implement the ISerializable interface for the
ConnectionDetails class, so the value of this property will be serialize
and stored in the form's resx file. When the form is re-opened, a design
time error occurs. So it's obvious that something is wrong when the form
designer is trying to construct instances of the
ConnectionDetailsCollection class.

There's a comment "The ISerializable interface implies a constructor with
the signature constructor (SerializationInfo information, StreamingContext
context). " in the following MSDN document that explains this exactly:
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserial
izable.aspx

As for the third point, after I configured the ConfigurationList property
in the designer and compile the project, I found that the value under the
Type column of the item "leftSqlEditor.ConfigurationList" within the
form.resx is null. So the type of the resource item is missing.

The sample in the
"http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iseria
lizable.aspx" shows that we should call the SerializationInfo.SetType
method to set the Type of the object to serialize in the
ISerializable.GetObjectData implementation.

Hope this helps.
If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 
L

Linda Liu[MSFT]

You're welcome, Michael!

If you have any other questions in the future, please don't hesitate to
contact us. It's always our pleasure to be of assistance.

Have a nice day!

Sincerely,
Linda Liu
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top