How to store int[] array in application Settings
Asked Answered
F

8

102

I'm creating a simple windows Forms application using C# express 2008. I'm an experienced C++ developer, but I am pretty much brand new to C# and .NET.

I'm currently storing some of my simple application settings using the settings designer and code like this:

// Store setting  
Properties.Settings.Default.TargetLocation = txtLocation.Text;  
...  
// Restore setting  
txtLocation.Text = Properties.Settings.Default.TargetLocation;  

Now I'd like to store either an array of ints ( int[] ), or possibly a List of ints ( List< int > ), as a setting. However, I can't figure out how to do this. I've searched the documentation, stackoverflow, and google, and I cannot find a decent explanation of how to do this.

My hunch based on the sparse examples I've found is that I have to create a class that is serializable that wraps my array or List, and then I will be able to use that Type in the settings designer. However, I'm not sure exactly how to do this.

Frown answered 19/11, 2009 at 21:14 Comment(0)
E
149

There is also another solution - requires a bit of manual editing of the settings file, but afterwards works fine in VS environment and in the code. And requires no additional functions or wrappers.

The thing is, that VS allows to serialize int[] type by default in the settings file - it just doesn't allow you to select it by default. So, create a setting with desired name (e.g. SomeTestSetting) and make it of any type (e.g. string by default). Save the changes.

Now go to your project folder and open the "Properties\Settings.settings" file with text editor (Notepad, for example) Or you can open it in VS by right-clicking in Solution Explorer on " -> Properties -> Settings.settings", select "Open With..." and then choose either "XML Editor" or "Source Code (Text) Editor". In the opened xml settings find your setting (it will look like this):

<Setting Name="SomeTestSetting" Type="System.String" Scope="User">
  <Value Profile="(Default)" />
</Setting>

Change the "Type" param from System.String to System.Int32[]. Now this section will look like this:

<Setting Name="SomeTestSetting" Type="System.Int32[]" Scope="User">
  <Value Profile="(Default)" />
</Setting>

Now save changes and re-open project settings - voilà! - We have the setting SomeTestSetting with type System.Int32[] which can be accessed and edited through VS Settings Designer (values too), as well as in the code.

Entebbe answered 24/11, 2010 at 14:26 Comment(10)
Awesome. To enter something into setting using the editor in Visual Studio, you should paste something like this, this is for a string array, which was what I needed <?xml version="1.0" encoding="utf-16"?> <ArrayOfString xmlns:xsi="w3.org/2001/XMLSchema-instance" xmlns:xsd="w3.org/2001/XMLSchema"> <string>String1</string> <string>String2</string> </ArrayOfString>Cyst
+1 .. don't understand why this was not accepted answer.. no extra code required (just modify one line of existing xml) and you get type safety and full vs designer support!Opalopalesce
Chris, thanks :) The thing is, I've added my answer much later then the originally accepted answer (about a year later, actually :) ). Just sharing the experience though :)Entebbe
For anyone curious, the config file XML syntax for int[] would look like this (except pretty printed): <setting name="SomeTestSetting" serializeAs="String"><value><ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><int>1</int><int>2</int><int>3</int></ArrayOfInt></value></setting>Clergy
Agreed, this is a better answer, so I changed the mark.Frown
@Entebbe How do I initialize with my own values for this int array instead of default value?Industrialist
@Prabu, After editing the settings file manually, you can go back to the Settings tab in the Project Properties and edit Value there (Just click on the Value field and you will see ellipsis button at the end of it (...). Click on it and you will be able to specify your values).Entebbe
@Prabu, The values will be serialized in the settings file like this (it's an example with array {1,2,3}): <Setting Name="SomeTestSetting" Type="System.Int32[]" Scope="User"> <Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt; &lt;ArrayOfInt xmlns:xsi="w3.org/2001/XMLSchema-instance" xmlns:xsd="w3.org/2001/XMLSchema"&gt; &lt;int&gt;1&lt;/int&gt; &lt;int&gt;2&lt;/int&gt; &lt;int&gt;3&lt;/int&gt; &lt;/ArrayOfInt&gt;</Value> </Setting>Entebbe
Can I do the same with a string[] string array?Piazza
I did get it to work ok. But I also had to edit the designer cs file and adjust the property there as a string array else I could work with the value.Piazza
A
45

to store:

string value = String.Join(",", intArray.Select(i => i.ToString()).ToArray());

to re-create:

int[] arr = value.Split(',').Select(s => Int32.Parse(s)).ToArray();

Edit: Abel suggestion!

Atiana answered 19/11, 2009 at 21:21 Comment(4)
Ok, so this looks like you are serializing the data manually into a string, and vice-versa. I guess this would work, I could just store the data as a string setting. I thought of doing something like this, but I was looking for something more "clean" or "standard" or just more .NET'ish. I'll keep this in mind though, if nothing better crops up. Thanks.Frown
Hi, I would go with McKay's avenue then! (config section / section group)Atiana
I'm going to go with this because it is simple. Thanks for all the ideas everyone, I'm sure they will help down the road.Frown
Note: You'll need to have System.Linq added to your usings/imports for the above trick to work.Colvert
A
11

There is one other way to achieve this result that is a lot cleaner in usage but requires more code. My implementing a custom type and type converter the following code is possible:

List<int> array = Settings.Default.Testing;
array.Add(new Random().Next(10000));
Settings.Default.Testing = array;
Settings.Default.Save();

To achieve this you need a type with a type converter that allows conversion to and from strings. You do this by decorating the type with the TypeConverterAttribute:

[TypeConverter(typeof(MyNumberArrayConverter))]
public class MyNumberArray ...

Then implementing this type converter as a derivation of TypeConverter:

class MyNumberArrayConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext ctx, Type type)
    { return (type == typeof(string)); }

    public override bool CanConvertFrom(ITypeDescriptorContext ctx, Type type)
    { return (type == typeof(string)); }

    public override object ConvertTo(ITypeDescriptorContext ctx, CultureInfo ci, object value, Type type)
    {
        MyNumberArray arr = value as MyNumberArray;
        StringBuilder sb = new StringBuilder();
        foreach (int i in arr)
            sb.Append(i).Append(',');
        return sb.ToString(0, Math.Max(0, sb.Length - 1));
    }

    public override object ConvertFrom(ITypeDescriptorContext ctx, CultureInfo ci, object data)
    {
        List<int> arr = new List<int>();
        if (data != null)
        {
            foreach (string txt in data.ToString().Split(','))
                arr.Add(int.Parse(txt));
        }
        return new MyNumberArray(arr);
    }
}

