How to store int[] array in application Settings

C#WinformsArraysSettings

C# Problem Overview


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.

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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!

Solution 3 - C#

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.

Solution 4 - C#

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));

Solution 5 - C#

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.

Solution 6 - C#

Used System.Object.

Example:

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

Extract:

arBytes = (byte[])Properties.Settings.Default.KeyObject;

Solution 7 - C#

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; }

Solution 8 - C#

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"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsidewinderguyView Question on Stackoverflow
Solution 1 - C#Jen-AriView Answer on Stackoverflow
Solution 2 - C#Mike Gleason jr CouturierView Answer on Stackoverflow
Solution 3 - C#csharptest.netView Answer on Stackoverflow
Solution 4 - C#João AngeloView Answer on Stackoverflow
Solution 5 - C#Russell BeardenView Answer on Stackoverflow
Solution 6 - C#SlavsView Answer on Stackoverflow
Solution 7 - C#Philip WallaceView Answer on Stackoverflow
Solution 8 - C#user1452079View Answer on Stackoverflow