John Smith Optional
John Smith Optional

Reputation: 13

Form control as an xml value

I have this example code in my c# win form...

List<string> listFurniture = new List<string>();
XDocument xml = XDocument.Load(Application.StartupPath + @"\Furniture.xml");
foreach (XElement quality in xml.Descendants("Quality"))
    listFurniture.Add(quality.Value);

maintextbox.Text = listFurniture[0];

... And this example xml

<Furniture>
  <Table>
    <Quality>textbox1.Text + "and" + textbox2.Text + "but" + textbox3.Text</Quality>   
    ...
  </Table>  
</Furniture>

My dilemma is, the maintextbox is producing the actual string "textbox1.Text", instead of the value of textbox1.

I want the xml value to be read as:

maintextbox.Text = textbox1.Text + "and" + textbox2.Text + "but" + textbox3.Text;

not as:

maintextbox.Text = "textbox1.Text + "and" + textbox2.Text + "but" + textbox3.Text";

I tried using a text file as well with StreamReader and I got the same result.

The reason for coding my project this way is because the sequence of the textboxes changes and so does the "and" and the "but". When that change happens, I wouldn't have to rewrite the code and recompile the program. I would just make the changes in xml.

Upvotes: 1

Views: 412

Answers (2)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236268

There is all OK with xml parsing in your solution. What you need is processing of Quality strings.

string[] parts = quality.Split('+');
Regex regex = new Regex(@"^""(.*)""$");
var textBoxes = Controls.OfType<TextBox>().ToList();

for (int i = 0; i < parts.Length; i++)
{
    string part = parts[i].Trim();

    var match = regex.Match(part);
    if (match.Success)
    {
        parts[i] = match.Groups[1].Value;
        continue;
    }

    var textBox = textBoxes.FirstOrDefault(tb => tb.Name + ".Text" == part);
    if (textBox != null) // possibly its an error if textbox not found
        parts[i] = textBox.Text; 
}    

mainTextBox.Text = String.Join(" ", parts);

What happened here:

  • We split quality string by + chars to get array of string parts
  • With regular expression we verify if part looks like something in quotes "something". If yes, then it will be or, and or other connective word
  • And last, we check all textboxes for matching name of textbox in quality string part. If it matches, then we replace part with text from textbox
  • We join parts to get result string

BTW you can parse Xml in one line:

var listFurniture = xml.Descendants("Quality") 
                       .Select(q => (string)q)
                       .ToList();

Upvotes: 2

Greg
Greg

Reputation: 11480


Update:

Since I received a comment to explain the code a bit; I'll explain it a bit.

First, XML as a language is designed for structure. That structure and ease; provides the flexibility and power to quickly parse data between languages or applications seamless. Your original question states that your textbox is producing a string value of your code textbox.text.

The XML need to be structured; an example structure would be:

<Furniture>
     <Table>
         <Color> Red </Color>
         <Quality> 10 </Quality>
         <Material> Wood </Material>
      </Table>
</Furniture>

So if you were to read your XML it would be finding the root tag. All other components would be nodes. These nodes need to be index, or siphoned through to attain the proper correlation you would like represented into your textbox.

That is what this code is doing; I'll break it down through each step.

// String you will format with the XML Structure.
StringBuilder output = new StringBuilder();

The next part will be as follows:

// Create an XML Reader, by wrapping it in the 'using' it will ensure once your done the object is disposed of.  Rather then leaving the connection to your document open.
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
       // We will read our document to the following; hold to that attribute.  The attribute is identifying the node and all of the child elements that reside within it: So in our case Table.
       reader.ReadToFollowing("Table");
       reader.MoveToFirstAttribute();
       string color = reader.Value;
       output.AppendLine("The color of the table " + color);

       // As you can see there isn't anything fancy here, it reads to our root node.  Then moves to the first child element.  Then it creates a string and appends it. Keep in mind we are using our StringBuilder so we are just appending to that structure.
       reader.ReadToFollowing("Material");
       output.AppendLine("The material: " + reader.ReadElementContentAsString());

       // Same result as we used earlier; just a different method to attain our value.
}

// Now we output our block.
OutputTextBlock.Text = output.ToString();

Now all the data is pushed into a string, obviously you can use the above code with a textbox to retrieve those values as well.

That is how you correctly receive XML into your application; but you mentioned two things earlier. So it sounds like your trying to use the textbox to physically write to the document, which can be done through the XmlWriter.

But the reason you also keep receiving your textbox because as far as the textbox is concerned textbox.text is associated to the value. Your structure is stating this string is the value.

In order to achieve your goal; you would have a method to write the value to the document. Then another to read it; so that it properly transitions the data in and out of your document and is represented correctly.

<Quality>Textbox1.Text</Quality> That doesn't allow the textbox value to automatically be read into your document and textbox. Your assigning a string value into the node. You would physically have to write to the document the values before it can be read.

The MSDN has examples of how to properly parse the data; hopefully I've clarified some of the reasons in which you are having your issue.


More code; straight from MSDN: Right off the MSDN:

StringBuilder output = new StringBuilder();

String xmlString =
        @"<?xml version='1.0'?>
        <!-- This is a sample XML document -->
        <Items>
          <Item>test with a child element <more/> stuff</Item>
        </Items>";
// Create an XmlReader
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
    XmlWriterSettings ws = new XmlWriterSettings();
    ws.Indent = true;
    using (XmlWriter writer = XmlWriter.Create(output, ws))
    {

        // Parse the file and display each of the nodes.
        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element:
                    writer.WriteStartElement(reader.Name);
                    break;
                case XmlNodeType.Text:
                    writer.WriteString(reader.Value);
                    break;
                case XmlNodeType.XmlDeclaration:
                case XmlNodeType.ProcessingInstruction:
                    writer.WriteProcessingInstruction(reader.Name, reader.Value);
                    break;
                case XmlNodeType.Comment:
                    writer.WriteComment(reader.Value);
                    break;
                case XmlNodeType.EndElement:
                    writer.WriteFullEndElement();
                    break;
            }
        }

    }
}
OutputTextBlock.Text = output.ToString();

or

StringBuilder output = new StringBuilder();

String xmlString =
    @"<bookstore>
        <book genre='autobiography' publicationdate='1981-03-22' ISBN='1-861003-11-0'>
            <title>The Autobiography of Benjamin Franklin</title>
            <author>
                <first-name>Benjamin</first-name>
                <last-name>Franklin</last-name>
            </author>
            <price>8.99</price>
        </book>
    </bookstore>";

// Create an XmlReader
using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
{
    reader.ReadToFollowing("book");
    reader.MoveToFirstAttribute();
    string genre = reader.Value;
    output.AppendLine("The genre value: " + genre);

    reader.ReadToFollowing("title");
    output.AppendLine("Content of the title element: " + reader.ReadElementContentAsString());
}

OutputTextBlock.Text = output.ToString();

Upvotes: 1

Related Questions