简体   繁体   中英

How to create an application settings parameter of type a list of structs?

In my project I have a custom struct:

struct Point {
  public uint xPoint { get; }
  public uint yPoint { get; }

  public Point(uint x, uint y) {
    xPoint = x;
    yPoint = y;
  }
}

I'm using a list of these Points:

List<Point> pathToNavigate = new List<Point>();

What I'm trying to do is save a list of my Points to the Settings.settings:像这样

I can't figure out how to change string to be a list of my struct Point.

I tried messing with the xml and manually add in my option but I can't work out how to do it. Most things I find tell me to use a custom namespace but I also can't get that working with a list of my Point struct.

Edit: My problem is with a custom struct using a list. The issue isn't adding the items to the list, it's being able to load their contents properly.

Applying the duplicate to your case (I can't flag it because I can't revote to close, and I post this answer to solve your difficulties as well as to be more precise and more complete than the various tutorials and duplicates):

How to save a List<string> on Settings.Default?

Having the serializable struct in for example the namespace below:

namespace WindowsFormsAppTest
{
  [Serializable]
  public struct Point
  {
    public uint xPoint { get; set; }
    public uint yPoint { get; set; }

    public Point(uint x, uint y)
    {
      xPoint = x;
      yPoint = y;
    }
  }
}

As mentionned previously, properties must be read-write, so I added auto-setters, and the struct must be serializable by adding the attrribute.

Compile the project.

You need to create a string parameter for example having the name MyList :

在此处输入图像描述

Then edit Settings.settings by hand using any text editor to change its type to:

System.Collections.Generic<WindowsFormsAppTest.Point>

In HTML text encoding like:

System.Collections.Generic.List&lt;WindowsFormsAppTest.Point&gt;

Settings.settings

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="WindowsFormsAppTest.Properties" GeneratedClassName="Settings">
  <Profiles />
  <Settings>
    <Setting Name="MyList" Type="System.Collections.Generic.List&lt;WindowsFormsAppTest.Point&gt;" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
  </Settings>
</SettingsFile>

After saved, go to Visual Studio and reload the file, then you will be able to see the type is changed:

在此处输入图像描述

You need to update this settings generated C# code file using the designer by expanding the type of the parameter and to click on this custom list type:

在此处输入图像描述

If not, or in case of problem, you need to manually update these file:

app.config to serializeAs Xml

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="WindowsFormsAppTest.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
        </sectionGroup>
    </configSections>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/></startup>
    <userSettings>
        <WindowsFormsAppTest.Properties.Settings>
            <setting name="MyList" serializeAs="Xml">
                <value />
            </setting>
        </WindowsFormsAppTest.Properties.Settings>
    </userSettings>
</configuration>

Settings.Designer.cs to change the type of the property

