C# Serialize WinForm - c#

I am trying to serialize a winform, with the end goal of being able to recreate the values in the various controls of the form. My form contains the typical controls, buttons/radio buttons/checkboxes/textboxes/listbox/tab control.
I am receiving this error:
An exception of type 'System.InvalidOperationException' occurred
in System.Xml.dll but was not handled in user code
Additional information: There was an error reflecting type
'Receptionist_Program.Objects.Client.Client_NCQ'.
I setup properties for each value I want to save:
public bool CbMedTreat
{
get { return cbMedTreat.Checked; }
set { cbMedTreat.Checked = value; }
}
public List<Client_AddDoctor> TxtDocExplain // Client_AddDoctor is another form
{
get { return listDoctors; }
set { listDoctors = value; }
}
// etc, variety of string and bool properties
At the top of the class I have the decoration:
[Serializable]
public partial class Client_NCQ : Form
Finally, here is my code doing the serialization:
Client_NCQ badname = new Client_NCQ();
badname.Initialize();
badname.ShowDialog();
string result = "";
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Client_NCQ));
// Error occurs here on above line: new XmlSerializer(typeof(Client_NCQ))
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, badname);
result = textWriter.ToString();
}
I tried two different things so far, first, I added the decoration [XmlIgnore] to the List<> property, this made no difference. Second, I tried ensuring that the constructor was empty and had no parameters.

Serializing an entire Form is a bad idea because it is not meant to be serialized:
it has a lot of properties that should not be serialized (e.g. displaying related properties)
even if it works properly, you will have a lot of data that is not relevant for your application's state
The correct solution is to keep all state information in your custom objects and bind to those objects using WinForm's databinding capabilities. If this means great changes to your application, try to serialize only the data that is relevant to constructing the state.
How can you know which data is relevant for application state?
Before constructing and showing the form, I expect that you are loading the data from a database, file etc. All that information should be contained in clearly defined objects of types marked with [Serializable] attribute. This way, it is easy to serialize and deserialize it at will.
Also, it is important to take into consideration version tolerant serialization or what happens when the form / state information is changed (e.g. a field is added) and an older XML is used to restore state.

