【问题标题】:Using XML nodes as page list. Add numbered XML nodes then reordering them. Visual C#使用 XML 节点作为页面列表。添加编号的 XML 节点,然后重新排序它们。视觉 C#
【发布时间】:2017-03-01 10:47:23
【问题描述】:

我希望有人可以帮助我解决我在项目中遇到的障碍。我是一般编程的初学者,我正在学习 C# (WinForms)。我正在制作一个简单的文本编辑器。我有一个富文本文件和一个 XML 文件来存储其他信息。在 XML 文件中,我有一个 pages 节点,我想用它来存储行号。我有一个按钮,可以将富文本框滚动到存储在 XML 文件中的下一页的行号。例如:

<pages>
  <page1>0</page1>
  <page2>32</page2>
</pages>

我有另一个按钮,可以将页面添加到页面节点。添加新页面时,我需要我的应用程序在页面节点中的正确位置添加具有正确名称的新节点。例如,为第 16 行添加一个新页面,文件将如下所示;

<pages>
  <page1>0</page1>
  <page2>16</page2>
  <page3>32</page3>
</pages>

到目前为止,我已经在 XML 文件中添加了一个新节点并获得了选定的行号,但我不知道下一步。 首先,如何让新节点(在上述情况下为 page2)的名称中包含正确的数字?如果您在第 8 行再次单击添加页面按钮,它将变为 page2,第 16 行将变为 page3,第 32 行将变为 page4,依此类推。请参阅以下代码中的注释 1,该代码来自主 form.cs 文件。

string xmlFile = @"C:/test.xml";

private void bottomBarAddPageButton_Click(object sender, EventArgs e)
    {
        int cursorPosition = mainTextbox.SelectionStart;
        int lineIndex = mainTextbox.GetLineFromCharIndex(cursorPosition);
        XmlNode pagesNodes = XmlReader.SelectSingleNode("root/pages");
        XmlNode pageNodeCreate = XmlReader.CreateElement("page"); // Comment 1: Needs to add correct page number here 
        pageNodeCreate.InnerText = Convert.ToString(lineIndex);
        pagesNodes.AppendChild(pageNodeCreate);
        XmlReader.Save(xmlFile);
    }

以下是 XML 文件当前的样子。单击bottomBarAddPageButton 会添加一个名为page 的新节点,其中内部文本作为选定的行号(lineIndex)。

<root>
  <otherNodes>
  <pages>
    <page>0</page>
    <page>28</page>
    <page>32</page>
    <page>8</page>
    <page>76</page>
    <page>10</page>
  </pages>
</root>

其次,添加新节点后,如何重新排序页面中的所有子节点,使 XML 文件看起来像这样?

<root>
  <otherNodes>
  <pages>
    <page1>0</page1>
    <page2>8</page2>
    <page3>10</page3>
    <page4>28</page4>
    <page5>32</page5> 
    <page6>76</page6>       
  </pages>
</root>

当页面被删除时,我还需要能够执行相反的操作。例如,如果单击富文本框第 19 行的删除页面按钮,则 page3(包含第 10 行的节点)将被删除,因此 page2 将从第 8 行到第 27 行。page3 从第 28 行到第 27 行第 32 行,依此类推。

正如我在开始时所说的,我是 C# 的初学者,但我不知道该怎么做,最好的方法是什么,或者我是否走在正确的道路上。所以我希望那里有人能够帮助我。我希望我已经足够清楚地解释了我想要做的事情,并且我的问题是有道理的。

非常感谢您的宝贵时间和帮助。

摩根

附:请原谅我发布了这么大的问题,但我想提供尽可能多的信息。

