I am writing a c# Html editor application, in which you type the code in a RichTextBox control. I want the RichTextBox to behave like notepad++ and other code editors in which the Html syntax gets highlighted in colors, like this for example:
How can I establish this in C# windows form RichTextBox? I have searched almost everywhere and didn't find anything that helped me. This is what I tried so far but I doesn't give the result I want:
private void SyntaxHighlight()
{
string[] tags = { "html","head","body","a","b","img","strong","p","h1","h2","h3","h4","h5","h6","embed","iframe","span","form",
"button","input","textarea","br","div","style","script","table","tr","td","th","i","u","link","meta","title"};
foreach (string s in tags)
{
richTextBox1.Find("<" + s);
richTextBox1.SelectionColor = Color.Blue;
richTextBox1.Find(">");
richTextBox1.SelectionColor = Color.Blue;
}
string[] attributes = { "href","src","height","width","rowspan","colspan","target","style","onclick","id","name","class"};
foreach (string s in attributes)
{
richTextBox1.Find(s + "=");
richTextBox1.SelectionColor = Color.Red;
}
}
Can someone help me? What should I write inside the SyntaxHighlight() method? can someone give me the appropriate code?
Thank you
Within your code you are only finding the 1st occurrence of the HTML tag and highlighting it. But instead, you should loop through the entire rich text content to finding proceeding occurrences of the same text. I just did a quick mock based on your exact code, please check it out.
private void highlightHTMLText()
{
string[] tags = { "html","head","body","a","b","img","strong","p","h1","h2","h3","h4","h5","h6","embed","iframe","span","form",
"button","input","textarea","br","div","style","script","table","tr","td","th","i","u","link","meta","title"};
foreach (string s in tags)
{
findAndHighlight("<" + s, Color.Blue);
findAndHighlight("</" + s, Color.Blue);
findAndHighlight(">", Color.Blue);
}
string[] attributes = { "href", "src", "height", "width", "rowspan", "colspan", "target", "style", "onclick", "id", "name", "class" };
foreach (string s in attributes)
{
findAndHighlight(s + "=", Color.Red);
}
}
private void findAndHighlight(string sSearchStr, Color oColor)
{
int index = richTextBox1.Text.IndexOf(sSearchStr);
while (index != -1)
{
richTextBox1.Select(index, sSearchStr.Length);
richTextBox1.SelectionColor = oColor;
index = richTextBox1.Text.IndexOf(sSearchStr, index + sSearchStr.Length);
}
}
Further as per this answer you should be able to make use of the same utility library Scintilla used by Notepad++ itself. As pointed out you do not need to re-invent the wheel, but as a developer I obviously prefer my own util (it is just me ;) ). Hope this helps.
Find doesn't move a cursor, it returns the location of the first match. Try this instead:
How to select text from the RichTextBox and then color it?
A little late to the party but, after wanting to create my own offline version of CodePen, I implemented my own version of html syntax highlighting following CodePen's theme.
This does syntax highlighting and markup formatting, though the formatting depends on whether or not your html is well-formed.
Just add this as a class for your RichTextBox, instantiate it accordingly and call it within whichever event works for you (I'm using it with the RTB's double_click event but that does eliminate double-click text selection). What I'm planning to do is add a timer, some boolean variables and work this within the key_up and key_down events to set the highlight update to be a bit more automatic and less intrusive on shortcuts. (which is hereby included below the class)
public void HighlightHTM(RichTextBox Htm_Input)
{
Htm_Input.Visible = false;
#region store the original caret position + forecolor
int originalIndex = Htm_Input.SelectionStart;
int originalLength = Htm_Input.SelectionLength;
Color originalColor = Color.FromArgb(200, 200, 200); // Grey
#endregion
#region try to format the markup
try { Htm_Input.Text = XElement.Parse(Htm_Input.Text).ToString(); } catch { }
#endregion
#region match everything but puncutation and equals
Regex e = new Regex(#"(.*?|=)[^\w\s]");
MatchCollection eMatches = e.Matches(Htm_Input.Text);
foreach (Match m in eMatches)
{
Htm_Input.SelectionStart = m.Groups[1].Index;
Htm_Input.SelectionLength = m.Groups[1].Length;
Htm_Input.SelectionColor = Color.FromArgb(221, 202, 126); // Yellow
}
#endregion
#region match tags
Regex t = new Regex(#"(<\w+|</\w+|/>|>)[^=]");
MatchCollection tMatches = t.Matches(Htm_Input.Text, 0);
foreach (Match m in tMatches)
{
Htm_Input.SelectionStart = m.Groups[1].Index;
Htm_Input.SelectionLength = m.Groups[1].Length;
Htm_Input.SelectionColor = Color.FromArgb(167, 146, 90); // Brown
}
#endregion
#region match quotes
Regex q = new Regex("\".*?\"");
MatchCollection qMatches = q.Matches(Htm_Input.Text);
foreach (Match m in qMatches)
{
Htm_Input.SelectionStart = m.Index;
Htm_Input.SelectionLength = m.Length;
Htm_Input.SelectionColor = Color.FromArgb(150, 179, 138); // Green
}
#endregion
#region match inner html
Regex h = new Regex(">(.+?)<");
MatchCollection hMatches = h.Matches(Htm_Input.Text);
foreach (Match m in hMatches)
{
Htm_Input.SelectionStart = m.Groups[1].Index;
Htm_Input.SelectionLength = m.Groups[1].Length;
Htm_Input.SelectionColor = Color.FromArgb(200, 200, 200); // Grey
}
#endregion
#region restoring the original colors, for further writing
Htm_Input.SelectionStart = originalIndex;
Htm_Input.SelectionLength = originalLength;
Htm_Input.SelectionColor = originalColor; // Light Grey
#endregion
Htm_Input.Focus();
Htm_Input.Visible = true;
}
Happy coding!
Edit: I should also mention that !doctype breaks formatting as it's not exactly xml-friendly in the context of "well-formed". For my purposes, all tags including body and relevant closings, css and js links are added programmatically at page save so only markup within the body tags are worked with inside the html RTB. This eliminates that problem.
You'll notice that this relies exclusively on Regex rather than on hard-coded tags and properties. I did this because tags and properties have a tendency to pop on and off the w3 scene quite often. That would force a dev to continually have to go back and edit those strings to remove deprecated tags / properties or to add new. Not optimal.
I also thought it prudent to go ahead and include the instantiation / usage examples to make this a bit more plug&play.
Above public Main(), instantiate like so:
#region Class Instantiation
SyntaxHighlight syntax = new SyntaxHighlight();
#endregion
... and, within your chosen event handler, call it like so:
private void htm_input_DoubleClick(object sender, EventArgs e)
{
syntax.HighlightHTM(Htm_Input);
}
Naturally, adding a SaveFileDialog and an OpenFileDialog pretty much provides this the functionality of your very own, albeit very basic, html editor. Toss in a WebBrowser control and apply the RTB's text as the WebBrowser's source and you've upgraded to live-view.
In the very least, this should serve as a viable reference for syntax highlighting in general. It really just boils down to identifying patterns and manipulating their colors so, for example, this will work effectively with css, javascript and even C# with some light adjusting of the pattern identification parameters.
The following is how I setup the automatic refresh with key_up / key_down and a timer set to 1000 ms:
#region Globals
int r = 0;
bool refresh = false;
#endregion
private void Htm_Input_KeyUp(object sender, KeyEventArgs e)
{
refresh = true; // enter refresh cycle
}
private void Htm_Input_KeyDown(object sender, KeyEventArgs e)
{
refresh = false; // abort refresh cycle
}
private void Timer_Refresh_Tick(object sender, EventArgs e)
{
// check if refresh cycle is entered, refresh at 3 seconds or reset the counter if aborted
if (refresh) { if (r == 3) { syntax.HighlightHTM(Htm_Input); refresh = false; r = 0; } r++; } else { r = 0; }
}
I am having an issue where when I write text to a second file, it display fine in wordpad where it includes the linebreaks, but in notepad all the text for copyText and richTextBox1.Text appear in one really long line.
How can I fix this? If somebody can include a code snippet with my code then it will be much appreciated as I can see what you have done and changed and I can keep it for reference for futr use.
My code is below where I take text from a text file (Testfile.txt) and insert it into richTextBox1, then when I click on button, text is copied to richText Box2 and is written in second file (_Parsed.txt).
I did a message box on text line and it displays text all without line break. I'm not sure if that is issue but I do need help as deadline is tomorrow and this is only thing I need to sort out and I'm done.
string Chosen_File = "C:\\_Testfile.txt";
string Second_File = "C:\\_Parsed.txt";
string wholeText = "";
private void mnuOpen_Click(object sender, EventArgs e) {
//Add data from text file to rich text box
richTextBox1.LoadFile(Chosen_File, RichTextBoxStreamType.PlainText);
//Read lines of text in text file
string textLine = "";
StreamReader txtReader;
txtReader = new StreamReader(Chosen_File);
do {
textLine = textLine + txtReader.ReadLine() + " ";
}
//Read line until there is no more characters
while (txtReader.Peek() != -1);
richTextBox1.Text = textLine;
txtReader.Close();
}
}
private void Write(string file, string text) {
//Check to see if _Parsed File exists
//Write to _Parsed text file
using(StreamWriter objWriter = new StreamWriter(file)) {
objWriter.Write(text);
objWriter.Close();
}
}
private void newSummaryMethod(string copyText) {
//Write into richTextBox2 all relevant text
copyText = richTextBox1.Text;
wholeText = richTextBox1.Text + copyText
Write(Second_File, wholeText);
}
private void btn1_Click(object sender, EventArgs e) {
newSummaryMethod(copyText);
}
The problem is probably with the line breaks in your text file(s).
TextBoxes expect \r\n sequence as an indication of the end-of-line, and your text files may contain only the \n.
Try to make this change to your code:
do {
textLine = textLine + txtReader.ReadLine() + "\r\n";
}
//Read line until there is no more characters
while (txtReader.Peek() != -1);
The problem is that you are never adding a new line to the file in your code. you are using objWriter.Write(text); which does not generate a new line.
you can use objWriter.WriteLine(text); instead and it will automatically add new line character at the end.
I think the difference in viewing the file is because the "word wrap" option is turned on in "wordpad" and it causes to break a long line into multiple line.
I need to write the algorithm that replaces the first character by the same character but in upper case. So, I have written this code:
private void RegionFilter_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = (sender as TextBox);
var initialText = tb.Text;
if (initialText != "")
{
var firstChar = initialText.Substring(0, 1).ToUpper();
var restOfString = initialText.Substring(1, initialText.Length - 1);
tb.Text = firstChar + restOfString;
}
}
But there is a problem: The caret doesn't move to the end after replacing the text, it still remains at the beginning.
It's important to say that there is no CaretIndex property in TextBox in Windows Phone 8.
How can I solve this?
You can, however, use the selection methods of the textbox like i have shown here:
myTextBox.Select(tbPositionCursor.Text.Length, 0);
You can find more information about this here::
http://msdn.microsoft.com/en-us/library/ms752349(v=vs.110).aspx
You can also change the position of the caret by setting TextBox.SelectionStart of your TextBox. Add at the end of your algorithm:
yourTextBox.SelectionStart = yourTextBox.Text.Length;
I want to change font style for a textbox when I click the button.For this my code is below and it is okay;
protected void Button1_Click(object sender, System.EventArgs e)
{
TextBox1.Font.Size = FontUnit.XLarge;
TextBox1.ForeColor = System.Drawing.Color.Crimson;
TextBox1.BackColor = System.Drawing.Color.Snow;
TextBox1.BorderColor = System.Drawing.Color.HotPink;
}
But Can I do where I select of write which is in the textbox it changes only that part.For example textbox1.Text="Computer Programs" and the user select only "Computer" part of textbox1.Only "Computer" part must be changed.
From the sounds of it you want to format only part of the text? For that you'll need to look at a RichTextBox control.
With a RichTextBox you can make use of text selections and set formatting on only those areas:
RichTextBox rich = new RichTextBox();
rich.Text = "Here is some text for the Rich Text Box";
rich.SelectionStart = 0;
rich.SelectionLength = 4;
rich.SelectionFont = new Font(rich.Font, FontStyle.Bold);
After you're done setting your font to your selection you should set the font immediately afterwards back to the original so that you don't continue the style outside the area you wanted to apply it:
rich.SelectionStart = rich.SelectionStart + rich.SelectionLength;
rich.SelectionLength = 0;
rich.SelectionFont = rich.Font;
This should result in "Here is some text for the Rich Text Box" changing to look like: "Here is some text for the Rich Text Box".
When using the Microsoft RichTextBox control it is possible to add new lines like this...
richtextbox.AppendText(System.Environment.NewLine); // appends \r\n
However, if you now view the generated rtf the \r\n characters are converted to \par not \line
How do I insert a \line control code into the generated RTF?
What does't work:
Token Replacement
Hacks like inserting a token at the end of the string and then replacing it after the fact, so something like this:
string text = "my text";
text = text.Replace("||" "|"); // replace any '|' chars with a double '||' so they aren't confused in the output.
text = text.Replace("\r\n", "_|0|_"); // replace \r\n with a placeholder of |0|
richtextbox.AppendText(text);
string rtf = richtextbox.Rtf;
rtf.Replace("_|0|_", "\\line"); // replace placeholder with \line
rtf.Replace("||", "|"); // set back any || chars to |
This almost worked, it breaks down if you have to support right to left text as the right to left control sequence always ends up in the middle of the placeholder.
Sending Key Messages
public void AppendNewLine()
{
Keys[] keys = new Keys[] {Keys.Shift, Keys.Return};
SendKeys(keys);
}
private void SendKeys(Keys[] keys)
{
foreach(Keys key in keys)
{
SendKeyDown(key);
}
}
private void SendKeyDown(Keys key)
{
user32.SendMessage(this.Handle, Messages.WM_KEYDOWN, (int)key, 0);
}
private void SendKeyUp(Keys key)
{
user32.SendMessage(this.Handle, Messages.WM_KEYUP, (int)key, 0);
}
This also ends up being converted to a \par
Is there a way to post a messaged directly to the msftedit control to insert a control character?
I am totally stumped, any ideas guys? Thanks for your help!
Adding a Unicode "Line Separator" (U+2028) does work as far as my testing showed:
private void Form_Load(object sender, EventArgs e)
{
richText.AppendText("Hello, World!\u2028");
richText.AppendText("Hello, World!\u2028");
string rtf = richText.Rtf;
richText.AppendText(rtf);
}
When I run the program, I get:
Hello, World!
Hello, World!
{\rtf1\ansi\ansicpg1252\deff0\deflang1031{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue255;}
\viewkind4\uc1\pard\cf1\f0\fs17 Hello, World!\line Hello, World!\line\par
}
It did add \line instead of \par.
Since you want to use a different RTF code, I think you may need to forget about the simplistic AppendText() method and manipulate the .Rtf property of your RichTextBox directly instead. Here is a sample (tested) to demonstrate:
RichTextBox rtb = new RichTextBox();
//this just gets the textbox to populate its Rtf property... may not be necessary in typical usage
rtb.AppendText("blah");
rtb.Clear();
string rtf = rtb.Rtf;
//exclude the final } and anything after it so we can use Append instead of Insert
StringBuilder richText = new StringBuilder(rtf, 0, rtf.LastIndexOf('}'), rtf.Length /* this capacity should be selected for the specific application */);
for (int i = 0; i < 5; i++)
{
string lineText = "example text" + i;
richText.Append(lineText);
//add a \line and CRLF to separate this line of text from the next one
richText.AppendLine(#"\line");
}
//Add back the final } and newline
richText.AppendLine("}");
System.Diagnostics.Debug.WriteLine("Original RTF data:");
System.Diagnostics.Debug.WriteLine(rtf);
System.Diagnostics.Debug.WriteLine("New Data:");
System.Diagnostics.Debug.WriteLine(richText.ToString());
//Write the RTF data back into the RichTextBox.
//WARNING - .NET will reformat the data to its liking at this point, removing
//any unused colors from the color table and simplifying/standardizing the RTF.
rtb.Rtf = richText.ToString();
//Print out the resulting Rtf data after .NET (potentially) reformats it
System.Diagnostics.Debug.WriteLine("Resulting Data:");
System.Diagnostics.Debug.WriteLine(rtb.Rtf);
Output:
Original RTF data:
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs17\par
}
New RTF Data:
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs17\par
example text0\line
example text1\line
example text2\line
example text3\line
example text4\line
}
Resulting RTF Data:
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs17\par
example text0\line example text1\line example text2\line example text3\line example text4\par
}
if you are using paragraphs to write to richtextbox you can use the LineBreak() same code shown below
Paragraph myParagraph = new Paragraph();
FlowDocument myFlowDocument = new FlowDocument();
// Add some Bold text to the paragraph
myParagraph.Inlines.Add(new Bold(new Run(#"Test Description:")));
myParagraph.Inlines.Add(new LineBreak()); // to add a new line use LineBreak()
myParagraph.Inlines.Add(new Run("my text"));
myFlowDocument.Blocks.Add(myParagraph);
myrichtextboxcontrolid.Document = myFlowDocument;
Hope this helps!