I'm trying to implement a Load / Save function for a Windows Forms application.
I've got following components:
I want to implement a way to save all of this into a file, and resume/load it later on.
What's the best way to do this?
I think XML serialization is the way to go, but I'm not quite sure how, or where to start. Or will it require a really complex solution to be able to do this?
Unity provides two ways to save a game's data. They can be quickly described as “the easy way” and “the not so easy way.” The easy way involves Unity's built-in PlayerPrefs system. Give a value to a key, call Save, and you're done.
Load definition imports a new resource into a project while Save definition saves it for export to another system. For example you may want to reuse an activity or data source which has already been defined for a different task.
Here's an example that binds an object and some ancestors to the UI; the use of C# 3.0 here is purely for brevity - everything would work with C# 2.0 too.
Most of the code here is setting up the form, and/or dealing with property-change notifications - importantly, there isn't any code devoted to updating the UI from the object model, or the object model from the UI.
Note also the IDE can do a lot of the data-binding code for you, simply by dropping a BindingSource onto the form and setting the DataSource to a type via the dialog in the property grid.
Note that it isn't essential to provide property change notifications (the PropertyChanged stuff) - however, most 2-way UI binding will work considerably better if you do implement this. Not that PostSharp has some interesting ways of doing 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));
}
}
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