【问题讨论】:

    标签: c# xml winforms line


    【解决方案1】:

    我认为最好的方法是将类命名为Page 并列出这些页面。然后,您可以将类序列化为 xml(并再次将 xml 反序列化为类)。这样您就可以操作Page 集合而无需摆弄xml。

    我会这样:

    类

    [Serializable]
    [XmlRoot("root")]
    public class Data
    {
    
        [XmlArray("Pages")]
        [XmlArrayItem(ElementName = "Page", Type = typeof(Page))]
        public List<Page> Pages { get; set; }
    
        //add as many properties as needed
        public int SomeIntNode { get ; set; }
    
        //using [XmlElement] attribute, you can change how the element will be named in xml 
        [XmlElement("otherName")]
        public string Name { get; set; }
    
    }
    
    [Serializable]
    public class Page
    {
        [XmlAttribute]
        public int Value { get; set; }
    
        //you can add additional attributes if needed.
        //just make a property and add [XmlAttribute] attrib, like this:
        //[XmlAttribute]
        //public sting SomeText { get; set; }
        //so it will be written as <Page Value="123" SomeText="abc" />
    }
    

    Data 类只是包含Pages 集合的容器。如果您还希望对它们进行序列化,可以将附加属性添加到 Data 类。

    Page 是包含值的类(RichTextBox 中的行)。

    [XmlArray("Pages")] 和 [XmlArrayItem(ElementName = "Page", Type = typeof(Page))] 等属性用作序列化程序(将数据转换和写入 xml 格式的类)的附加描述。首先告诉序列化器如何命名集合,然后告诉序列化器如何命名元素及其类型。 属性[Serializable] 告诉类可以被序列化。

    用法

    创建Data 类的实例并将页面添加到它的Pages 集合中。 写入数据,并在必要时再次加载。加载后,您可以通过索引或使用foreach 或使用for 循环遍历Pages。然后,您可以将一个或多个页面添加到集合中并再次对其进行排序。

    public void Test()
    { 
    
        //create data and write it to xml
        CreateData();
        //load data from xml
        Data data = LoadData();
        //iterate thru collection if needed
        foreach (Page page in data.Pages)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("page number {0}, value: {1}",(data.Pages.IndexOf(page) + 1), page.Value));
            //or, you can write to debug window like this:
            //System.Diagnostics.Debug.WriteLine("page number " + (data.Pages.IndexOf(page) + 1).ToString() + ", value: " + page.Value.ToString());
            //or fill some listbox
            //listBox1.Items.Add("page number " + (data.Pages.IndexOf(page) + 1).ToString() + ", value: " + page.Value.ToString());
            //...
        }
        //access second page (by index... index 1 is page 2)
        var specificPage = data.Pages[1];
        //add additional page and sort it by value
        data.Pages.Add(new Page() { Value = 15 });
        data.Pages.OrderBy(x => x.Value);
        //write again if needed
        WriteToXml(data);
    
    }
    
    private void CreateData()
    {
        Data data = new Data();
        data.Pages = new List<Page>();             
        data.Pages.Add(new Page() { Value = 10 });
        data.Pages.Add(new Page() { Value = 20 });
        data.Pages.Add(new Page() { Value = 30 });
    
        //other properties
        data.SomeIntNode = 100;
        data.Name = "here is name";
    
        WriteToXml(data);
    
    }
    
    private void WriteToXml(Data data)
    {
        XmlSerializer xSer = new XmlSerializer(typeof(Data));
        using (TextWriter writer = new StreamWriter(@"c:\temp\pages.xml"))
        {
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            xSer.Serialize(writer, data, ns);
        }
    }
    
    private Data LoadData()
    {
        XmlSerializer xSer = new XmlSerializer(typeof(Data));
        using (TextReader reader = new StreamReader(@"c:\temp\pages.xml"))
        {
            return xSer.Deserialize(reader) as Data;
        }
    }
    

    xml 数据(编辑:由于新属性和新根名称而更新)

    <?xml version="1.0" encoding="utf-8"?>
    <root>
      <Pages>
        <Page Value="10" />
        <Page Value="20" />
        <Page Value="30" />
        <Page Value="15" />
      </Pages>
      <SomeIntNode>1000</SomeIntNode>
      <otherName>here is name</otherName>
    </root>
    

    如果您有任何问题,请随时提出。

    编辑:将data 元素重命名为root(更像您的示例)并在Data 类中添加新属性以显示如何添加其他数据(查看CreateData())。

    【讨论】:

    • 非常感谢 Nino 的回复,非常有帮助。我确实有几个问题。首先,我在System.Diagnostics.Debug.WriteLine(string.Format("page number {0}, value: {1}" + (data.Pages.IndexOf(page) + 1), page.Value)); 线上得到了一个FormatException Index (zero based) must be greater than or equal to zero and less than the size of the argument list. 我用谷歌搜索了这个,但据我所知,它的格式正确,所以我不明白这个错误。我已经手动更改了 xml 文件,因此 page 节点与您的 xml 数据代码块相同,但它仍然给出了 FormatException。
    • 其次,您的代码将 root 节点中的所有内容替换为更新后的 pages 节点,并删除我的应用程序需要启动的所有其他数据。因此,如何仅重写 pages 节点而不删除或以其他方式影响 xml 文件中的任何其他节点?再次感谢您的时间和帮助摩根
    • 我已编辑代码并修复了 string.Format 上的错误。应该有逗号而不是加号,例如System.Diagnostics.Debug.WriteLine(string.Format("page number {0}, value: {1}",(data.Pages.IndexOf(page) + 1), page.Value))。如果对您来说更容易,在 VS 的输出窗口中写入数据的同一行(使用 View -&gt; Output 打开它,然后从下拉列表中选择 Debug)可以这样写:System.Diagnostics.Debug.WriteLine("page number " + (data.Pages.IndexOf(page) + 1).ToString() + ", value: " + page.Value.ToString());。
    • 您应该将所有其他属性添加到 Data 类,以便它们可以自动序列化为 xml 并从 xml 反序列化。只需将您需要的所有属性添加到类中即可。我已经编辑了答案以添加更多属性。
    • 现在看看我的回答。而且,如果您认为此答案有用,请点赞或标记为解决方案:)
    猜你喜欢
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多