Bold Specific Elements from JSON Array in Rich Textbox - c#

I'm currently facing an issue with the formatting of a chunk of text which is retrieved from a webserver as a JSON array.
What I am trying to acomplish is to format the text in a way that can be easily read by the user. An Example output of what I am trying to achieve is:
This is a Title
This is a little informative paragraph based on the subject selected
This is a secondary title
This is another paragraph
The way the string looks (before modification):
{"Title":"This is a Title", "Content_One": "This is alittle
informative paragraph based on the subject selected", "Title_Two":
"This is another paragraph"}
My current application is using Winforms, and I'm attempting to pump this into a Rich Text Box (hopefully going to handle the correct formatting). As a little long shot, I tried returning HTML Tags (Very long shot) For this to provide no change to the text.
I have also tried individually iterating through the array, and attempting to pramatically bold out certain elements from the JSON Array. None of which I have tried have provided expected output.
Attempt one:
TTKNormalContent.Text = new Font(ReturnArr.Title, FontStyle.Bold).ToString();
Which returns:
An unhandled exception of type
'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in
System.Core.dll
Attempt one:
TTKNormalContent.Text = ReturnArr.Title;
TTKNormalContent.SelectionFont = new Font(this.Font, FontStyle.Bold);
Which does not bold out the text
Attempt Two:
I can temporarily make the text bold by:
TTKNormalContent.Font = new Font(TTKNormalContent.Font, FontStyle.Bold);
TTKNormalContent.Text = ReturnArr.Title;
But adding:
TTKNormalContent.Font = new Font(TTKNormalContent.Font, FontStyle.Regular);
TTKNormalContent.Text += ReturnArr.ContentOne;
Which will remove the boldness

You need to use AppendText. When you use Text+= "something" you replace the format.
You can use this example:
var json = "{\"Title\":\"This is a Title\", \"Content_One\": \"This is alittle informative paragraph based on the subject selected\", \"Title_Two\": \"This is another paragraph\"}";
var start = 0;
Dictionary<string, string> values = new JavaScriptSerializer().Deserialize<Dictionary<string, string>>(json);
values.Cast<KeyValuePair<string, string>>()
.ToList()
.ForEach(item =>
{
this.richTextBox1.AppendText(item.Key);
this.richTextBox1.AppendText( ":" );
start += item.Key.Length + 1;
this.richTextBox1.AppendText(item.Value);
this.richTextBox1.Select(start, item.Value.Length);
this.richTextBox1.SelectionFont = new Font(this.richTextBox1.Font, FontStyle.Bold);
this.richTextBox1.AppendText("\n");
start += item.Value.Length + 1;
});
Screenshot:

Related

Gembox document removes table of contents

I'm using Gembox document to replace some text in a docx document and it works great. However, I have a table of contents field that disappears after saving the document.
I tried doing the following but the field still disappears leaving only the placeholder text:
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });
UPDATE (2021-01-15):
Please try again with the latest version from the BugFixes page or from NuGet.
The latest version will work on the machine that uses culture with ';' character as a list separator.
Or you can specify that culture like this:
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
CultureInfo.CurrentCulture = new CultureInfo("fr");
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });
Also, the issue with the missing tab stop should be resolved now as well.
ORIGINAL:
When I tried to update your TOC from MS Word, I got the following:
No table of contents entries found.
After investigating the field's code of your TOC element, I figured out what the problem is.
This is the instruction text that you have:
{ TOC \h \z \t "TitreChapitre;1;SousTitreChapitre;2" }
These semicolon character separators (;) are culture-dependent. In other words, updating this TOC element will work on a machine that has a French region and settings, but it won't work when you have an English region and settings.
I'm currently on vacation, so I can't do anything about this. When I come back I will fix this problem for you.
For now, can you use the following as a workaround (I also noticed an issue with missing TabStop, this workaround will cover that as well):
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
var section = toc.Parent as Section;
var tocWidth = section.PageSetup.PageWidth - section.PageSetup.PageMargins.Left - section.PageSetup.PageMargins.Right;
var toc1Style = document.Styles["toc 1"] as ParagraphStyle;
var toc1TabStop = new TabStop(tocWidth - toc1Style.ParagraphFormat.RightIndentation, TabStopAlignment.Right, TabStopLeader.Dot);
toc1Style.ParagraphFormat.Tabs.Add(toc1TabStop);
var toc2Style = document.Styles["toc 2"] as ParagraphStyle;
var toc2TabStop = new TabStop(tocWidth - toc2Style.ParagraphFormat.RightIndentation, TabStopAlignment.Right, TabStopLeader.Dot);
toc2Style.ParagraphFormat.Tabs.Add(toc2TabStop);
toc.InstructionText = toc.InstructionText.Replace(';', ',');
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });
I hope this works for you.