public global::System.Collections.Generic.List<WindowsFormsAppTest.Point> MyList {
    get {
        return ((global::System.Collections.Generic.List<WindowsFormsAppTest.Point>)(this["MyList"]));
        }

Save all and/or compile again.

Now, you can write for example in the Main method or in the constructor of the main form or in the load event handler:

private void FormTest_Load(object sender, EventArgs e)
{
  if ( Properties.Settings.Default.MyList == null )
    Properties.Settings.Default.MyList = new List<Point>();
  Properties.Settings.Default.Save();
}

And for example in a button click event handler:

private void ButtonCreate_Click(object sender, EventArgs e)
{
  Properties.Settings.Default.MyList.Add(new Point(10, 10));
  Properties.Settings.Default.MyList.Add(new Point(10, 20));
  Properties.Settings.Default.MyList.Add(new Point(20, 20));
  Properties.Settings.Default.MyList.Add(new Point(50, 50));
  Properties.Settings.Default.Save();
}

Now the config file is:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <WindowsFormsAppTest.Properties.Settings>
            <setting name="MyList" serializeAs="Xml">
                <value>
                    <ArrayOfPoint xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                        <Point>
                            <xPoint>10</xPoint>
                            <yPoint>10</yPoint>
                        </Point>
                        <Point>
                            <xPoint>10</xPoint>
                            <yPoint>20</yPoint>
                        </Point>
                        <Point>
                            <xPoint>20</xPoint>
                            <yPoint>20</yPoint>
                        </Point>
                        <Point>
                            <xPoint>50</xPoint>
                            <yPoint>50</yPoint>
                        </Point>
                    </ArrayOfPoint>
                </value>
            </setting>
        </WindowsFormsAppTest.Properties.Settings>
    </userSettings>
</configuration>

To change one item we can write, because being a struct so a value-type:

private void ButtonUpdate_Click(object sender, EventArgs e)
{
  var point = Properties.Settings.Default.MyList[0];
  point.xPoint = 100;
  point.yPoint = 100;
  Properties.Settings.Default.MyList[0] = point;
  Properties.Settings.Default.Save();
}

And now the settings file has:

<Point>
    <xPoint>100</xPoint>
    <yPoint>100</yPoint>
</Point>
...

To test this, we can list the items using another button:

private void ButtonShow_Click(object sender, EventArgs e)
{
  var list = Properties.Settings.Default.MyList.Select(p => $"{p.xPoint}, {p.yPoint}");
  MessageBox.Show(string.Join(Environment.NewLine, list.ToArray()));
}

在此处输入图像描述

Note

To cleanup the application settings, you need to delete for example all these folders:

c:\Users\User\AppData\Local\Organization\WindowsFormsAppTest.exe_Url_*

Where Organization and WindowsFormsAppTest comes from the manifest file defined in AssemblyInfo.cs using fields AssemblyCompany and AssemblyTitle .

The steps below work.

However, I believe your issue is simply because xPoint and yPoint do not have public setters. This is due to how XmlSerializer works. See the documentation here .

First, create a setting. In this case I named it ListOfPoints . The type is irrelevant, we're going to change it anyways.

Manually edit "Settings.settings". I just open it with Visual Studio's XML editor but use what you prefer.

Then change the type of the setting only. Note you need to use HTML encoding for < and > .

Entire Settings.settings:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="WindowsFormsApp1.Properties" GeneratedClassName="Settings">
  <Profiles />
  <Settings>
    <Setting Name="ListOfPoints" Type="System.Collections.Generic.List&lt;WindowsFormsApp1.MyPoint&gt;" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
  </Settings>
</SettingsFile>

The only change made was this:

Type="System.Collections.Generic.List&lt;WindowsFormsApp1.MyPoint&gt;"

All Code:

[Serializable]
public struct MyPoint
{
    public uint X { get; set; }
    public uint Y { get; set; }

    public MyPoint(uint x, uint y)
    {
        X = x;
        Y = y;
    }

    public override bool Equals(object obj)
    {
        if (!(obj is MyPoint))
            return false;
        var other = (MyPoint)obj;
        return other.X == X && other.Y == Y;
    }

    public override int GetHashCode()
    {
        return unchecked(X.GetHashCode() ^ Y.GetHashCode());
    }
}

private static readonly List<MyPoint> saveMe = new List<MyPoint>();
private static List<MyPoint> loadMe;

private static void SaveData()
{
    Properties.Settings.Default.ListOfPoints = saveMe;
    Properties.Settings.Default.Save();
}

private static void LoadData()
{
    Properties.Settings.Default.Reload();
    loadMe = Properties.Settings.Default.ListOfPoints;
    TestData();
}

private static void TestData()
{
    if (loadMe.Count != saveMe.Count)
        throw new Exception("Different counts");
    for (int i = 0; i < loadMe.Count; i++)
    {
        if (!loadMe[i].Equals(saveMe[i]))
            throw new Exception($"{nameof(MyPoint)} at index {i} doesn't match");
    }
}

Test this by adding whatever you wish to saveMe . Then run SaveData followed by LoadData .

LoadData will throw an exception if the data doesn't match.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM