How to implement Save / Load functionality?

I am trying to implement the Load / Save function for a Windows Forms application.

I have the following components:

  • Tree view
  • Several types of lists
  • Multiple text fields
  • Multiple objects (which contain a large dictionary)

I want to implement a way to save all this in a file and resume / load it later.

What is the best way to do this?

I think XML serialization is the way to go, but I'm not quite sure how and where to start. Or will it require a really complicated solution to do this?

+5
winforms save load
Dec 07 '08 at 20:20
source share
7 answers

Here is an example that links an object and some ancestors to a user interface; using C # 3.0 here is pure for brevity - everything will work with C # 2.0 as well.

Most of the code here sets up the form and / or regarding property change notifications - It is important to note that there is no code dedicated to updating the user interface from the object model or the object model from the user interface.

Note that the IDE can also execute a lot of data binding code for you by simply dropping the BindingSource onto the forms and setting the data source to type through a dialog in the property grid.

Note that there is no need to provide a change to the notification property (PropertyChanged material) - however, most two-way UI bindings will work much better if you implement this. Not that PostSharp has some interesting ways to do this with minimal code.

using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Windows.Forms; using System.Xml.Serialization; static class Program { // formatted for vertical space [STAThread] static void Main() { Application.EnableVisualStyles(); Button load, save, newCust; BindingSource source = new BindingSource { DataSource = typeof(Customer) }; XmlSerializer serializer = new XmlSerializer(typeof(Customer)); using (Form form = new Form { DataBindings = {{"Text", source, "Name"}}, // show customer name as form title Controls = { new DataGridView { Dock = DockStyle.Fill, // grid of orders DataSource = source, DataMember = "Orders"}, new TextBox { Dock = DockStyle.Top, ReadOnly = true, // readonly order ref DataBindings = {{"Text", source, "Orders.OrderRef"}}}, new TextBox { Dock = DockStyle.Top, // editable customer name DataBindings = {{"Text", source, "Name"}}}, (save = new Button { Dock = DockStyle.Bottom, Text = "save" }), (load = new Button{ Dock = DockStyle.Bottom, Text = "load"}), (newCust = new Button{ Dock = DockStyle.Bottom, Text = "new"}), } }) { const string PATH = "customer.xml"; form.Load += delegate { newCust.PerformClick(); // create new cust when loading form load.Enabled = File.Exists(PATH); }; save.Click += delegate { using (var stream = File.Create(PATH)) { serializer.Serialize(stream, source.DataSource); } load.Enabled = true; }; load.Click += delegate { using (var stream = File.OpenRead(PATH)) { source.DataSource = serializer.Deserialize(stream); } }; newCust.Click += delegate { source.DataSource = new Customer(); }; Application.Run(form); } } } [Serializable] public sealed class Customer : NotifyBase { private int customerId; [DisplayName("Customer Number")] public int CustomerId { get { return customerId; } set { SetField(ref customerId, value, "CustomerId"); } } private string name; public string Name { get { return name; } set { SetField(ref name, value, "Name"); } } public List<Order> Orders { get; set; } // XmlSerializer demands setter public Customer() { Orders = new List<Order>(); } } [Serializable] public sealed class Order : NotifyBase { private int orderId; [DisplayName("Order Number")] public int OrderId { get { return orderId; } set { SetField(ref orderId, value, "OrderId"); } } private string orderRef; [DisplayName("Reference")] public string OrderRef { get { return orderRef; } set { SetField(ref orderRef, value, "OrderRef"); } } private decimal orderValue, carriageValue; [DisplayName("Order Value")] public decimal OrderValue { get { return orderValue; } set { if (SetField(ref orderValue, value, "OrderValue")) { OnPropertyChanged("TotalValue"); } } } [DisplayName("Carriage Value")] public decimal CarriageValue { get { return carriageValue; } set { if (SetField(ref carriageValue, value, "CarriageValue")) { OnPropertyChanged("TotalValue"); } } } [DisplayName("Total Value")] public decimal TotalValue { get { return OrderValue + CarriageValue; } } } [Serializable] public class NotifyBase { // purely for convenience [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; protected bool SetField<T>(ref T field, T value, string propertyName) { if (!EqualityComparer<T>.Default.Equals(field, value)) { field = value; OnPropertyChanged(propertyName); return true; } return false; } protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } } 
+6
Dec 08 '08 at 8:23
source share

Ideally, you should not remain in a user interface state; You must keep the state of some object model representing your data. With the exception of TreeView , it is fairly simple to use data binding to bind the object model to the user interface. This can be either a DataTable based approach or a custom class hierarchy (my preferences).

Once you share data from the user interface, saving data is simple. There are many examples for XmlSerializer , etc.

+6
Dec 07 '08 at 20:24
source share

Yes, you need to use XML serialization for this. But, as Mark Gravell noted, you must have objects containing data displayed by your GUI components. Then you can practically make the serialization (de) automatic, with minimal lines of code.

+1
Dec 07 '08 at 20:34
source share

There is a problem with the above sample. note that your application will eventually be updated. your object model can change dramatically and therefore cannot be deserialized. There are some things you could do to make deserialization from xml version 1 deserialize for your object model in version 2, but if it is possible that you can have big structural changes, then xml deserialization is not suitable.

if so, and your application is deployed to clients, I highly recommend looking further at your save / load logic.

Serialize / Deserialize Serialization
serialize the state of the object as:

 <ObjectState version="1"> <Field1>value</Field1> ... etc ... </ObjectState> 

So now you have a version of the object model that generated the saved state. in your deserialization, you can take special measurements to accommodate this fact. for example, write the value of Field1 in a list in another object.

another approach:

Sequential serialization and conversion before deserialization
serialize the state of your object as above (with the version attribute).
when deserialization looks at the version attribute, if it is not the version you are expecting, convert the serialized state of the object using xsl scripts or C # code to your current version. you can save the xsl conversion list in your current project

 - conversions - v1-v2 - v2-v3 

if you are currently in version 3 and want to download the xml file, look at the version attribute and run all xsl scripts to get the current version (version 3). therefore you must run xsl-script v1-v2 and then v2-v3.

in this case, you can have the usual serialization and deserialization classes that do not have to worry about being able to back.

+1
Dec 25 '10 at 10:10
source share

It's pretty trivial to use data binding to bind the object model to the user interface.

How can I link an object using GUI management without read-only memory? If I do this manually, it means that I have to write a ridiculous amount of code for each individual object in memory. I already have some kind of class repository for this data, but it does not bind the sorting script, it seems to read this entry here.

Should I write a loader that loads serialized XML and receives the object and then reads the object and populates the entire GUI? Obviously, this is more like manual loading, not binding. Did I miss something?

0
Dec 07 '08 at 21:20
source share

Here's a great article on how to make a class or structure serializable. I would create a class that allows you to store all the data you need. Make the class consistent. Thus, in just a few lines of code, you can save all your data to a file. Then, with a few more lines of code, you can extract the data from the file.

http://www.codeproject.com/KB/cs/objserial.aspx

0
Dec 07 '08 at 21:27
source share

An alternative to serializing your classes is to use the ADO.NET data set for storing data, which has built-in tools for saving the XML file. The code will be minimal, and you can store only the necessary data by creating tables that match the model of the operation you are performing. In addition, you can use the same code if you later decide to save the state of the user interface in the database instead of the local file. You will need to have an alternative function to save the data set.

0
Dec 07 '08 at 23:59
source share



All Articles