richTextBox - add text and table

i want to add formatted text and table to a richTextBox.
For this I use these codes:
Text:
richTextBox1.SelectionFont = new Font("Maiandra GD", 30, FontStyle.Bold);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.SelectionIndent = 0;
richTextBox1.AppendText("text text text");
And the table:
StringBuilder tableRtf = new StringBuilder();
tableRtf.Append(#"{\rtf1\fbidis\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}");
for (int j = 0; j <5; j++)
{
tableRtf.Append(#"\trowd");
tableRtf.Append(#"\cellx2500" + " ghhghgjghjghjhggjh");
tableRtf.Append(#"\intbl\cell");
tableRtf.Append(#"\cellx10000\intbl\cel");
tableRtf.Append(" " + "sdfsdfs" + #"\intbl\clmrg\cell\row");
}
tableRtf.Append(#"\pard");
tableRtf.Append(#"}");
richTextBox1.Rtf=tableRtf.ToString();
But the
richTextBox1.Rtf=tableRtf.ToString();
kills the previous contents.
How can I make compatible them?
It is not a duplicate because I want two thing:
1) add formatted text to richTextBox this way:
richTextBox1.SelectionFont = new Font("Maiandra GD", 30, FontStyle.Bold);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.AppendText("text text text");
It is well readable and I can modify easily.
2) And I want to add tables.
So the structure:
text text text text text
text text text text text
|TABLE|
text text text text text
text text text text text
text text text text text
|TABLE|
etc.
But I don't know how can I apply tables without losing previous contents?
What you need to do is to dissect the rtf codes into the headers and the bodies.
The table body starts with the loop and keeping the \par is surely a good idea.
But you must neither replace the old text nor simply append the body to the end.
Instead you need to insert it before the last curly! This is because that last curly marks the end of the whole rtf code and anything after it will be ignored!
This was simple.
For a full solution you also will want to combine the headers.
This is a lot more work and writing it all out would go beyond an SO answer.
But the basic principle is simple:
You need to understand the things your table header adds to the things already in the primal header.
The most common things are afont table and a color table.
So if you want to use one or more different fonts in the appended table you need to do this:
add them to the font table with a new font index, e.g. as \f1\fnil Consolas; after the previous semicolon.
use it by changing the loop to include the new font right after the first \intbl table-paragraph-formatting control word: \intbl\f2\fs24 ghhghgjghjghjhggjh.
repeat as needed if you want to use different fonts in the table.
add a cfNfont color selector, if you want to.
The same idea will also work for the color table. It doesn't have a explicit indexing, so order matters; all colors are appended, each with a semicolon at the end:
{\colortbl ;\red255\green0\blue0;\red25\green0\blue220;}
..adds a blue color to the red from the formatted text.
You see, this is work and takes some effort and preparations.
You can find the full rtf specs here.
Here is a screenshot of playing a little with the rtf..:
Note that the parts of table header was created by the control; you may want to use a dummy control for this or maybe you can figure out which parts you need and which are not necessary..
Update: Here is a 'appending rtf for dummies' version:
tableRtf.Append(#"{\fonttbl{\f0\fnil\fcharset0 Courier;}}");
for (int j = 0; j <5; j++)
{
tableRtf.Append(#"\trowd");
tableRtf.Append(#"\cellx2500" + " ghhghgjghjghjhggjh");
tableRtf.Append(#"\intbl\cell");
tableRtf.Append(#"\cellx10000\intbl\cel");
tableRtf.Append(" " + "sdfsdfs" + #"\intbl\clmrg\cell\row");
}
tableRtf.Append(#"\pard");
tableRtf.Append(#"}");
string rtf1 = richTextBox1.Rtf.Trim().TrimEnd('}');
string rtf2 = tableRtf.ToString();
richTextBox1.Rtf = rtf1 + rtf2;
Note that the font table inserted before the table body does work! But make sure not to add the rtf-start tag!!

Set specific line of multiline TextBox in c#

Hello I am new to C Sharp & Windows Forms. I am unable to set the specific string of a multiline TextBox. I have tried below things so far.
textBox1.Lines[1] = "welcome to stackOverflow";
The above code does not give a compile time error but when I saw the result using Debug mode it was not expected.
Then i was also reading this MSDN article but in this there is a new collection created by using stream[] constructor but still the same problem arises.
It should give compiler error because you are trying to assign a string to char here:
textBox1.Text[1] = "welcome to stackOverflow";
Text property is of type string, when you use indexer on a string it gives you the char at that position. And also string is immutable so you can't really change a character at specific position without creating a new string.
You should set the Text directly like this:
textBox1.Text = "welcome to stackOverflow";
Or if you have more than one line in an array of string you should set the Lines property:
var lines = new [] { "foo", "bar" };
textBox1.Lines = lines;
Any value that you set directly to textBox1.Lines will be effected to textBox1.
There is a solution to resolve your problem. I think it's best way.
You have to clone the current value of your textbox. Then you set new value on it. Finally, you set back to textbox.
var curValue = (string[])textBox1.Lines.Clone();
curValue[1] = "welcome to stackOverflow";
//Set back to textBox1
textBox1.Lines = curValue;

Rich Text to Plain Text via C#?

I have a program that reads through a Microsoft Word 2010 document and puts all text read from the first column of every table into a datatable. However, the resulting text also includes special formatting characters (that are usually invisible in the original Word document).
Is there a way that I can take the string of text that I've read and strip all the formatting characters from it?
The program is pretty simple, and uses the Microsoft.Office.Interop.Word assemblies. Here is the main loop where I'm grabbing the text from the document:
// Loop through each table in the document,
// grab only text from cells in the first column
// in each table.
foreach (Table tb in docs.Tables)
{
for (int row = 1; row <= tb.Rows.Count; row++)
{
var cell = tb.Cell(row, 1);
var listNumber = cell.Range.ListFormat.ListString;
var text = listNumber + " " + cell.Range.Text;
dt.Rows.Add(text);
}
}
EDIT: Here is what the text ("1. Introduction") looks like in the Word document:
This is what it looks like before being put into my datatable:
And this is what it looks like when put into the datatable:
So, I'm trying to figure out a simple way to get rid of the control characters that seem to be appearing (\r, \a, \n, etc).
EDIT: Here is the code I'm trying to use. I created a new method to convert the string:
private string ConvertToText(string rtf)
{
using (RichTextBox rtb = new RichTextBox())
{
rtb.Rtf = rtf;
return rtb.Text;
}
}
When I run the program, it bombs with the following error:
The variable rtf, at this point, looks like this:
RESOLUTION: I trimmed the unneeded characters before writing them to the datatable.
// Loop through each table in the document,
// grab only text from cells in the first column
// in each table.
foreach (Table tb in docs.Tables)
{
for (int row = 1; row <= tb.Rows.Count; row++)
{
var charsToTrim = new[] { '\r', '\a', ' ' };
var cell = tb.Cell(row, 1);
var listNumber = cell.Range.ListFormat.ListString;
var text = listNumber + " " + cell.Range.Text;
text = text.TrimEnd(charsToTrim);
dt.Rows.Add(text);
}
}
I don't know exactly what formatting you're trying to remove, but you could try something like:
text = text.Where(c => !Char.IsControl(c)).ToString();
That should strip the non-printing characters out.
Al alternative can be that You need to add a rich textbox in your form (you can keep it hidden if you don't want to show it) and when you have read all your data just assign it to the richtextbox. Like
//rtfText is rich text
//rtBox is rich text box
rtBox.Rtf = rtfText;
//get simple text here.
string plainText = rtBox.Text;
Why dont you give this a try:
using System;
using System.Text.RegularExpressions;
public class Example
{
static string CleanInput(string strIn)
{
// Replace invalid characters with empty strings.
try {
return Regex.Replace(strIn, #"[^\w\.#-]", "",
RegexOptions.None, TimeSpan.FromSeconds(1.5));
}
// If we timeout when replacing invalid characters,
// we should return Empty.
catch (RegexMatchTimeoutException) {
return String.Empty;
}
}
}
Here's a link for it as well.
http://msdn.microsoft.com/en-us/library/844skk0h.aspx
Totally different approach would be to look at the Open Office XML SDK.
This example should get you started.

how to get text file rows with no delimiter into array

I have a text file that I'm trying to input into an array called columns.
Each row in the text file belongs to a different attribute in a sub-class I have created.
For example, row 2 in my text file is a date that I would like to pass over...I do not want to use the Split because I do not have a delimiter but I do not know an alternative. I am not fully understanding the below if someone could help. When I try to run it, it says that columns[1] is out of its range...Thank you.
StreamReader textIn =
new StreamReader(
new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read));
//create the list
List<Event> events = new List<Event>();
while (textIn.Peek() != -1)
{
string row = textIn.ReadLine();
string[] columns = row.Split(' ');
Event special = new Event();
special.Day = Convert.ToInt32(columns[0]);
special.Time = Convert.ToDateTime(columns[1]);
special.Price = Convert.ToDouble(columns[2]);
special.StrEvent = columns[3];
special.Description = columns[4];
events.Add(special);
}
Input file sample:
1
8:00 PM
25.00
Beethoven's 9th Symphony
Listen to the ninth and final masterpiece by Ludwig van Beethoven.
2
6:00 PM
15.00
Baseball Game
Come watch the championship team play their archrival--No work stoppages, guaranteed.
Well, one way to do it (though it is a bit ugly) would be to use File.ReadAllLines, and then loop through the array, something like this:
string[] lines = File.ReadAllLines(path);
int index = 0;
while (index < lines.Length)
{
Event special = new Event();
special.Day = Convert.ToInt32(lines[index]);
special.Time = Convert.ToDateTime(lines[index + 1]);
special.Price = Convert.ToDouble(lines[index + 2]);
special.StrEvent = lines[index + 3];
special.Description = lines[index + 4];
events.Add(special);
lines = lines + 5;
}
This is very brittle code - a lot can break it. What if one of the events is missing a line? What if there are multiple blank lines in it? What if one of the Convert.Toxxx methods throws an error?
If you have the option to change the format of the file, I strongly recommend you make it delimited at least. If you can't change the format, you'll need to make the code sample above more robust so that it can handle blank lines, failed conversions, missing lines, etc.
Much, much, much easier to use a delimited file. Even easier to use an XML or JSON file.
Delimited File (CSV)
Let's say you have the same sample input, but this time it's a CSV file, like this:
1,8:00 PM,25.00,"Beethoven's 9th Symphony","Listen to the ninth and final masterpiece by Ludwig van Beethoven."
2,6:00 PM,15.00,"Baseball Game","Come watch the championship team play their archrival--No work stoppages, guaranteed"
I put quotes on the last two items in case there's ever a comma in there, it won't break the parsing.
For CSV files, I like to use the Microsoft.VisualBasic.FileIO.TextFieldParser class, which despite it's name can be used in C#. Don't forget to add a reference to Microsoft.VisualBasic and a using directive (using Microsoft.VisualBasic.FileIO;).
The following code will allow you to parse the above CSV sample:
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.Delimiters = new string[] {","};
parser.TextFieldType = Delimited;
parser.HasFieldsEnclosedInQuotes = true;
string[] parsedLine;
while (!parser.EndOfData)
{
parsedLine = parser.ReadFields();
Event special = new Event();
special.Day = Convert.ToInt32(parsedLine[0]);
special.Time = Convert.ToDateTime(parsedLine[1]);
special.Price = Convert.ToDouble(parsedLine[2]);
special.StrEvent = parsedLine[3];
special.Description = parsedLine[4];
events.Add(special);
}
}
This still has some issues though - you would need to handle cases where there were missing fields and I would recommend using TryParse methods instead of Convert.Toxxx, but it's a little easier (I think) than the non-delimited sampe.
XML File (Using LINQ to XML)
Now let's try it with an XML file and use LINQ to XML to get the data:
<Events>
<Event>
<Day>1</Day>
<Time>8:00 PM</Time>
<Price>25.00</Price>
<Title><![CDATA[Beethoven's 9th Symphone]]></Title>
<Description><![CDATA[Listen to the ninth and final masterpiece by Ludwig van Beethoven.]]></Description>
</Event>
<Event>
<Day>2</Day>
<Time>6:00 PM</Time>
<Price>15.00</Price>
<Title><![CDATA[Baseball Game]]></Title>
<Description><![CDATA[Come watch the championship team play their archrival--No work stoppages, guaranteed]]></Description>
</Event>
</Events>
I've used CDATA for the title and description so that special characters won't break the XML parsing.
This is easily parsed into your Events by the following code:
XDocument doc = XDocument.Load(path);
List<Event> events = (from x in doc.Descendants("Event")
select new Event {
Day = Convert.ToInt32(x.Element("Day").Value),
Time = Convert.ToDateTime(x.Element("Time").Value),
Price = Convert.ToDouble(x.Element("Price").Value),
StrEvent = x.Element("Title").Value,
Description = x.Element("Description").Value
}).ToList();
Of course, this is still not perfect as you still have the possibility of conversion failures or missing elements.
Pipe-Delimited File Example
Per our discussion in the comments, if you want to use the pipe (|), you need to put each event (in its entirety) on one line, like this:
1|8:00 PM|25.00|Beethoven's 9th Symphony|Listen to the ninth and final masterpiece by Ludwig van Beethoven.
2|6:00 PM|15.00,|Baseball Game|Come watch the championship team play their archrival--No work stoppages, guaranteed
You can still use the TextFieldParser example above if you like (just change the delimiter from , to |, or if you want you can use your original code.
Some Final Thoughts
I wanted to also address the original code and show why it wasn't working. The main reason was that you were reading one line at a time, and then splitting on ' '. This would have been a good start if all the fields were on the same line (although it still would have had problems because of spaces in the Time, StrEvent and Description fields), but they weren't.
So when you read the first line (which was 1) and split on ' ', you got one value back (1). When you tried to access the next element of the split array, you got the index out of range error because there was no columns[1] for that line.
Essentially, you were trying to treat each line as if it had all the fields in it, when in reality it was one field per line.
For your given sample file something like
string[] lines = File.ReadAllLines(path);
for (int index = 4; index < lines.Length; index += 5)
{
Event special = new Event();
special.Day = Convert.ToInt32(lines[index - 4]);
special.Time = Convert.ToDateTime(lines[index - 3]);
special.Price = Convert.ToDouble(lines[index - 2]);
special.StrEvent = lines[index - 1];
special.Description = lines[index];
events.Add(special);
}
Would do the job, but like Tim already mentioned, you should consider changing your file format.
delimiters can be deleted if your side column values haven't intersect char or have fix size.by this condition you can read file and split field on it.
if you want to read from file and load data automatically to variables , i suggest Serialize and deSeialize variabls to file but that file isn't text file!

Categories

Resources