Anyone able to tell me how to store a hastable in the .config file of a console/winforms app? I just have a few strings (20 of em) that represent a return code from a web method call... So I just thought I'd stick em in the config file and load em into a hashtable when the app starts... Then translate the return value to text to display to the user... Can't figure out what the XMLshould look like in the config file... So I thought I'd just write some code to store the hashtable in the config file to give myself a start on what the XML should look like... So I;ve got this code.. but it just doesn't modify the config file at all...
Code Block
Settings.Default.theReturnCodes = new Hashtable(); Settings.Default.theReturnCodes.Add("1", "Waiting for Confirmation."); Settings.Default.Save(); So, when I run the above .. nothing changes in the myApp.config file... I thought this code would save theReturnCodes in the config file... No? Thanks PJFINTRAX | | PJFINTRAX Wednesday, January 16, 2008 1:30 PM | No problem m8, but in case you are interested I got it working. You use a string to store the serialized dictionary and this string you can store in the settings file. So Try this (remember to add theReturnCodes as string in settings):
Code Block
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
.
.
.
.
Dictionary <int, string> dicReturnCodes = new Dictionary<int,string>();
private void btnStoreDictionary_Click(object sender, EventArgs e)
{
try
{
dicReturnCodes.Add(0, "Test 1");
dicReturnCodes.Add(1, "Test 2");
}
catch (Exception err)
{
MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
string tempString;
StringBuilder sb = new StringBuilder(null);
StringWriter sw = new StringWriter(sb);
Serialize(sw, dicReturnCodes);
tempString = sb.ToString();
Properties. Settings.Default.theReturnCodes = tempString;
Properties. Settings.Default.testString = "Changed It!\n" + DateTime.Now.ToString();
Properties. Settings.Default.Save();
}
private void btnReadDictionary_Click(object sender, EventArgs e)
{
StringReader sr = new StringReader(Properties.Settings.Default.theReturnCodes);
Deserialize(sr, dicReturnCodes);
if (dicReturnCodes.Count == 0)
MessageBox.Show("Settings(Dictionary) are empty, please store settings before reading", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show(dicReturnCodes[0].ToString());
if (Properties.Settings.Default.testString == null)
MessageBox.Show("Settings(String) are empty, please store settings before reading", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show(Properties.Settings.Default.testString, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public static void Serialize(TextWriter writer, IDictionary dictionary)
{
List<Entry> entries = new List<Entry>(dictionary.Count);
foreach (object key in dictionary.Keys)
{
entries.Add( new Entry(key, dictionary[key]));
}
XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
serializer.Serialize(writer, entries);
}
public static void Deserialize(TextReader reader, IDictionary dictionary)
{
dictionary.Clear();
XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
List<Entry> list = (List<Entry>)serializer.Deserialize(reader);
foreach (Entry entry in list)
{
dictionary[entry.Key] = entry.Value;
}
}
public class Entry
{
public object Key;
public object Value;
public Entry() { }
public Entry(object key, object value)
{
Key = key; Value = value;
}
}
Now its working and very tested | | Fábio Franco Thursday, January 17, 2008 11:43 AM | Is the scope oftheReturnCodes set to User? Is its Type set to Hashtable??
If both are correct then I would have no Idea why it wouldn't modify the file.
Note that by using "new Hashtable()" you are discarding all previously added values to the Hashtable
Regards,
Fábio | | Fábio Franco Wednesday, January 16, 2008 2:20 PM | Hi Fábio,
Yeah.. I set the scope by using the settings editor (Properties/settings.settings) and set the type to hashtable... But I couldn't get the editor to allow me to put any initial value into it...the object collection editor just comes up empty and all the buttons are greyed out so i can't add anything...
I've no idea what the xml should look like.. otherwise I'd just add it to the config file manually...
Thats why I wrote that code.. I just want to do it once to see what the xml loks like... Then I'll remove that code and edit the congfig file manually to put my strings in it.. and then just ahve code to read the strings from the hastable...
PJFINTRAX
| | PJFINTRAX Wednesday, January 16, 2008 2:56 PM | Well, first i think you should use Dictionary instead of Hashtable. Generic is a better option (Type safety)and has better performance for value types. Second I successfully ran this code:
Code Block
private void button3_Click(object sender, EventArgs e)
{
Properties. Settings.Default.theReturnCodes = new Dictionary<int,string>();
Properties. Settings.Default.theReturnCodes.Add(1, "Waiting for confirmation.");
Properties. Settings.Default.Save();
}
private void button4_Click(object sender, EventArgs e)
{
MessageBox.Show(Properties.Settings.Default.theReturnCodes[1].ToString());
}
There is no reason for you to manually write or read in the XML file. VS will do that for you, and your code will be cleaner and easier to use. You just run this kind of code once to write to the XML file and then if you don't need to write more values you can remove the writing code and use just the reading code.
Regards,
Fábio | | Fábio Franco Wednesday, January 16, 2008 4:07 PM | Hi Fabio, Thank youv ery much for going to so much trouble to answer my question. I really appreciate it.
Can I ask, did you get something actually written to the config file? What did it look like?
How did you add theReturnCodes to the settings in the firlst place? Was it through the settings editor? If so, what type did you give it? And did you give it a value?
Thanks PJFINTRAX
| | PJFINTRAX Wednesday, January 16, 2008 4:18 PM |
| PJFINTRAX wrote: |
Hi Fabio, Thank youv ery much for going to so much trouble to answer my question. I really appreciate it.
| |
Hey, no problem, always glad to help
| PJFINTRAX wrote: |
Can I ask, did you get something actually written to the config file? What did it look like?
| |
The wierd thing is that I don't see anything written in the config file I don't know where it is storing the settings. all I get in the config file is:
Code Block
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
</configuration>
| PJFINTRAX wrote: |
How did you add theReturnCodes to the settings in the firlst place? Was it through the settings editor? If so, what type did you give it? And did you give it a value?
| | Well, since the settings editor wont allow you to add a dictionary (at least in my case) I added it in my Form's file so the designer won't change it:
Code Block
namespace TestApp.Properties
{
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
[ global::System.Configuration.UserScopedSettingAttribute()]
[ global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Generic.Dictionary<int, string> theReturnCodes
{
get
{
return ((global::System.Collections.Generic.Dictionary<int, string>)(this["theReturnCodes"]));
}
set
{
this["theReturnCodes"] = value;
}
}
}
}
This way you can use it the same way you use using the settings editor. And I just gave a value on the first time I ran the application and then I deleted the code that added the values. Now just retreive the values.
Any doubts, just ask.
Regards,
Fábio
| | Fábio Franco Wednesday, January 16, 2008 5:05 PM | Ahhh!!! So you don't see anything changing in the .config file at all ? Okay.. now... thats what I'm having the problem with... it just doesn't make sense... I;ve been thinkin that this hasn't worked because there was nothing changing in the .config file... So you don't know where its storing this stuff either eh? This is very odd.... I made things real simple... just to see if I could figure out what is going on... I just have a setting.. just an ordinary string... and I read it.. and I set it.. So the code is like this
Code Block
string mystring = Properties.Settings.Default.theString; mystring = "Changed it"; Properties.Settings.Default.theString = mystring; Properties.Settings.Default.Save(); And the config file looks like this:
Code Block
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="HBOS_DCC_Download.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> </configSections> <userSettings> <HBOS_DCC_Download.Properties.Settings> <setting name="theString" serializeAs="String"> <value>this is the initial string</value> </setting> </HBOS_DCC_Download.Properties.Settings> </userSettings> </configuration> When I run the code the first time....the value of mystring after the execution of the first line of code is "this is the initial string" ... then the rest of the code executes. Looking at the config file at that point.. the config file hasnot changed....the setting for theString still reads "this is the initial string" .. so it appears that the Properties.Settings.Default.Save() had no effect... Now, here's where it gets really weird...When I run the code the second time ... the value that mystring gets in the first line.. is "Changed it" ...... and god only knows where THAT came from!!... PJ | | PJFINTRAX Wednesday, January 16, 2008 5:19 PM | Yes, I'm just as confused as you are PJ, I was sure the value would be in the config file, I don't know where it goes. But you see that Save works because you get the "Changed it" string on the first line of code right? I know it gets stored somewhere I just don't know where, my config file never gest modified, not eve like yours that at least has the default value. So this is something I will try to investigate to try figuring out where that gets stored. | | Fábio Franco Wednesday, January 16, 2008 5:34 PM | Alright, I figured out where the settings are stored. It should be in:
C:\Documents and Settings\YourUserName\Local Settings\Application Data\YourAppName\SomeWeirdDirectoryName\1.0.0.0\user.config
Then I realized that it must be like that so the settings are relative to the user logged on.
Regards,
Fábio | | Fábio Franco Wednesday, January 16, 2008 6:46 PM | Hi there again,
You're right.. it makes sense to have the user settings stored in a user specific location allright eh?
I found the file and of course, as you said it would be, the changed data is there ....
But it only seems to work with strings... i could not get it to work with a hastable...
I'll try to see if it will work with the code you supplied for the dictionary...
Don't think it will work without some initial values tho...
Thanks Fabio.. its been very interesting...
PJFINTRAX///
| | PJFINTRAX Thursday, January 17, 2008 9:34 AM | One last thing.. heh... I added your code.. for the dictionary.. And now I get the error Error 17 'HBOS_DCC_Download.Properties.Settings' does not contain a definition for 'theReturnCodes' So .. it would appear that I need to add theReturnCodes to the settings file before it will allow me to write to it.. You did this in the example earlier... I'm just wondering how you got it into the settings... or if you needed to add it? Thanks PJ My code is looking like this now: (the compiler barfs on the first line that tries to assign a new Dictionary to theReturnCodes...)
Code Block: program.cs
using System; using System.Collections; using System.Collections.Generic; using System.Configuration; using System.Text;
namespace HBOS_DCC_Download { class Program { static void Main(string[] args) { string mystring = Properties.Settings.Default.theString; mystring = "ohlapalomablanca-Changed"; Properties.Settings.Default.theString = mystring;
Properties.Settings.Default.theReturnCodes = new Dictionary<int, string>();
Properties.Settings.Default.theReturnCodes.Add(1, "Waiting for confirmation.");
Properties.Settings.Default.Save(); } } }
namespace HBOS_DCC_DOWNLOAD.Properties {
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Generic.Dictionary<int, string> theReturnCodes {
get {
return ((global::System.Collections.Generic.Dictionary<int, string>)(this["theReturnCodes"]));
}
set {
this["theReturnCodes"] = value;
}
}
}
} The settings file is just:
Code Block
<?xml version='1.0' encoding='utf-8'?> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="HBOS_DCC_Download.Properties" GeneratedClassName="Settings"> <Profiles /> <Settings> <Setting Name="theString" Type="System.String" Scope="User"> <Value Profile="(Default)">this is the initial string</Value> </Setting> </Settings> </SettingsFile> | | PJFINTRAX Thursday, January 17, 2008 9:56 AM | I think you are referencing it the wrong way. It shouldn't be:
HBOS_DCC_Download.Properties.Settings.theReturnCodes
it should be:
HBOS_DCC_Download.Properties.Settings.Default.theReturnCodes
Also I was reading a bit and realized that HashTables and Dictionaries are not natively XmlSerializable, so it must be done something else to make it XmlSerializable. There is this article:
XML Serializing a Hashtable or generic Dictionary
Maybe now it will work, I myself will do it here as what I thought was working, wasn't really working.
Regards,
Fábio | | Fábio Franco Thursday, January 17, 2008 10:40 AM | Heh heh.. Yeah.. I kinda suspected that code wasn't workin...
But no worries.. its great to have some help at all...
I think I'm trying to do too much by storing the list of return codes in the settings file .. its just too much hassle.. I figure I might just try storing a dictionary in its own file altogether... and forget about the settings file...
So i was having a look around at serialising dictionary objects myself... Found this: http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx
I'll have to spend some time learning about xml serialisation properly before I can understand it.. heh...
Thanks for your patience Fabio... PJFINTRAX
| | PJFINTRAX Thursday, January 17, 2008 10:47 AM | No problem m8, but in case you are interested I got it working. You use a string to store the serialized dictionary and this string you can store in the settings file. So Try this (remember to add theReturnCodes as string in settings):
Code Block
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
.
.
.
.
Dictionary <int, string> dicReturnCodes = new Dictionary<int,string>();
private void btnStoreDictionary_Click(object sender, EventArgs e)
{
try
{
dicReturnCodes.Add(0, "Test 1");
dicReturnCodes.Add(1, "Test 2");
}
catch (Exception err)
{
MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
string tempString;
StringBuilder sb = new StringBuilder(null);
StringWriter sw = new StringWriter(sb);
Serialize(sw, dicReturnCodes);
tempString = sb.ToString();
Properties. Settings.Default.theReturnCodes = tempString;
Properties. Settings.Default.testString = "Changed It!\n" + DateTime.Now.ToString();
Properties. Settings.Default.Save();
}
private void btnReadDictionary_Click(object sender, EventArgs e)
{
StringReader sr = new StringReader(Properties.Settings.Default.theReturnCodes);
Deserialize(sr, dicReturnCodes);
if (dicReturnCodes.Count == 0)
MessageBox.Show("Settings(Dictionary) are empty, please store settings before reading", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show(dicReturnCodes[0].ToString());
if (Properties.Settings.Default.testString == null)
MessageBox.Show("Settings(String) are empty, please store settings before reading", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show(Properties.Settings.Default.testString, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public static void Serialize(TextWriter writer, IDictionary dictionary)
{
List<Entry> entries = new List<Entry>(dictionary.Count);
foreach (object key in dictionary.Keys)
{
entries.Add( new Entry(key, dictionary[key]));
}
XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
serializer.Serialize(writer, entries);
}
public static void Deserialize(TextReader reader, IDictionary dictionary)
{
dictionary.Clear();
XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
List<Entry> list = (List<Entry>)serializer.Deserialize(reader);
foreach (Entry entry in list)
{
dictionary[entry.Key] = entry.Value;
}
}
public class Entry
{
public object Key;
public object Value;
public Entry() { }
public Entry(object key, object value)
{
Key = key; Value = value;
}
}
Now its working and very tested | | Fábio Franco Thursday, January 17, 2008 11:43 AM |
|