I have a form with information in it that the user enters, i want to save this to XML... i'm fairly new to programming but have read XML is the best thing to use. How would i go about it? If it helps im using Sharp Develop as an IDE. Current it has 10 text boxes and 10 datetimepickers.
The easiest thing would be to create a class that stores those 10 values as properties and use xml serialization to convert it to XML, then store it to the file system.
Here's a tutorial: http://www.switchonthecode.com/tutorials/csharp-tutorial-xml-serialization
More Detail:
This is super basic Object Oriented/Windows Forms stuff.
Create a Class that stores each of the values:
public class Values{
public string YourFirstValue { get; set;}
public DateTime YourSecondValue { get; set;}
...
}
and of course you'd want names that map to their actual meanings, but these should suffice for now.
Then, when clicking a button on your form, store the values in that class:
void Button1_OnClick(object sender, EventArgs args){
Values v = new Values();
v.YourFirstValue = this.FirstField.Text;
v.YourSecondValue = this.YourSecondField.Value
...
SaveValues(v);
}
Then implement the SaveValues method to serialize the xml using XmlSerializer for the serialization and StreamWriter to store the result to a file.
public void SaveValues(Values v){
XmlSerializer serializer = new XmlSerializer(typeof(Values));
using(TextWriter textWriter = new StreamWriter(#"C:\TheFileYouWantToStore.xml")){
serializer.Serialize(textWriter, movie);
}
}
Related
I have a pair of apps. One is a game for young kids. The other is a tool for use by the parents. I separate the apps in this way to keep the UI as simple as possible for the kids.
One part of the parent app is to control specific settings of the kids' app. I've just been using Settings.settings for most of my settings up to now, but can't see a simple way for the parent app to access and change the settings in the kids' app (apart from a rather kludgy back-door using XML).
Is there a way, or alternatively, is there another place I should consider keeping my shared settings?
I like the ease of two-way binding for managing settings via a dialog, but could survive without that if necessary.
FWIW: Both apps do use a common DLL where a lot of common code resides. Maybe there's a way of leveraging that?
You can save the settings to a file into a common folder like the below (eg C:\ProgramData\yourfolder)
String DataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\yourfolder";
and access the settings file from both applications. No need of a database and easy of use.
The settings file can be a simple json or whatever you feel comfortable.
In the end, I went with having a class that contains the shared settings. Here's the main part of it:
public class AppConfig
{
public int SaveSize = 1000;
//... other shared settings here
static private bool ready = false;
static private AppConfig instance;
static public AppConfig Instance
{
get
{
if (!ready)
{
ReadFromFile();
}
return instance;
}
}
static private readonly string filename = Common.AppDataFileName("config.xml");
static private void ReadFromFile()
{
ready = true;
if (!File.Exists(filename))
{
instance = new AppConfig();
return;
}
XmlSerializer serializer = new XmlSerializer(typeof(AppConfig));
using (FileStream fs = new FileStream(filename, FileMode.Open))
{
instance = (AppConfig)serializer.Deserialize(fs);
}
}
static public void Save()
{
XmlSerializer serializer = new XmlSerializer(typeof(AppConfig));
using (TextWriter writer = new StreamWriter(filename))
{
serializer.Serialize(writer, Instance);
}
}
}
Because you have to have an instance for serialization, I instantiate that on-demand and then refer to it as needed. E.g.:
int size = AppConfig.Instance.SaveSize;
Don't know if this is the best way, but it works for my needs for now. If it is a bad way to do this, don't be shy to comment below. I'm here to learn!
I have a C# application with a settings field that is a collection of custom objects.
When I start the application I create some class instances that take instances from the settings entry collection and keep a reference to them internally and modify them. While debugging I saw that changes done through these external references are not reflected when I callSettings.Default.Save() but changes done by directly accessing the properties likeSettings.Default.<property> work fine.
I looked up the code responsible for Save() and saw that the implementation actually checks a SettingsPropertyValue.IsDirty field to decide whether to or not to serialize it. Of course when I access the external references to the objects in the settings field that value is not set.
Is there any lightweight solution to this?
I don't think I'm the first person to encounter this. One way I can think of is implementing the IsDirty property in the collections I serialize and add INotifyPropertyChanged interface event PropertyChanged for all the contained instances so that the container is being notified of changes and can reflect them to the actual settings property. But that means wrapping each of the settings classes around with this logic. So what I am asking for is if there is a lightweight solution to this found by anyone else who has encountered this issue.
Example
Consider this class:
namespace SettingsTest
{
[DataContract(Name="SettingsObject")]
public class SettingsObject
{
[DataMember]
public string Property { get; set; }
}
}
And the following program:
namespace SettingsTest
{
class Program
{
static void Main(string[] args)
{
var settingsObjects = new List<SettingsObject>();
var settingsObject = new SettingsObject{Property = "foo"};
settingsObjects.Add(settingsObject);
Settings.Default.SettingsObjects = settingsObjects;
Settings.Default.Save();
settingsObject.Property = "bar";
Settings.Default.Save();
}
}
}
After the second Save() call the final output in user.config file is:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<userSettings>
<SettingsTest.Settings>
<setting name="SettingsObjects" serializeAs="Xml">
<value>
<ArrayOfSettingsObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SettingsObject>
<Property>foo</Property>
</SettingsObject>
</ArrayOfSettingsObject>
</value>
</setting>
</SettingsTest.Settings>
</userSettings>
</configuration>
As you can see the modification to the property via the external reference to the SettingsObject instance was not persisted.
You seem to be covering a bunch of issues with this question. As you correctly said "changes done by directly accessing the properties likeSettings.Default. work fine." Modifying in memory references will not cause the settings to be updated automatically as you wanted. INotifyProperty changed is one solution.
If you want a quick and dirty method of serializing these objects into your settings file you can use the following code.
Objects involved:
/// <summary>Generic class to support serializing lists/collections to a settings file.</summary>
[Serializable()]
public class SettingsList<T> : System.Collections.ObjectModel.Collection<T>
{
public string ToBase64()
{
// If you don't want a crash& burn at runtime should probaby add
// this guard clause in: 'if (typeof(T).IsDefined(typeof(SerializableAttribute), false))'
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
byte[] buffer = new byte[(int)stream.Length];
stream.Read(buffer, 0, buffer.Length);
return Convert.ToBase64String(buffer);
}
}
public static SettingsList<T> FromBase64(string settingsList)
{
using (var stream = new MemoryStream(Convert.FromBase64String(settingsList)))
{
var deserialized = new BinaryFormatter().Deserialize(stream);
return (SettingsList<T>)deserialized;
}
}
}
[Serializable()]
public class SettingsObject
{
public string Property { get; set; }
public SettingsObject()
{
}
}
The main method demonstrates the issue you are facing, with a solution.
class Program
{
static void Main(string[] args)
{
// Make sure we don't overwrite the previous run's settings.
if (String.IsNullOrEmpty(Settings.Default.SettingsObjects))
{
// Create the initial settings.
var list = new SettingsList<SettingsObject> {
new SettingsObject { Property = "alpha" },
new SettingsObject { Property = "beta" }
};
Console.WriteLine("settingsObject.Property[0] is {0}", list[0].Property);
//Save initial values to Settings
Settings.Default.SettingsObjects = list.ToBase64();
Settings.Default.Save();
// Change a property
list[0].Property = "theta";
// This is where you went wrong, settings will not be persisted at this point
// because you have only modified the in memory list.
// You need to set the property on settings again to persist the value.
Settings.Default.SettingsObjects = list.ToBase64();
Settings.Default.Save();
}
// pull that property back out & make sure it saved.
var deserialized = SettingsList<SettingsObject>.FromBase64(Settings.Default.SettingsObjects);
Console.WriteLine("settingsObject.Property[0] is {0}", deserialized[0].Property);
Console.WriteLine("Finished! Press any key to continue.");
Console.ReadKey();
}
}
So all I'm doing is storing your whole list of objects as a base64 encoded string. Then we deserialize on the next run.
From what I understand of the question you want to only hold in memory references without hitting the settings object. You could simply run some code at the end of main to persist this list and any other setting you need to. Any changes will still be in memory as long as you hold a reference the object & will remain around until you terminate the application.
If you need the settings to be saved while the application is running just create a Save() method of your own & call it from the end of Main() as well when the user performs an action that requires saving the settings. e.g.
public static void SaveSettings(SettingsList list)
{
Settings.Default.SettingsObjects = list.ToBase64();
Settings.Default.Save();
}
Edit: One caveat as mentioned in the comments below.
From my benchmarks this method is very slow, meaning it's not a good idea to persist large object lists in settings this way. Once you have more than a handful of properties you might want to look at an embedded database like SQLite. A CSV, INI or XML file could also be an option here for large numbers of trivial settings. One benefit of simpler storage formats is easy modification by non-developers eg csv in excel. Of course this may not be what you want ;)
<reportParameterTestSettings>
<Report uri="/standard/Project-Iteration-WorkItem-MultiFT">
<ReportParameters>
<name>WorkItemType</name>
<name>CurrentUser</name>
<name>SelectedDate</name>
<name>TopIteration</name>
<name>TopTeam</name>
<name>ExecutionIteration</name>
<name>ShowIteration</name>
<name>SelectedExecutionIteration</name>
<name>SelectedShowIteration</name>
<name>ExecutionStartDate</name>
<name>ShowEndDate</name>
<name>Holiday</name>
<name>JobRole</name>
<name>EnabledFeature</name>
</ReportParameters>
</Report>
</reportParameterTestSettings>
How can i make this xml such that is it doesn't have redundant tags such as name. Also this example consists of only one Report. In reality there would be more than 20 reports. I was thinking of adding all parameters as one string comma separated and then doing a split on it. Is there a better way to do it?
Are you creating this file? A little confused by what you're asking but you should be able to do something like this
use XML Serialization
using System.XML.Serialization
public class reportParameterTestSettings
{
[XmlElement("Report")]
public List<ReportParameters> Report { get; set; }
[XmlAttribute("Uri")]
public string Uri { get; set; }
}
public class ReportParameters
{
public string WorkItemType { get; set; }
public string CurrentUser { get; set; }
...etc ...etc
}
Write it to an xml file
reportParameterTestSettings data = new reportParameterTestSettings();
//add your items to data
XmlSerializer writer = new XmlSerializer(typeof(reportParameterTestSettings));
using (FileStream file = File.Create(#"c:\file.xml"))
{
writer.Serialize(file, data);
}
Reading your xml file
reportParameterTestSettings data;
XmlSerializer reader = new XmlSerializer(typeof(reportParameterTestSettings));
using (FileStream file = File.OpenRead(#"c:\file.xml"))
{
data = reader.Deserialize(file) as reportParameterTestSettings;
}
You should then end up with a file that looks like
<reportParameterTestSettings>
<Report Uri=stuff>
<ReportParameters>
<WorkItemType>Stuff here</WorkItemType>
<CurrentUser>Stuff here</CurrentUser>
...etc ...etc
</ReportParameters>
</Report>
<Report Uri=stuff>
<ReportParameters>
<WorkItemType>Stuff here</WorkItemType>
<CurrentUser>Stuff here</CurrentUser>
...etc ...etc
</ReportParameters>
</Report>
</reportParameterTestSettings>
The newboston community has some great tutorials on xml in C#:
starting with this tutorial move on:http://thenewboston.org/watch.php?cat=15&number=109
I think your current structure is simple enough already. In fact it's very similar to having multiple rows in a database table.
In terms of querying your xml, I recommend looking at linq to xml http://msdn.microsoft.com/en-us/library/bb387061.aspx
In my C# program that is made with Visual Studio 2010 and uses WinForms, I would like the program to save state of some checkboxes and textboxes so the next time program will be loaded they are checked or unchecked as theire last run's state. Also same with strings inside textboxes and etc...
What will be the proper way to achieve this? Is there a built in stuff in .NET? Any tips and code snippets would be appriciated!
Thanks
You'd probably want to look at reading the relevant values from your UI during the FormClosing event, and then saving them into User Settings.
Have a look at: http://codehill.com/2009/01/saving-user-and-application-settings-in-winforms/
I would bind the value to user settings, and saving the configuration OnClose event.
One way to do this is using an XML configuration file and serializing it:
ConfigManager.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyApplication
{ [ Serializable() ]
public class ConfigManager
{
private int removeDays = 7;
public ConfigManager() { }
public int RemoveDays
{
get
{
return removeDays;
}
set
{
removeDays = value;
}
}
}
somewhere in your application
private ConfigManager cm;
private XmlSerializer ser;
...
Then you have to load the configuration:
private void LoadConfig()
{
try
{
cm = new ConfigManager();
ser = new XmlSerializer(typeof(ConfigManager));
filepath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + cm.filepath;
if (File.Exists(filepath))
{
FileStream fs = new FileStream(filepath, FileMode.Open);
cm = (ConfigManager)ser.Deserialize(fs);
// do something
}
} catch (Exception ex) { }
}
To save it:
XmlSerializer ser;
ConfigManager cm;
...
private void saveConfig()
{
try {
cm.RemoveDays = 6;
TextWriter tw = new StreamWriter(filepath, false);
ser.Serialize(tw, cm);
tw.Close();
} catch (Exception ex)
}
You asked very broad question. there are two ways to look at it.
1) If you have a need to persist application level configuration, your best bet is to use Application Settings. One can serialize program settings the user has done using your app, and restore them after the program has restarted. This works with WinForms and WPF:
2) If you need user level persistence, you need user settings.
Also, you can create custom class that implements that stores all of the configuration properties that you need.
Implement ISerializable and mark it [Serializable]. You could just mark it [Serializable], but if you add new properties in the future, you'll run into deserialization problems.
Add a Version property.
Add two static methods: Load and Save. These methods use IsolatedStorage to deserialize/serialize your configuration class to disk. You can use any kind of serialization you want - I use binary. Why not XML? Because binary is faster and users never need to get into these files. I used to do this for .net 2.0.
I've been storing collections of user settings in the Properties.Settings.Default object and using the Visual Studio settings designer (right-click on your project, click on Properties and then click on the Settings tab) to set the thing up. Recently, several users have complained that the data this particular setting tracks is missing, randomly.
To give an idea (not exactly how I do it, but somewhat close), the way it works is I have an object, like this:
class MyObject
{
public static string Property1 { get; set; }
public static string Property2 { get; set; }
public static string Property3 { get; set; }
public static string Property4 { get; set; }
}
Then in code, I might do something like this to save the information:
public void SaveInfo()
{
ArrayList userSetting = new ArrayList();
foreach (Something s in SomeCollectionHere) // For example, a ListView contains the info
{
MyObject o = new MyObject {
Property1 = s.P1;
Property2 = s.P2;
Property3 = s.P3;
Property4 = s.P4;
};
userSetting.Add(o);
}
Properties.Settings.Default.SettingName = userSetting;
}
Now, the code to pull it out is something like this:
public void RestoreInfo()
{
ArrayList setting = Properties.Settings.Default.SettingName;
foreach (object o in setting)
{
MyObject data = (MyObject)o;
// Do something with the data, like load it in a ListView
}
}
I've also made sure to decorate the Settings.Designer.cs file with [global::System.Configuration.SettingsSerializeAs(global::System.Configuration.SettingsSerializeAs.Binary)], like this:
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SettingsSerializeAs(global::System.Configuration.SettingsSerializeAs.Binary)]
public global::System.Collections.ArrayList SettingName
{
get {
return ((global::System.Collections.ArrayList)(this["SettingName"]));
}
set {
this["SettingName"] = value;
}
}
Now, randomly, the information will disappear. I can debug this and see that Properties.Settings.Default is returning an empty ArrayList for SettingName. I would really rather not use an ArrayList, but I don't see a way to get a generic collection to store in this way.
I'm about to give up and save this information using plain XML on my own. I just wanted to verify that I was indeed pushing this bit of .NET infrastructure too far. Am I right?
I couldn't find an answer as to why these settings were disappearing and since I kept on happening, I ended up storing the complicated sets of settings separately in an XML file, manually serializing and deserializing them myself.
I had a very similar experience when using the SettingsSerializeAs Binary Attribute in the Settings Designer class. It worked in testing, but some time later it failed to restore the property values.
In my case there had been subsequent additions to the Settings made via the designer. Source control history showed that the SettingsSerializeAs Attribute had been removed from Settings.Designer.cs without my knowledge.
I added the following code to check that the attribute hadn't been accidentally lost it the equivalent of the RestoreInfo() method.
#if(DEBUG)
//Verify that the Property has the required attribute for Binary serialization.
System.Reflection.PropertyInfo binarySerializeProperty = Properties.Settings.Default.GetType().GetProperty("SettingName");
object[] customAttributes = binarySerializeProperty.GetCustomAttributes(typeof(System.Configuration.SettingsSerializeAsAttribute), false);
if (customAttributes.Length != 1)
{
throw new ApplicationException("SettingsSerializeAsAttribute required for SettingName property");
}
#endif
Also, only because it is missing from your example, don't forget to call Save. Say after calling SaveInfo().
Properties.Settings.Default.Save();
When using the Settings feature with the User scope, the settings are saved to the currently logged in user's Application Data (AppData in Vista/7) folder. So if UserA logged in, used your application, and then UserB logged in, he wouldn't have UserA's settings loaded, he would have his own.
In what you're trying to accomplish, I would suggest using the XmlSerializer class for serializing an list of objects. The use is pretty simple:
To serialize it:
ArrayList list = new ArrayList();
XmlSerializer s = new XmlSerializer(typeof(ArrayList));
using (FileStream fs = new FileStream(#"C:\path\to\settings.xml", FileMode.OpenOrCreate))
{
s.Serialize(fs, list);
}
To deserialize it:
ArrayList list;
XmlSerializer s = new XmlSerializer(typeof(ArrayList));
using (FileStream fs = new FileStream(#"C:\path\to\settings.xml", FileMode.Open))
{
list = (ArrayList)s.Deserialize(fs);
}
From your example I don't see anything incorrect about what your trying to do. I think the root of the problem your describing might be your assembly version changing? User settings do not auto-magically upgrade themselves (at least I couldn't get them to).
I can appreciate your plight, I went through this a few months ago. I coded up a UserSettings class that provided standard name/value pair collections (KeyValueConfigurationElement) under a named group heading something like the following:
<configSections>
<section name="userSettings" type="CSharpTest.Net.AppConfig.UserSettingsSection, CSharpTest.Net.Library"/>
</configSections>
<userSettings>
<add key="a" value="b"/>
<sections>
<section name="c">
<add key="a" value="y"/>
</section>
</sections>
</userSettings>
Anyway see if this meets your needs or provides some insight into implementing a custom ConfigurationSection to allow what you need.
Oh yea, the code is here:
http://csharptest.net/browse/src/Library/AppConfig