I have this tiny C# winforms application that will NOT grow larger in any way.
It's just two input fields and a button.
I want to know if you guys have knowledge of a way to store the values that a user inputs in a local datastore. You may consider 10 records to be a lot of data for this scenario.
Requirements
It shouldn't require any setup from the database side. (table creation, etc)
I should be able to just give it an object and it should store it, I don't want to waste time on that.
The data needs to be fairly easily retrievable.
I want to be able to reuse this thing for every small app I create like this.
My Ideas
A POCO object that will be XML-Serialized and saved to the Local Settings folder. Upon loading of the app, this file is deserialized back into the POCO object.
An OODBMS: I have no experience with these but I always thought they consisted of a single dll so it would be easy to package them with the program.
I once, a long long time ago, built an application that stored user settings inside the registry. Don't know if that is still appreciated though.
What do you think is the best approach?
Code samples are very much appreciated!
I've taken both answers into account and built the following:
public static class IsolatedStorageExtensions
{
public static void SaveObject(this IsolatedStorage isoStorage, object obj, string fileName)
{
IsolatedStorageFileStream writeStream = new IsolatedStorageFileStream(fileName, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(writeStream, obj);
writeStream.Flush();
writeStream.Close();
}
public static T LoadObject<T>(this IsolatedStorage isoStorage, string fileName)
{
IsolatedStorageFileStream readStream = new IsolatedStorageFileStream(fileName, FileMode.Open);
BinaryFormatter formatter = new BinaryFormatter();
T readData = (T)formatter.Deserialize(readStream);
readStream.Flush();
readStream.Close();
return readData;
}
}
A wrapper POCO object that contains that data to be serialized:
[Serializable]
internal class DataStoreContainer
{
public DataStoreContainer()
{
UserIDs = new List<int>();
}
public List<int> UserIDs { get; set; }
}
To consume these extensions:
private IsolatedStorageFile _isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
private DataStoreContainer _data = new DataStoreContainer();
private const string FILENAME = "MyAppName.dat";
And in any method where you want to get the data :
_data = _isoStore.LoadObject<DataStoreContainer>(FILENAME);
To save the data:
_isoStore.SaveObject(_data, FILENAME);
Have you looked at Isolated Storage? It stores data in a local file, specific to that user (or to the application, depending on how you specify). You can easily serialize objects to and from the store because it's stream-based. It sounds like the perfect solution for your problem.
Since you state 10 items would be a lot I would vote for #1 or a variation of #1, Binary serialized... you don't seem to indicate that being able to read the data is important and binary data should give you smaller file sizes, though if 10 is a lot this still shouldn't be important.
That being said I enjoy what I've seen of db4objects.
Related
I am building a C# winforms application which needs to read in data from multiple files and allow the user to view/edit the data. There will be a large amount of data, so the user needs to be able to save their changes, close the program, and resume their work later.
I am struggling with the best approach for retaining this data after the user exits the program. I've followed a tutorial for data binding to objects, but in this tutorial the data is hardcoded into the Form_Load event and the changes are lost when you exit the program. The author alludes to preferring to use an object-based data source instead of a database for data binding, but doesn't describe on how/if he saves data after the user exits.
Is there a way to store the data in the object-based data source between sessions, without setting up a local database or manually writing to some type of file? Or must I set up a local database in order to save data?
As users Jimi and bhmahler mentioned in the comments, the concept I was looking for was Object Serialization.
I created the following method to save my data:
private void Serialize()
{
Stream s = File.Open("data.txt", FileMode.Open);
BinaryFormatter b = new BinaryFormatter();
List<Airplane> data = new List<Airplane>();
foreach (var a in bsAirplanes) //bsAirplanes is the bindingsource object
{
data.Add((Airplane)a);
}
b.Serialize(s, data);
s.Close();
}
And this method to load saved data:
private void Deserialize()
{
Stream s = File.OpenRead("data.txt");
BinaryFormatter b = new BinaryFormatter();
List<Airplane> data = (List<Airplane>) b.Deserialize(s);
foreach(Airplane a in data)
{
bsAirplanes.Add(a);
}
s.Close();
}
I also had to mark the Airplane class with the [Serializable()] attribute.
I am using a tabcontrol which I want to serialize and save . I am using this code but it gives that tabcontrol class is not marked as serializable . How to mark it serializable as I am not able to override the class . How to do it ?
using (Stream stream = File.Open("data.dat", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream,tabControl1);
}
It gives this error
System.Windows.Forms.TabControl not marked as serializable
Why don't serialize Controls and is there an alternative?
If you serialize a control, there are some problems:
You can't do it because System.Windows.Forms.TabControl not marked as serializable like you have seen.
If you will do it and only if it is allowed, are there a lot of properties and classes, interfaces, events etc. that are serialized with it, inherited from the classes above and that is not what you will.
The only way you could do it is by made a new class, bind all the values you will save with the properties and serialize that class.
[Serializable] // don't forget this! It will mark your class so you can serialize it.
public class BindingClass // p.s.: give this a better name!
{
public string Text { get; set; } // Bind whit a control of your tab control.
public float Number { get; set; }
public string ImageLocation { get; set; } // used for the image
public IEnumerable<object> ListOfString { get; set; } // used for a list
}
Code example
Text and numbers
Well for text and numbers it is easy. You can made an intense of your class and you can bind that. After it you can serialize it. An example:
BindingClass bc = new BindingClass();
bc.Text = textBox1.Text;
bc.Number = numbericUpDown.Value;
using (Stream stream = File.Open("data.dat", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream, bc);
}
Images
For images is it a little bit complex. You can serialize an image but it is also a bad thing to do that. Better is to save the image in your bin/debug folder of your project and serialize the path of that image. An example:
string imageLocation = Application.StartupPath + #"\myImage.jpg"
pictureBox1.Image.Save(imageLocation, ImageFormat.Jpeg);
// declare bc like code above.
bc.ImageLocation = imageLocation;
// serialize bc.
If the image already exists in the file, you can override it. But if you will work with histories, not a good thing... You can solve it by use the current date time as filename! Change your code with this:
string imageLocation = Application.StartupPath +
DateTime.Now.ToString("yyyyMMddHHmmss") + ".jpg"
Note: You can also use a blob service like Azure and Amazon (not free) or upload images to Imgur, Flickr or 9gag (patical free). Remark that there must be an internet connection between the client and server. You can upload by searching on Google how to do it.
List of string
For list of strings you can use this:
bc.ListOfString = comboBox1.Items;
Note
I haven't test the code. So if you have a problem with one the examples comment it and I will look at it, but try also to look on Google for a solution for your problem. Try it yourself, best way to learn...
Alternative for serialize (update 16-Jun-16)
Serializing is a save way to make your code unreadable for people. However that can give problems if you scale your application. The problem is also happen by Microsoft Word. The old .doc files are also serialized code, the new .docx files are zipped xml files and now it's easier to make .docx files.
Good alternatives are Json or XML.
I am making a basic platformer, my first ever game. I've run into a bit of a problem. So far the game only has one level, and it is loaded from a .txt file. However I'd like to have a sort of an Angry Birdish world/level selection screen.
The plan is to have an icon for each level visible, but only so far completed levels and the next one accessible. Also for the completed levels the score (stars, whatever) would be displayed under the icon.
I do not wish to load the levels from XML, at least not yet. Only the persistent world data that needs to be read AND written. I assume the easiest way is to load even the formatting of the level selection screen from XML, and not use the method i currently use (text files).
I could do this with text files, I suppose, but I really do not relish the idea of writing and sorting through the file. I then discovered that XML-files should be a bit less problematic in this regard. However additional problem rises from the fact tht I've never ever worked with XML-files before.
Could someone point me in a direction of a tutorial for this sort of things, or some sample you might have come accross that accomplishes at least relatively similar results. I don't expect anyone to do the coding for me, but if you have pointers or time and patience to provide a sample, I'd appreciate it a lot.
After some further digging and fumbling with tutorials for older XNA versions I managed to produce following save/load class:
namespace SaveLoadXML
{
class SaveLoad
{
public LevelInfo Load (int id)
{
LevelInfo level;
// Get the path of the save game
string fullpath = "World.xml";
// Open the file
FileStream stream = File.Open(fullpath, FileMode.OpenOrCreate,
FileAccess.Read);
try
{
// Read the data from the file
XmlSerializer serializer = new XmlSerializer(typeof(LevelInfo));
level = (LevelInfo)serializer.Deserialize(stream);
}
finally
{
// Close the file
stream.Close();
}
return (level);
}
public void Save (LevelInfo level, int id)
{
// Get the path of the save game
string fullpath = "World.xml";
// Open the file, creating it if necessary
FileStream stream = File.Open(fullpath, FileMode.OpenOrCreate);
try
{
// Convert the object to XML data and put it in the stream
XmlSerializer serializer = new XmlSerializer(typeof(LevelInfo));
serializer.Serialize(stream, level);
}
finally
{
// Close the file
stream.Close();
}
}
}
}
Now I started to think, is there a way to target a specific part of the XML-file, or is the writing always just from the start? Almost all of the examples I saw had a condition at the start: if the file exists, delete it and then write.
I assume I could (or even should?) make a list of LevelInfo objects and just load them all at once, as there is no real need to load a single LevelInfo anyway. On the saving however, do I need to load the previous state (old list) and then manipulate the list regarding the certain indexes involved, and then delete te file, and save it again.
This might open an easy way for the system to fail if something goes wrong in the saving or power fails for example. The whole file would be lost or corrupt. I suppose this ould be countered with using back-up file and then checking the integrity of the main file, but now it's starting to feel like quite a mountain to climb for a beginner like me.
Having tried this question on GameDev, I'll just clarify the main question here:
1) Can I save only info about one or two levels in the XML-file containing info for all levels? ie. can I use some indexing to point the write operation to a particular section that would then be overwritten/replaced.
2) If not, is there any way to safely load all info from file, delete file, save all info after modifying it where needed.
After some looking into this Json stuff, I've managed to successfully serialize test level information. However, de-serialization fails as I have a rectangle as a part of the object. Error is as follows:
Error converting value "{X:1 Y:1 Width:1 Height:1}" to type 'Microsoft.Xna.Framework.Rectangle'. Path '[0].Rectangle', line 6, position 46.
class LevelInfo
{
public int ID { get; set; }
public Vector2 Dimensions { get; set; }
public Vector2 Position { get; set; }
public Rectangle Rectangle { get; set; }
public int Stars { get; set; }
public string Text { get; set; }
}
class SaveLoadJSON
{
public static List<LevelInfo> Load()
{
List<LevelInfo> levels = new List<LevelInfo>();
using (StreamReader file = File.OpenText("World.json"))
{
JsonSerializer serializer = new JsonSerializer();
levels = (List<LevelInfo>)serializer.Deserialize(file, typeof(List<LevelInfo>));
}
return levels;
}
public static void Save(List<LevelInfo> levels)
{
if (File.Exists("World.json"))
{
File.Delete("World.json");
}
using (FileStream fs = File.Open("World.json", FileMode.CreateNew))
using (StreamWriter sw = new StreamWriter(fs))
using (JsonWriter jw = new JsonTextWriter(sw))
{
jw.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(jw, levels);
}
}
}
Is there a way to work around this? Preferably a relatively simple way for a simple beginner like me.
Or alternatively, is there a way to omit the rectangle information to begin with, and maybe add it later? If I input nothing to the rectangle, it still is added to Json-file with 0 values. I do need the rectangle info for the drawing.
So here comes the promised answer.
Personally I'd prefer using JSon for storing data, since it's a lot easier to work with than XML, and takes up less storage. What you're going to want to do, is make Data Models of your player, enemy, items, scene objects, etc.
Then, you'll want to JsonConvert.SerializeObject() a parent data model, which will contain all those things.
Save this in any file, and Deserialize it again upon load, and reconstruct all objects from scratch.
Alternatively, just have all properties in the classes you're working with already, be public. That way, JsonConvert will be able to actually serialize the entire model. Keep in mind, if you do this runtime, it will make more of a complete snapshot of the Levels current state. Aka. where the enemies are located, the health remaining and whatever else you may have.
I hope this answers your question.
I'm making a game now in C# (which is a Console Application) and its variables need to be saved.
I've tried using Settings but there's a big problem about it: If the file name is changed or the file is transferred to somewhere else, the Settings are lost.
So what is a good alternative to Settings for saving variables and retrieving them later in the application?
EDIT: I'd like to save the variables to a text file and retrieve it later, is it possible? If yes, then how?
And please don't suggest online servers, because I'm working on a singleplayer game without keeping tracks of the players whatsoever.
One simple way to store data of a fixed type is serialization with the BinaryFormatter class.
See the MSDN documentation for Binary Formatter. I've copied some of the relevant code here.
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
void SaveData()
{
// Create a hashtable of values that will eventually be serialized.
Hashtable addresses = new Hashtable();
addresses.Add("Jeff", "123 Main Street, Redmond, WA 98052");
addresses.Add("Fred", "987 Pine Road, Phila., PA 19116");
addresses.Add("Mary", "PO Box 112233, Palo Alto, CA 94301");
// To serialize the hashtable and its key/value pairs,
// you must first open a stream for writing.
// In this case, use a file stream.
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
// Construct a BinaryFormatter and use it to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, addresses);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
void LoadData()
{
// Declare the hashtable reference.
Hashtable addresses = null;
// Open the file containing the data that you want to deserialize.
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
try
{
BinaryFormatter formatter = new BinaryFormatter();
// Deserialize the hashtable from the file and
// assign the reference to the local variable.
addresses = (Hashtable) formatter.Deserialize(fs);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
// To prove that the table deserialized correctly,
// display the key/value pairs.
foreach (DictionaryEntry de in addresses)
{
Console.WriteLine("{0} lives at {1}.", de.Key, de.Value);
}
}
If your app structure is dynamic by it's nature, so it's name can be changed, location can be changed (even if , to be honest, don't understand reasons behind that) the only possibility I can see is relay in external source for retrieving or storing your config information.
In short: setup a server somewhere that holds your app configuration data, and on first startup try to reach that server, load file from it, read a data. If it fails, just load default information.
Good candidates could be : DropBox, SkyDrive, GoogleDrive, Box... find suitable C# API for any of them an store/read data you need. The only thing I would invite your attention to for this solution, is licensing. Keep an eye on it, and be sure that you can use it in your application in a way you decide to use it.
Saving the values out to a flat file...
Storing the values in an XML File, or a database file...
Windows Registry...
There are many places you can store information, and only experience will really teach you what to put where... To make an intelligent guess, you need to be familiar with all the approaches...
The only real option that isn't susceptible to the user intentionally changing the data stored on their computer, losing it due to changing machines, etc. would be to not store the data on their computer at all. Have a database or other server that you host that users connect to over the network which stores their data for them.
You may try to use the Isolated Storage
Isolated storage is not available for Windows Store apps. Instead, use
the application data classes in the Windows.Storage namespaces
included in the Windows Runtime API to store local data and files.
You may also try to use XML file to store the users setting and then store it in the SpecialFolder.ApplicationData directory.
You can also use the app.config file to save application-level settings
I have following code to read a file
StreamReader str = new StreamReader(File.Open(fileName, FileMode.Open, FileAccess.Read));
string fichier = str.ReadToEnd();
str.Close();
This is part of a asp.net webservice and has been working fine for an year now in production. Now with increasing load on server, customer has started getting "File already in use" error. That file is being read from this code and is never written to from application.
One problem that I clearly see is that we are not caching the contents of file for future use. We will do that. But I need to understand why and how we are getting this issue.
Is it because of multiple threads trying to read the file? I read that StreamReader is not thread safe but why should it be a problem when I am opening file in Read mode?
You need to open the file with read access allowed. Use this overload of File.Open to specify a file sharing mode. You can use FileShare.Read to allow read access to this file.
Anothr possible solution is to load this file once into memory in a static constructor of a class and then store the contents in a static read-only variable. Since a static constructor is guaranteed to run only once and is thread-safe, you don't have to do anything special to make it work.
If you never change the contents in memory, you won't even need to lock when you access the data. If you do change the contents, you need to first clone this data every time when you're about to change it but then again, you don't need a lock for the clone operation since your actual original data never changes.
For example:
public static class FileData
{
private static readonly string s_sFileData;
static FileData ()
{
s_sFileData = ...; // read file data here using your code
}
public static string Contents
{
get
{
return ( string.Copy ( s_sFileData ) );
}
}
}
This encapsulates your data and gives you read-only access to it.
You only need String.Copy() if your code may modify the file contents - this is just a precaution to force creating a new string instance to protect the original string. Since string is immutable, this is only necessary if your code uses string pointers - I only added this bit because I ran into an issue with a similar variable in my own code just last week where I used pointers to cached data. :)
FileMode just controls what you can do (read/write).
Shared access to files is handled at the operating system level, and you can request behaviors with FileShare (3rd param), see doc