How do I save/serialize a custom class to the settings file?
Asked Answered
U

5

24

I have a small class that holds two strings as follows:

    public class ReportType
    {
        private string displayName;
        public string DisplayName
        {
            get { return displayName; }
        }

        private string reportName;
        public string ReportName
        {
            get { return reportName; }
        }

        public ReportType(string displayName, string reportName)
        {
            this.displayName = displayName;
            this.reportName = reportName;
        }
    }

I want to save an instance of this class to my settings file so that I can do the following:

ReportType reportType = Settings.Default.SelectedReportType;

Googling seems to suggest that it is possible but there doesn't appear to be a clear guide anywhere for me to follow. I understand that some serialization is required but don't really know where to begin. Also, when I go into the Settings screen in Visual Studio and click "browse" under the Type column there is no option to select the current namespace which contains the ReportType class.

Unthrone answered 24/8, 2009 at 9:5 Comment(1)
use can use much easier approach: public string ReportName { get; private set; } -- if .NET 3+ is availableMauromaurois
U
19

OK I think that I have eventually worked it out. The first thing to do is to add the following attributes to each property of the ReportType class that needs to be serialised and inherit the class from ApplicationSettingsBase:

    public class ReportType : ApplicationSettingsBase
    {
        private string displayName;
        [UserScopedSetting()]
        [SettingsSerializeAs(System.Configuration.SettingsSerializeAs.Xml)]
        public string DisplayName
        {
            get { return displayName; }
        }

..............

and then, once you have rebuilt your assembly (important!) you can go into the settings screen and click browse and then type your namespace and class name in the text box at the bottom (e.g. Label_Creator.ReportType). The namespace and class name do not appear in the tree and so this part is not exactly obvious what you need to do which is why it is a bit confusing....

Unthrone answered 24/8, 2009 at 10:34 Comment(2)
How do you initialize the property then? Im trying to reproduce your steps but it keeps failing. When I call (using your case) Settings.Default.ReportType, it drops with a null reference exception...the problem may be that my properties use winforms Keys class instead of e.g. stringShannon
This solution did not work for me, not sure if it's due to version of VS existed at the time of posting. For the benefit of others, I'm posing another answer.Milkwhite
M
4

@Calanus solution did not work for me as-is (on Visual Studio 2015). The missing step is actually setting or getting from the actual settings. As for the original question, implementing a simple POCO can be achieved like this:

[Serializable]
public class ReportType
{
    public string DisplayName { get; set; }
    public string ReportName { get; set; }

    public ReportType() { }

    public ReportType(string displayName, string reportName)
    {
        DisplayName = displayName;
        ReportName = reportName;
    }
}

// the class responsible for reading and writing the settings
public sealed class ReportTypeSettings : ApplicationSettingsBase
{
    [UserScopedSetting]
    [SettingsSerializeAs(SettingsSerializeAs.Xml)]
    [DefaultSettingValue("")]
    public ReportType ReportType
    {
        get { return (ReportType)this[nameof(ReportType)]; }
        set { this[nameof(ReportType)] = value; }
    }
}

I have used for actually serialization a list:

[Serializable]
public class Schedule
{
    public Schedule() : this(string.Empty, DateTime.MaxValue)
    {
    }

    public Schedule(string path, DateTime terminationTime)
    {
        path = driverPath;
        TerminationTime = terminationTime;
    }

    public DateTime TerminationTime { get; set; }
    public string Path { get; set; }
}

public sealed class Schedules : ApplicationSettingsBase
{
    [UserScopedSetting]
    [SettingsSerializeAs(SettingsSerializeAs.Xml)]
    [DefaultSettingValue("")]
    public List<Schedule> Entries
    {
        get { return (List<Schedule>)this[nameof(Entries)]; }
        set { this[nameof(Entries)] = value; }
    }
}

Instantiate a Schedules (ReportTypeSettings) object. It will automatically read the settings. You can use Reload method to refresh. For instance:

ReportTypeSettings rts = new ReportTypeSettings();
rts.Reload();
rts.ReportType = new ReportType("report!", "report1");
rts.Save();

IMPORTANT NOTES:

  1. Note that UserScoped is intentionally used. ApplicationScoped behaves differently, so make sure you know what you are doing.
  2. The members are public (including the setter), and there's a default constructor even though it was needed by the code. However, serialization using XML didn't work properly. Due to time constraints I didn't investigate.
  3. You can change serialization to binary format as well. It will use BASE64 to store the data.
  4. All the settings is stored in LOCAL APP DATA folder, in a text file.
Milkwhite answered 21/5, 2016 at 19:59 Comment(2)
c'tor = constructorDamascene
Is it at all possible to simply make a custom object that can be serialized using the default Settings class, without having to create an entirely new ApplicationSettings-derived class?Sap
M
3

Just a bit more clear code then Charlie's

public class ReportType
{
  public static ReportType CreateDefaults()
  {
    return new ReportType
    {
       DisplayName =  ConfigurationManager.AppSettings["DefaultDisplayName"],
       ReportName = ConfigurationManager.AppSettings["DefaultReportName"]
    };
  }
}
Mauromaurois answered 24/8, 2009 at 9:18 Comment(2)
Actually I'm afraid I'm going to undo my upvote. Since this object is supposed to hold configuration data, I think it should be immutable so that it's state cannot be changed after construction. So my answer is better ;)Schilling
@Schilling - how is this any different than your anwser, other than syntax? I'm missing the "immutable" part -- shouldn't this be a get property instead?Laurinda
S
2

How about creating a static method which returns an instance of ReportType containing data from the config file. It's simpler and I don't think serializing is necessary.

public class ReportType
{
  public static ReportType GetDefaultSelectedReportType()
  {
    string displayName = ConfigurationManager.AppSettings["DefaultDisplayName"];
    string reportName = ConfigurationManager.AppSettings["DefaultReportName"];
    return new ReportType(displayName, reportName);
  }
  .
  .
  .
}
Schilling answered 24/8, 2009 at 9:14 Comment(2)
I could do that as a last resort but I actually have a number of different reports to save settings for and I will probably also be increasing the ReportType class to store more strings than the two shown here.Unthrone
Have a settings helper class with extension methods for each report then? A serialized object in a config will look a bit wierd I think.Schilling
S
0

I want to add in my own solution after having difficulty with the others. There's no need to do anything super special to get a custom class/Type with properties to show up in the Select a Type dialog box, and be serialized correctly by the .Net Application Settings system.

Create a Type/Class

Create a class as the original poster has, but be aware of two important details:

  • The class must be public, and contain a constructor without any parameters, to act as the default state of the class. I can't find any official documentation on this, but it has been my experience that this is required for successful serialization and recogniztion by the Select a Type dialog box.
  • The class must be contained in an assembly outside of the current assembly you are defining the Application Setting in (see here.) You can work around this and other quirks of the Designer by defining the Settings by code.

Prepare the Designer

In order for the new Type to be displayed in the Designer correctly, you need to first build your solution, then close the solution. Now reopen it, and your Type will now be browsable in the Settings designer.

This will be enough for .Net to be able to serialize your Type either directly to a string if possible, or to plain XML.

Advanced Serialization Control

You can also exercise advanced control over how your custom Type class is serialized without much extra code or overriding the base Settings class.

The LocalFileSettingsProvider (default used by Project Settings Designer) has a defined logic for how it attempts to serialize Settings properties. If you define a TypeConverter for your Type, then it will follow that - otherwise, it follows Xml Serialization rules. This means you can use Xml property attributes throughout your class to control how it is serialized to a file. For example, if you want to exclude certain properties entirely, you can hide them by making them private.

Sap answered 7/11, 2023 at 18:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.