By providing some convenience methods on the MyNumberArray class we can then safely assign to and from List, the complete class would look something like:

[TypeConverter(typeof(MyNumberArrayConverter))]
public class MyNumberArray : IEnumerable<int>
{
    List<int> _values;

    public MyNumberArray() { _values = new List<int>(); }
    public MyNumberArray(IEnumerable<int> values) { _values = new List<int>(values); }

    public static implicit operator List<int>(MyNumberArray arr)
    { return new List<int>(arr._values); }
    public static implicit operator MyNumberArray(List<int> values)
    { return new MyNumberArray(values); }

    public IEnumerator<int> GetEnumerator()
    { return _values.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator()
    { return ((IEnumerable)_values).GetEnumerator(); }
}

Lastly, to use this in the settings you add the above classes to an assembly and compile. In your Settings.settings editor you simply click the "Browse" option and select the MyNumberArray class and off you go.

Again this is a lot more code; however, it can be applied to much more complicated types of data than a simple array.

Allopatric answered 19/11, 2009 at 22:56 Comment(1)
Thanks this looks interesting. I'll try it out when I get the chance.Frown
P
3

Specify the setting as a System.Collections.ArrayList and then:

Settings.Default.IntArray = new ArrayList(new int[] { 1, 2 });

int[] array = (int[])Settings.Default.IntArray.ToArray(typeof(int));
Petrochemistry answered 19/11, 2009 at 21:37 Comment(0)
B
2

A simple solution is to set the default value of a setting to null in the property, but in the constructor check if the property is null and if so then set it to its actual default value. So if you wanted an array of ints:

public class ApplicationSettings : ApplicationSettingsBase
{
    public ApplicationSettings()
    {
        if( this.SomeIntArray == null )
            this.SomeIntArray = new int[] {1,2,3,4,5,6};
    }

    [UserScopedSetting()]
    [DefaultSettingValue("")]
    public int[] SomeIntArray
    {
        get
        {
            return (int[])this["SomeIntArray"];
        }
        set
        {
            this["SomeIntArray"] = (int[])value;
        }
    }
}

It feels kind of hacky, but its clean and works as desired since the properties are initialized to their last (or default) settings before the constructor is called.

Balcer answered 1/3, 2011 at 19:29 Comment(2)
The designer for application setting files autogenerates the code, and so a change like this would be overwritten anytime anyone used the designer, even accidentally.Cockfight
You should add the config portion, What would the config look like?Dialysis
A
1

Used System.Object.

Example:

byte[] arBytes = new byte[] { 10, 20, 30 };
Properties.Settings.Default.KeyObject = arBytes;

Extract:

arBytes = (byte[])Properties.Settings.Default.KeyObject;
Autogenous answered 28/10, 2012 at 6:16 Comment(1)
I tried using System.Object, but the settings were not persisting between session. (Yes it was a release build, stand-alone install, whatever you want to call it, I was not debugging using the IDE)Factorial
B
0

I think you are right about serializing your settings. See my answer to this question for a sample:

Techniques for sharing a config between two apps?

You would have a property which is an array, like this:

/// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
[XmlAttribute]
public int [] Numbers { get; set; }
Balikpapan answered 19/11, 2009 at 21:22 Comment(0)
D
0

Make some functions that convert a int array in a string, but between each put a character like " " (space).

So if the array is { 1,34,546,56 } the string would be "1 34 645 56"

Damalas answered 14/8, 2013 at 10:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.