When using a custom type in .csproj Application Settings (Settings.settings), how can default values be set? [closed]
Asked Answered
Z

1

-1

Currently I am working on a project, where I want to store a custom class in Application Settings, and this is working how I want it to work. If I edit the new setting, it can be Saved, and the user.config updates with the changes.

I want to define some default values in the settings.settings, but I don't know where to do it. What do I have to do?

example settings

Ideally, I want to see an initial user.config look something like this, where Part is the custom type:

<Setting Name="Name" Type="Project_Config.Part" Scope="User">
    <value Profile="(Default)>">
        <Part xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <display_name>Extra 3</display_name>
            <work_name>Sonstiges3</star_name>
            <folder_path />
        </Part>
    </value>
</Setting>
Zama answered 21/10, 2020 at 15:48 Comment(4)
If you downvote please explain me why I am pretty new to C#Zama
this is related to you being new not to c#, but to Stackoverflow. Question is missing code for us to reproduce the error and give answerPopulation
Thanks for the answere, I added how I set the default Values in the settings.settingsZama
now there is different issue. I am familiar with c#, but not with XML you provided. maybe you can add more tags to the question. so people who are familiar with technology and subscribed to specific tags take a lookPopulation
C
1

It seems the question should really be phrased like this:

How can I define default values when I use a custom type in my project's Properties\Settings.setting definitions?

Per comments to the original version of this answer, it seems the XML shown in the question is not really relevant - what's really important is that default values get populated.

(Of course, correct me if I'm wrong, OP.)

Step 1: Define a custom type

Any class will do. But it must have a parameterless constructor. For this example, I just made a Part class with three string properties.

Step 2: Create a TypeConverter for the custom type

This allows the underlying .NET classes to convert a string into this custom type.

This is the 'magic' step, because any default value entered into the Settings designer is always auto-generated as a string parameter to a DefaultSettingValueAttribute.

(The core idea for this came from another SO answer here.)

And don't forget to attribute the class from Step 1 with this converter.

Step 3: Create a setting that uses the custom type

In the Settings designer window, when selecting a Type, choose 'Browse...'. In the window that appears, you won't be able to find your type in the main GUI tree. That's OK; just type the fully qualified type name (with all of its namespaces) into the text field. (In this example, it's SomeNamespace.Part.)

After opening the Type drop-down and selecting Browse:

Type a fully namespace qualified class name

Then enter a default string in the Value column which your TypeConverter from Step 2 can convert to your custom Type from Step 1. I chose a;b;c.

Settings designer should look like this now

Putting it all together:

You can use this code sample, together with the instructions above, to test the idea. It will ensure your custom settings type has default values. Parts of the TypeConverter code lines are taken from the answer linked above.

using System;
using System.ComponentModel;

namespace SomeNamespace
{
    public class Program
    {
        static void Main()
        {
            Part p = Properties.Settings.Default.Part;
            // you can breakpoint here after deleting user.config, and verify that p contains default values.
            p.display_name = "Extra 3";
            p.work_name = "Sonstiges3";
            p.folder_path = "path";
            Properties.Settings.Default.Part = p;
            Properties.Settings.Default.Save();
        }

    }

    [TypeConverter(typeof(PartConverter))]
    public class Part
    {
        public string display_name { get; set; }
        public string work_name { get; set; }
        public string folder_path { get; set; }

        // parameterless constructor required for using as a custom Settings object
        public Part() { }

        // used with the TypeConverter to go back-and-forth between class type and string
        public override string ToString()
        {
            return $"{display_name};{work_name};{folder_path}";
        }
    }

    public class PartConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string stringValue && !string.IsNullOrWhiteSpace(stringValue))
            {
                // Obviously, using more robust parsing in production code is recommended.
                string[] parts = stringValue.Split(';');
                if (parts.Length == 3)
                    return new Part() { display_name = parts[0], work_name = parts[1], folder_path = parts[2] };
                else
                    throw new FormatException("Invalid format");
            }
            else
                return base.ConvertFrom(context, culture, value);
        }
    }
}

For completeness, here's the auto-generated designer code. Note how the default value of a;b;c is generated:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace SomeNamespace.Properties {
    
    
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
    public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }
        
        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("a;b;c")]
        public global::SomeNamespace.Part Part {
            get {
                return ((global::SomeNamespace.Part)(this["Part"]));
            }
            set {
                this["Part"] = value;
            }
        }
    }
}
Casto answered 21/10, 2020 at 19:1 Comment(4)
Thanks for the answer, I don't want the user to dig into the user.config. Isn't that the file where all settings are stored if changed with the scope user? The problem is, when I just set the default value in the settings.settings I can't access it always gives me the error that for example part1.display_name is null. But when I create an object and save it e.g. as part2 I can access via part2.display_name. The only diffrence I noticed is that part2 is stored as xml in the user.config and the part1 is in settings.settingsZama
@Zama Ah. For that, you need a custom TypeConverter, per this answer. If you define a TypeConverter for your Part type, then it allows you to set a default value in the Settings designer as a string. The string is then converted into your type. Ultimately, this string gets set in the auto-generated settings.designer.cs, inside a DefaultSettingValueAttribute. Maybe later I can update my answer to show this.Casto
@Zama You should probably update your question with this info, too.Casto
Thanks for the detailed answer that's exactly what I was looking for!Zama

© 2022 - 2024 — McMap. All rights reserved.