Every form has its own mechanism to store and retrieve (serialize and deserialize) data and it does this automatically. However, the following conditions are to be met in order to use this feature automatically.
- All properties which are desired to be serialized must have public get and set accesor.
- If a certain property represents custom object such as user defined class or struct, the object must be adorned with [Serializable] attribute.
- Property which is desired to be serialized must not have [DesignerSerializationVisibility] attribute set to Hidden. By default it is Visible so not specifying this attribute at all is sufficiently serves the purpose.
Consider this example:
namespace MnM.Drawing
{
[Serializable, TypeConverter(typeof(ExpandableObjectConverter))]
public class Coordinates
{
public int X { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Y { get; set; }
public int Z { get; protected set; }
}
public class MyForm : Form
{
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public Coordinates MyCoordinates { get; set; }
}
}
Now, MyForm will automatically serialize MyCoordinates object but...
Only property X will get serialized because it fits the requisite
status to qualify for auto serialization.
Property Y can not be serialized because of DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
Property Z can not get serialized automatically because it does
not have public set accesor.
In order to serialize Y and Z, custom serialization code is required. In most cases, need of custom serialization does not arise and custom serialization can be done in many ways but its a vast topic.

Related

Custom canvas class serialization

I've a class with some properties which I want to serialize. My problem is that
I can't serialize the "CustomCanvasClass". I only need the X/Y properties of it.
So I created a new property and marked the "CustomCanvasClass" property as [NonSerialized].
Unfortunatly it won't works. Maybe have somebody another idea to copy this data out of the class.
[Serializable]
public class CustomClass
{
//won't serialized
public double X
{
get
{
return Canvas.GetLeft(CustomCanvasClass);
}
set
{
Canvas.SetLeft(CustomCanvasClass, value);
}
}
public string Property1 { get; set; }
//CanvasElement inherits from Canvas. Serialization would throw a Exception.
public CanvasElement CustomCanvasClass
{
get
{
return _CustomCanvasClass;
}
set
{
_CustomCanvasClass = value;
}
}
[NonSerialized]
private CanvasElement _CustomCanvasClass;
}
Use a DTO for the properties you need and serialize that.
DTO stands for data transfer object. It contains data you want to transfer only and no logic.
E.g. Add a class like this:
class MyCustomClassDto
{
public double X {get;set;}
public double Y {get;set;}
}
So instead of trying to serialize your custom class directly, you would initialize an instance of this with your X and Y values and serialize that.
Then in your main class you could add this:
public MyCustomClassDto GetData()
{
return new MyCustomerClassDto{X = X, Y = Y};
}
You could add a serialization method to your DTO also.
Alternatively you can use a mapping tool like automapper - which would be suitable if you have many DTOs or corresponding objects in different layers.
Hope that makes the idea clear. Can't see other ways of expanding without seeing more details/context.
The proper mvvm itemspanel approach in the comment on the question is preferable, but it might require substantial rewriting depending on your existing codebase. You might want to consider the business case for such refactoring imo, considering the effort against how much more is likely to be built on top of it.

Efficient (Space) Serialization for Network Transfer

I am attempting to write some infrastructure to facilitate updating objects between a server and client(s). This will likely be used in a game, however, I feel that the question is not at all specific to the game (so I have asked it here).
For security and efficiency reasons I would like the server to selectively update object properties. For example, a specific property of an object may only be useful to the client which controls that object, as such the server will only update the 'owner' with this information. Alternatively, some properties may need to be sent to all clients. To implement this I have defined a custom attribute which specifies the manner in which the network should handle the property:
[AttributeUsage(AttributeTargets.Property)]
public class NetworkParameterAttribute : System.Attribute
{
public enum NetworkParameterType
{
ServerToOwner,
ServerToAll,
ServerToOwnerView,
OwnerToServer
}
private NetworkParameterType type;
public NetworkParameterType Type
{
get
{
return type;
}
}
public NetworkParameterAttribute(NetworkParameterType Type)
{
this.type = Type;
}
}
Now in an object class I can define properties like so:
public class TestObject
{
[NetworkParameter(NetworkParameterAttribute.NetworkParameterType.ServerToAll)]
public int ID { get; set; }
[NetworkParameter(NetworkParameterAttribute.NetworkParameterType.ServerToOwner)]
public string Name { get; set; }
}
I can then write a simple function which automatically grabs a certain set of properties from an object:
public byte[] GetBytes(NetworkParameterAttribute.NetworkParameterType type)
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
foreach (PropertyInfo info in this.GetType().GetProperties())
{
foreach (object attribute in info.GetCustomAttributes(true))
{
if (attribute is NetworkParameterAttribute &&
((NetworkParameterAttribute)attribute).Type == type)
{
formatter.Serialize(stream, info.GetValue(this, null));
}
}
}
byte[] buf = new byte[stream.Length];
Array.Copy(stream.GetBuffer(), buf, stream.Length);
return buf;
}
A similar function can put the object back together on the receiving side. The issue that I am having is that the serialization is very inefficient in terms of space used. For example, grabbing the ServerToAll properties from a TestObject results in 54 bytes (whereas it could be as little as 4).
So the question: Is there a more efficient way of serializing objects to a byte stream that will work for my intended purpose? Note that I would prefer not to write a lot of serialization related code.
Thank you!
NetworkParameterAttribute should have additional field that denotes the corresponding property as "dirty". Every change to a property should effectively set this flag, and during serialization the flag should be reset. Only dirty properties should actually be serialized.
Additionally, now that the object is only partially serialized, during serialization now you need to provide the information about what properties are being serialized. Maintain one bitvector of dirty properties while you populate the stream, and put this bitvector in the beginning of returned byte array.
EDIT: Instead of having a flag inside an attribute we can have the actual value that was serialized last. The advantage is that we don't need additional code for each property to keep the flag synchronized with the property. During serialization we compare two values and serialize property if the values are not equal.

How to serialize a class with a complex property using XML serialization in .NET?

I want to serialize a class. In this class there's a property of type Class1 that in turn has its own properties.
public abstract class ComponentBase
{
[ToSerialize] //An attribute defined my me, indicating whether or not to serialize this property.
public ComponentArgs Parameters { get; set; }
}
public class ComponentArgs
{
public string WorkingPath { get; set; }
public IList<Language> Languages { get; set; }
public string ComponentOutputPath { get; set; }
}
The information serialized must be put into a Dictionary<string,string>, such as
ComponentSettings[str_Name] = str_Value;
The method used in reading this value is Reflection
// pinfo: Property Info got via Type.GetProperties();
componentSettings.Add(pinfo.Name, pinfo.GetValue((object)this, null).ToString());
The information after serialization is:
<Parameters>MS.STBIntl.Pippin.Framework.ComponentArgs</Parameters>
instead of the value of ComponentArgs.WorkingPath.
The solution I thought of is to append to the following line an if judgement:
componentSettings.Add(pinfo.Name, pinfo.GetValue((object)this, null).ToString());
if(pinfo is ComponentArgs)
componentSettings.Add(pinfo.Name, pinfo.GetValue(
(ComponentArgs)this, null).WorkingPath+"\n"+
LanguageList+"\n"+ //Language list is a concatinated string of all elements in the list.
(ComponentArgs)this, null).ComponentOutputPath+"\n"+
);
When deserializing, add a judgement of whether the value contains more than 2 "\n", if so, extract each value from the string.
But this way seems clumsy and much more like an workaround. I wonder if there's any more professional way of doing it? My reviewer is very particular and he won't accept such a solution. If you know a way, could you please share it with me? Thanks a lot.
There are lots of ways to use inbuilt serialization.
The simplest and oldest is the [Serializable] attribute that tells .NET to serialize the members.
You can also use the WCF [DataContract] attribute to serialize stuff.
There is also the IXMLSerializable interface which allows you to implement custom XML readers and writers for you classes.
The bottom line is, there is no need to roll your own - it has been done.

Properties won't get serialized into the .designer.cs file

In VS2010, control properties won't get serialized despite the ShouldSerializeFoo method, with the DesignerSerializationVisibility.Visible/Content as well.
Here's the code:
class Class1 : UserControl {
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public string Foo {
get; set;
}
public bool ShouldSerializeFoo() {
return true;
}
public Class1() {
Foo = "dupa";
}
}
However, the designer doesn't generate anything for this property:
//
// class11
//
this.class11.Location = new System.Drawing.Point(224, 262);
this.class11.Name = "class11";
this.class11.Size = new System.Drawing.Size(150, 150);
this.class11.TabIndex = 2;
this.class11.Load += new System.EventHandler(this.class11_Load);
You're mixing serialization schemes. The designer serialization (which is what DesignerSerializationVisibility is for) has nothing to do with the instance serialization mechanism (which is what ShouldSerializeXXX functions, among many other things, deal with).
DesignerSerializationVisibility.Content doesn't make much sense for a string (or any other immutable type). The designer can view a property's serialization as three types:
None - it never serializes anything about the property
Visible - it will serialize the actual value of the property
Content - it will serialize the property values of the value of the property.
By default, a property is considered Visible. I realize that my definition of Content might be a little confusing. What I mean by that is like this:
public class MyControl : Control
{
public class SomeOptions
{
public string Option1 { get; set; }
public string Option2 { get; set; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public SomeOptions Options { get; private set; }
public string Foo { get; set; }
}
Now, when this class is serialized by the designer, it will look something like this:
// myControl1
this.myControl1.Foo = "value";
this.myControl1.Options.Option1 = "option1";
this.myControl1.Options.Option2 = "option2";
This should hopefully make more sense; marking a property as Content signifies that, rather than serializing the actual value of the property (in this case, it would be an instance of SomeOptions), it should serialize the property values of that value instead.
So, back to your question, that's why you don't want Content for a string property. Because strings are immutable, there's nothing for the designer to serialize. Either mark it as Visible or leave the attribute off entirely (since that's the default).
While it's possible to provide custom direction to the designer as to whether or not you want to serialize a particular property, it's a pretty involved (and nasty) process. The easy way, though, is to use the DefaultValue attribute on the property. If whether or not the property should be serialized can be determined by comparing its value to a constant (in other words, it's not dependent upon anything else at runtime, like the value of another property), you can decorate the property like this:
[DefaultValue("foo")]
public string Foo { get; set; }
If the designer then sees that the value of Foo is equal to "foo", then it won't serialize the property at all.

Why do I even need to Serialize in the first place?

So, I'm working with the following assembly, which has the following defined (fairly harmless):
public class QueryDefinition
{
private List<QueryFilter> TheCurrentFilters = null;
public List<QueryFilter> CurrentFilters
{
set { TheCurrentFilters = value; }
get { return TheCurrentFilters; }
}
// other code
public class QueryFilter
{
// member variables are: seven public string's & two public int's
public override string ToString()
{
return FilterText;
}
}
}
Within another assembly, we have a UserControl:
public partial class QueryWizard : UserControl
{
private List<QueryDefinition.QueryFilter> TheCurrentFilters = null;
public List<QueryDefinition.QueryFilter> CurrentFilters
{
set { TheCurrentFilters = value; }
get { return TheCurrentFilters; }
}
// other code
}
Interesting code, but that's what I have to work with.
Anyhow, if I go to another project (that references this UserControl), create a Form, and then drop the control onto the Form, I get this error:
'System.Runtime.Serialization.SerializationException: Type QueryDefinition+QueryFilter' in Assembly ... is not marked as serializable.'
I'm not actually using any Serialization code, so what of this List of QueryFilter's is the reason for a SerializationException?
I have used the [Serializable] tag, to get rid of this. But recently we were rebuilding projects (Visual WebGUI upgrade) and now I run into the "unable to load type required for deserialization" issue. Instead of figuring out that problem, I decided to try and figure out why we need the Serialization tags in the first place! Thanks.
It is because the designer tries to serialize the contents of the usercontrols "CurrentFilters" property into the form initialization code.
Check the DesignerSerializationVisibility attribute: http://msdn.microsoft.com/en-us/library/system.componentmodel.designerserializationvisibility.aspx
If you don't intend to support designtime editing of the CurrentFilters property, setting it to hidden should fix the problem (I think, was ages since I built winforms controls)
The actual values for the CurrentFilters are getting serialized using BinaryFormatter and stored in a .resx file. You almost certainly don't want this to happen. For one, you'll take a dependency on the [AssemblyVersion] number of the assembly that contains your QueryFilter class. Which should explain the "unable to load type" exception you get now.
First find out how CurrentFilters ended up with values at design time. You'll need to beware of events that run at design time. The typical candidates are the constructor and the Load event. Use the Control.DesignTime property to prevent code from running.
Next, ensure that the property value doesn't get persisted by applying an attribute:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<QueryFilter> CurrentFilters
{
}

Categories

Resources