I have an assembly which contains several UserControl
objects that I want to be able to save/load via the application UI. To do this, each control implements the ISerializable
interface to customize the fields they need to save.
Here's a simplified version of that library:
namespace LibraryProject
{
using System;
using System.Runtime.Serialization;
using System.Windows.Forms;
[Serializable]
public partial class UserControl1 : UserControl, ISerializable
{
public UserControl1()
{
InitializeComponent();
}
public UserControl1(SerializationInfo info, StreamingContext ctxt)
: this()
{
this.checkBox1.Checked = info.GetBoolean("Checked");
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Checked", this.checkBox1.Checked);
}
}
}
The client application instantiates several of this controls, and allows the user saving/loading the various UserControl
configurations. Here's a simplified version of the application:
namespace ApplicationProject
{
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;
using System.Windows.Forms;
using LibraryProject;
public partial class Form1 : Form
{
private const string filename = @"test.xml";
//int hash1;
//int hash2;
public Form1()
{
InitializeComponent();
//hash1 = this.ctrl1.GetHashCode();
}
private void SaveClick(object sender, EventArgs e)
{
using (var stream = File.Open(filename, FileMode.Create))
{
var formatter = new SoapFormatter();
formatter.Serialize(stream, this.ctrl1);
}
}
private void LoadClick(object sender, EventArgs e)
{
using (var stream = File.Open(filename, FileMode.Open))
{
var formatter = new SoapFormatter();
this.ctrl1= (UserControl1)formatter.Deserialize(stream);
}
//hash2 = this.ctrl1.GetHashCode();
}
}
}
On SaveClick
, the values are properly saved to file.
On LoadClick
, the CheckBox.Checked
is properly updated in the Debugger Watch list, but the UI doesn't reflect the new value.
I have tried adding calls to Refresh()
, Invalidate()
, Update()
, but nothing seems to work.
As expected, hash1
and hash2
are different, but Form1
uses the correct instance.
What am I doing wrong, and how can I fix the UI to display the correct (updated) value?
EDIT: Also, notice that I need to handle multiple config files, that the user must have the ability to save/load to/from a path of her choice
You can't deserialize something directly to an interface, You need a concrete class to instantiate.
Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.
JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).
Deserialization is the process of reconstructing a data structure or object from a series of bytes or a string in order to instantiate the object for consumption. This is the reverse process of serialization, i.e., converting a data structure or object into a series of bytes for storage or transmission across devices.
I have a sneaky suspicion that the UI isn't update because it isn't yet visible. I have found controls with a visible property set to false sometimes act up.
You are really better off serializing the data in some structured format and then using that to instantiate a control and populate the controls. Something like this (untested):
public class UserControlData
{
public string Type { get; set; } // or assembly qualified type
public List<ControlValue> ControlValues { get; set; }
}
public class ControlValue
{
public string Name { get; set; }
public object Value { get; set; }
}
public interface IControlPersistence
{
List<ControlValue> GetControlValues();
void SetControlValues(List<ControlValue> controlValues);
}
Then serialize / deserialize the user control data and instantiate and set values from the definitions. After you have instantiated and added the user control it can update its control values independently from the instantiation --- or even only once it is visible (should that endup being the issue).
I would also suggest wrapping the XmlSerializer and using that for serialization (maybe something like IObjectSerializer
/ XmlObjectSerializer : IObjectSerializer
).
Hope that makes sense.
I'm not sure but I'm going to guess it's because InitializeComponent
doesn't get called.
But to solve your problem, it is better to serialize a surrogate. Just make little surrogate classes marked [Serializable] and copy properties from the UserControl to the surrogate before serialization. Then you don't have to mess around with GetObjectData
- the serialization process just assumes every property of the surrogate should be serialized.
The deserialization process will give you a surrogate back. The surrogate just has to know how to instantiate a UserControl and map the properties back to it.
And if you define a common interface, you don't have to know which specific type of UserControl you are deserializing:
var surrogate = formatter.Deserialize(stream) as ISerializationSurrogate;
UserControl uc = surrogate.Create();
this.Controls.Add(uc);
Here's an example of how a surrogate might look:
[Serializable]
public class MySurrogate: ISerializationSurrogate
{
public MySurrogate() {}
public MySurrogate(MyControl control)
{
CB1Checked = control.checkBox1.Checked;
}
public bool CB1Checked { get; set; }
public Control Create()
{
var control = new MyControl();
control.checkBox1.Checked = CB1Checked;
return control;
}
}
Update
Actually, I bet the problem is that you're simply reassigning this.ctrl
, which doesn't change which controls are on the form. What you actually need to do is something like this:
this.Controls.Remove(existingControl); // if it exists
this.Controls.Add(newControl);
But you should still use serialization surrogates. They make this kind of stuff much easier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With