【问题标题】:How to create an application settings parameter of type a list of structs?如何创建结构列表类型的应用程序设置参数?
【发布时间】:2021-04-27 02:21:15
【问题描述】:

在我的项目中,我有一个自定义结构:

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

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

我正在使用这些点的列表:

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

我要做的是将我的点列表保存到 Settings.settings:

我不知道如何将字符串更改为我的结构点列表。

我尝试弄乱 xml 并手动添加我的选项,但我不知道该怎么做。我发现的大多数事情都告诉我使用自定义命名空间,但我也无法使用我的 Point 结构列表来使用它。

编辑:我的问题是使用列表的自定义结构。问题不在于将项目添加到列表中,而是能够正确加载它们的内容。

【问题讨论】:

  • 这能回答你的问题吗? How to save a List<string> on Settings.Default?。任何自定义对象都需要标记为[Serializable],因为List&lt;T&gt; 已经是。
  • 遗憾的是没有。
  • 我可以将这些项目保存到我的列表中。但是当我在重新启动程序后尝试加载它们时,它会加载列表中的项目数量,而不是它们的实际内容。每个坐标始终为 0。
  • 您的公共属性需要公共设置器。请参阅我的答案以获取完整的解决方案,包括对其实际工作进行测试。如果您仍有问题,请发布重现问题的代码。

标签: c# list visual-studio struct application-settings


【解决方案1】:

将副本应用于您的案例(我无法标记它,因为我无法撤销关闭,我发布此答案是为了解决您的困难以及比各种教程和副本更精确和更完整):

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

在例如下面的命名空间中具有可序列化结构:

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

如前所述,属性必须是可读写的,所以我添加了自动设置器,并且结构必须通过添加属性来序列化。

编译项目。

您需要创建一个字符串参数,例如名称为MyList

然后使用任何文本编辑器手动编辑Settings.settings,将其类型更改为:

System.Collections.Generic<WindowsFormsAppTest.Point>

在 HTML 文本编码中,如:

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

设置.设置

<?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>

保存后,去Visual Studio重新加载文件,就可以看到类型改变了:

您需要使用设计器更新此设置生成的 C# 代码文件,方法是扩展参数类型并单击此自定义列表类型:

如果没有,或者出现问题,您需要手动更新这些文件:

app.config 到 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 更改属性的类型

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

全部保存和/或重新编译。

现在,您可以在 Main 方法或主窗体的构造函数或加载事件处理程序中编写示例:

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

例如在按钮单击事件处理程序中:

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

现在配置文件是:

<?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>

我们可以写来改变一个项目,因为是一个结构所以一个值类型:

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

现在设置文件有:

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

为了测试这一点,我们可以使用另一个按钮列出项目:

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

注意

要清理应用程序设置,您需要删除所有这些文件夹:

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

其中OrganizationWindowsFormsAppTest 来自AssemblyInfo.cs 中使用字段AssemblyCompanyAssemblyTitle 定义的清单文件。

【讨论】:

  • 这是一篇很棒的帖子,但这正是我遇到的问题所在。问题是当我关闭程序然后打开它时,我尝试加载这些点。我得到MyList.count = 4 的结果。但是当我检查 MyList[0].xPoint 时,它总是等于 0。
  • @ParallelPancakes 如果不设置属性 writable 和 struct serializable 是不行的。
【解决方案2】:

以下步骤有效。

但是,我相信您的问题仅仅是因为 xPointyPoint 没有公共设置器。这是由于XmlSerializer 作品。请参阅文档here

首先,创建一个设置。在这种情况下,我将其命名为ListOfPoints。类型无关紧要,无论如何我们都会改变它。

手动编辑“Settings.settings”。我只是用 Visual Studio 的 XML 编辑器打开它,但使用你喜欢的。

然后只更改设置的类型。请注意,&lt;&gt; 需要使用 HTML 编码。

整个 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>

唯一的改变是:

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

所有代码:

[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");
    }
}

通过在saveMe 中添加您想要的任何内容来进行测试。然后运行SaveData,然后运行LoadData

如果数据不匹配,LoadData 会抛出异常。

【讨论】:

  • 这就是我目前所处的位置。加载我的 var 时,我能够匹配计数。但是,当我在加载后尝试检查每个点的内容时,没有为每个点保存(或加载?)该值。当我第一次保存它时,我可以看到ListOfPoints[0].x 是 525。但是,一旦我关闭并重新打开应用程序,它就会成功加载正确的 ListOfPoints 计数。但它不保存 x 或 y 值。因此,当我检查我的积分值时,ListOfPoints.x 始终为 0,而应为 525。
  • @ParallelPancakes 查看更新后的答案。我错过了阅读您的代码的问题。尝试简单地为您的属性添加一个公共设置器。我在LoadData 中添加了重新加载设置,以测试您提到的应用程序重启问题。
  • 您引导我找到正确答案,谢谢。当需要public uint xPoint { get; set; } 时,问题是public uint xPoint { get; }。多么愚蠢的错误导致了如此巨大的头痛。
  • 请评论为什么投反对票,以便我解决任何问题。
猜你喜欢
  • 1970-01-01
  • 2019-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
相关资源
最近更新 更多