Hyperlink to a line of RichTextBox in C# - c#

I am looking a way to create a HyperLink in a RichTextBox pointing to a line of the text of the same RichTextBox.
I just found how to do that with Internet Links but I don't find a way to do it with the same text inside of the control (It's like Hyperlinks in MS Word pointing to a header or bookmark).
Thanks in Advance. - CCB

No, this will not work unless you code the necessary stuff yourself.
Two suggestions:
A simple workaround with links always starting with www.
The nicer solution with arbitrary link text
Let's have a look at both options..:
Using the built-in functionality of recognizing an URL seems the right way to start, but the link will always have to look like a URL, not like a hyperlink to an anchor.. If you can live with a solution that has, say, links like this: www.goto.234 and anchors like this: #234# this is really rather simple..
A working example can be as simple as this:
private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e)
{
var s = e.LinkText.Split('.');
string anchor = s[2];
int a = richTextBox1.Text.IndexOf("#" + anchor + "#" );
if (a >= 0) richTextBox1.SelectionStart = a; else return; // do add more checks!
richTextBox1.SelectionLength = 0;
Text = anchor + " # " + a;
//richTextBox1.ScrollToCaret(); <<--- this crashes on my machine!
// so I take the jump out of the click event and it works:
Timer ttt = new Timer() { Interval = 100 };
ttt.Tick += (ss, ee) => { richTextBox1.ScrollToCaret(); ttt.Stop(); };
}
Option two: If you'd rather have more choice of how the links should read you can do this:
Start by formatting each to
Start with a special character, say a tilde '~'
format it to look blue and underlined if you want to
Either make it one word or replace space by underlines and format those to have the forecolor equal to the backcolor
Now this can do the job:
public string delimiters = " ()[]{}!&?=/\\,;.\r\n";
private void richTextBox2_Click(object sender, EventArgs e)
{
int sstart = -1;
string s = getWordAt(richTextBox2.Text,
richTextBox2.SelectionStart, delimiters, out sstart);
if (s.Length < 3) return;
string char1 = s.Substring(0, 1);
if (char1 == "~")
{
int p = richTextBox2.Text.IndexOf("#" + s.Substring(1));
if (p >= 0) { richTextBox2.SelectionStart = p; richTextBox2.ScrollToCaret(); }
}
}
public static string getWordAt(string text, int cursorPos,
string delimiters, out int selStart)
{
int startPos = 0;
selStart = startPos;
if ((cursorPos < 0) | (cursorPos > text.Length) | (text.Length == 0)) return "";
if ((text.Length > cursorPos) & (delimiters.Contains(text[cursorPos]))) return "";
int endPos = text.Length - 1;
if (cursorPos == text.Length) endPos = text.Length - 1;
else { for (int i = cursorPos; i < text.Length; i++)
{ if (delimiters.Contains(text[i])) { endPos = i - 1; break; } } }
if (cursorPos == 0) startPos = 0;
else { for (int i = cursorPos; i > 0; i--)
{ if (delimiters.Contains(text[i])) { startPos = i + 1; break; } } }
selStart = startPos;
return text.Substring(startPos, endPos - startPos + 1);
}
Here are the two versions side by side, once at the top then after clicking on a link:
Both versions work fine, although both could do with some more checks.
Note that I was too lazy to format the pseudo-links in the second example, so they show their tildes and hashes..
Not hard to write a helper function that can insert the formatting; the search will still work as it searches in the Text, not the Rtf property..

Related

C# - split a RichTextBox line in two based on the caret position

I've got a RichTextBox, here referred to as box.
string currentline = box.Lines[box.GetLineFromCharIndex(box.SelectionStart)];
That line there fetches the line the caret is in. It works excellently.
However, I have a need to get two strings from this. The first is everything on that line UP to the caret, and the second is everything on that line AFTER it.
For instance, if the line is How is you|r day going?, with | representing the caret, I would get How is you and r day going?, separately.
I wrote this monstrosity, which works:
string allbefore = box.Text.Substring(0, box.SelectionStart);
string allafter = box.Text.Substring(box.SelectionStart, box.Text.Length - box.SelectionStart);
string linebefore = "";
for (int i = 0; i < allbefore.Length; i++)
{
linebefore += allbefore[i];
if (allbefore[i] == '\n')
linebefore = "";
}
string lineafter = "";
for (int i = 0; i < allafter.Length; i++)
{
if (allafter[i] == '\n')
break;
else
lineafter += allafter[i];
}
It gives me the result I want, but involves looping through EVERY character in the entire box, which just hurts. Is there an easy way to do this I'm just missing? Thanks.
This might do the trick for you
string currentline = box.Lines[box.GetLineFromCharIndex(box.SelectionStart)];
var listOfStrings = new List<string>();
string[] splitedBox = currentline.Split('|');
foreach(string sp in splitedBox)
{
string[] lineleft = sp.Split('\n');
listOfStrings.Add(lineleft[lineleft.Count() - 1]);
}
In the first approach we are splitting the line by char | than finding if we have any \n if it exsist we are taking the values accordingly
Another approach could be
string box = "How is \n you|r day \n going?";
bool alllinesremoved = true;
while(alllinesremoved)
{
if(box.Contains('\n'))
{
if(box.IndexOf('\n') > box.IndexOf('|'))
{
box = box.Remove(box.IndexOf('\n'), (box.Length - box.IndexOf('\n')));
}
else
{
box = box.Remove(0, box.IndexOf('\n') + 1);
}
}
else
{
alllinesremoved = false;
}
}
string[] splitedBox = box.Split('|');
in the second approach we are removing the characters before and after the \n and then splitting the string. I think the second one seems more good to me.
Have you tried using line.split? Not sure if this is what you want.
Store the position of \n using indexOf and, if >= 0, that is, the string contains it, use substring and assign the value otherwise.
string allbefore = box.Text.Substring(0, box.SelectionStart);
string allafter = box.Text.Substring(box.SelectionStart, box.Text.Length - box.SelectionStart);
int newLinePos = allBefore.lastIndexOf("\n");
string lineBefore = ((newLinePos >= 0) ? (allBefore.substring(newLinePos + 1)) : (allBefore));
newLinePos = allafter.indexOf("\n");
string lineAfter = ((newLinePost >= 0) ? (allAfter.substring(0, newLinePos)) : (allAfter));

Copy paste from a text box adds extra lines

So basically I have a button that takes the strings delimited by line breaks in one text box that then formats them a particular way and puts them in a different text box. Everything looks fine when I run the code, however, when I copy and paste the text from the second textbox to a different place, it adds a line break after everything that I took from the original box.
private void ToTableButton_Click(object sender, EventArgs e)
{
StringBuilder tableText = new StringBuilder();
string[] lines = BasicTextBox.Text.Split('\n');
TableTextBox.Clear();
try
{
for (int i = 0; i < columnsUpDown.Value; i++)
{
if (i == columnsUpDown.Value - 1)
{
tableText.Append(lines[i]);
}
else
{
tableText.Append(lines[i] + " | ");
}
}
tableText.Append(Environment.NewLine);
for (int i = 0; i < columnsUpDown.Value; i++)
{
if (i == columnsUpDown.Value - 1)
{
tableText.Append("--");
}
else
{
tableText.Append("--|");
}
}
int currentPos = Convert.ToInt32(columnsUpDown.Value);
while (currentPos <= lines.Length)
{
tableText.Append(Environment.NewLine);
for (int i = 0; i < columnsUpDown.Value; i++)
{
if (i == columnsUpDown.Value - 1)
{
tableText.Append(lines[currentPos]);
}
else
{
tableText.Append(lines[currentPos] + " | ");
}
currentPos++;
}
}
}
catch
{
}
TableTextBox.Text = tableText.ToString();
}
I thought maybe this be because the split doesn't remove the \n but I wasn't sure how to remove it afterwards. Any advice would be greatly appreciated.
I would think that trimming the lines[currentPos] would be the correct way...
tableText.Append(lines[currentPos].ToString().Trim());

How to select RichTextBox text given an index and length

If you are only given an index and length (or EndIndex) of a certain text to select, how do you perform this in WPF version of RichTextBox?
This is very doable in Textbox as you can call Textbox.Select(startIndex,Length) but I don't see anything equivalent in RTB.
Edit: I have found the answer to making a selection
internal string Select(RichTextBox rtb, int index, int length)
{
TextRange textRange = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
if (textRange.Text.Length >= (index + length))
{
TextPointer start = textRange.Start.GetPositionAtOffset(index, LogicalDirection.Forward);
TextPointer end = textRange.Start.GetPositionAtOffset(index + length, LogicalDirection.Backward);
rtb.Selection.Select(start, end);
}
return rtb.Selection.Text;
}
However, when I try to highlight the line after the selection has been made:
rtb.Selection.ApplyPropertyValue(TextElement.BackgroundProperty, new SolidColorBrush(Colors.LightBlue));
The highlighting feature works only on the first try and breaks after second try. Anyone know the reason for this?
Ok this question is old but i finally found the Answer so i put this here.
I was having similiar issues when i tried to make some Syntaxhighlighter using the RichTextBox.
What I found out is, that when you play arround with ApplyPropertyValue you cannot simply use GetPositionAtOffset anymore. I believe that applying propertyvalues seems to change the "internal positions" of TextTokens inside the Document, hence 'braking' this functionality.
The Workaround:
Everytime you need to work with GetPositionAtOffset first call ClearAllProperties on the complete TextRange of the Document, then reapply all your properties using ApplyPropertyValue but thistime from right to left. (right means highest offset)
I dont know if you had applied any PropertyValues expect the Selection highlighting, so you might need to put more thinking inthere.
This is how my code looked when it caused the Problem:
private void _highlightTokens(FlowDocument document)
{
TextRange fullRange = new TextRange(document.ContentStart, document.ContentEnd);
foreach (Token token in _tokenizer.GetTokens(fullRange.Text))
{
TextPointer start = fullRange.Start.GetPositionAtOffset(token.Position);
TextPointer end = start.GetPositionAtOffset(token.Length);
TextRange range = new TextRange(start, end);
range.ApplyPropertyValue(TextElement.ForegroundProperty, _getTokenColor(token));
}
}
And i fixed it like this:
private void _highlightTokens(FlowDocument document)
{
TextRange fullRange = new TextRange(document.ContentStart, document.ContentEnd);
fullRange.ClearAllProperties(); // NOTICE: first remove allProperties.
foreach (Token token in _tokenizer.GetTokens(fullRange.Text).Reverse()) // NOTICE: Reverse() to make the "right to left" work
{
TextPointer start = fullRange.Start.GetPositionAtOffset(token.Position);
TextPointer end = start.GetPositionAtOffset(token.Length);
TextRange range = new TextRange(start, end);
range.ApplyPropertyValue(TextElement.ForegroundProperty, _getTokenColor(token));
}
}
Use the Select() method on the RichTextBox.Selection property.
Blockquote you can get the text between spaces.....
private string RichWordOver(RichTextBox rch, int x, int y)
{
int pos = rch.GetCharIndexFromPosition(new Point(x, y));
if (pos <= 0) return "";
string txt = rch.Text;
int start_pos;
for (start_pos = pos; start_pos >= 0; start_pos--)
{
char ch = txt[start_pos];
if (!char.IsLetterOrDigit(ch) && !(ch=='_')) break;
}
start_pos++;
int end_pos;
for (end_pos = pos; end_pos < txt.Length; end_pos++)
{
char ch = txt[end_pos];
if (!char.IsLetterOrDigit(ch) && !(ch == '_')) break;
}
end_pos--;
if (start_pos > end_pos) return "";
return txt.Substring(start_pos, end_pos - start_pos + 1);
}
private void rchText_MouseClick(object sender, MouseEventArgs e)
{
MessageBox.Show(RichWordOver(rchText, e.X, e.Y));
}

how to type to a label?

I am trying to have a multi line textbox that when you type in it streams it to the label, BUT the label has to have a max length of 15 so like once it reaches 15 characters in the textbox it should start overwriting the label since it reached it's max length
thanks to anyone who can help
Add onchange event on text box :
if (textBox1.Text.Length<=15)
{
label1.Caption=textBox1.Text;
}
For example
I'm not sure what kind of overwriting You want to achieve.
There are at least three methods you can use:
displaying always the last 15 characters from the textbox, as described in the Olivier's answer
clearing the label's text each 15 characters inserted and start filling in the label over again, you can use this code to achieve this:
private void textBox1_TextChanged(object sender, EventArgs e)
{
String text = textBox1.Text.Replace("\r\n", "|");
int startIndex = ((text.Length - 1) / 15) * 15;
label1.Text = text.Substring(Math.Max(0, startIndex));
}
you can also overwrite char by char when text length is over 15 characters, however, I suppose it's not what you would like to achieve, because it would cause a mess in the textbox ;), however, it can be used as a kind of visual effect :). If you want code snippet for this, let me know :).
Update
Here's the code for the third overwriting method:
String lastText = "";
private void textBox1_TextChanged(object sender, EventArgs e)
{
String textBoxText = textBox1.Text.Replace("\r\n", "|");
if (textBoxText.Length > lastText.Length)
{
int charIndex = (textBoxText.Length - 1) % 15;
if (charIndex >= 0)
{
label1.Text = label1.Text.Insert(charIndex, textBoxText.Substring(textBoxText.Length - 1));
if (charIndex < textBoxText.Length - 1)
{
label1.Text = label1.Text.Remove(charIndex + 1, 1);
}
}
}
else
{
int charIndex = textBoxText.Length % 15;
if (textBoxText.Length >= 15)
{
label1.Text = label1.Text.Insert(charIndex, textBoxText[textBoxText.Length - 15].ToString());
if (charIndex < textBoxText.Length - 1)
{
label1.Text = label1.Text.Remove(charIndex + 1, 1);
}
}
else
{
label1.Text = label1.Text.Remove(label1.Text.Length - 1, 1);
}
}
lastText = textBoxText;
}
Add a handler to the TextChanged event of the TextBox that sets the Label's content based on the text. E.g. (untested, might have your concept wrong or be off by one somewhere)
int startIndex = Math.Max(0, myTextBox.Text.Length - 15);
int endIndex = Math.Min(myTextBox.Text.Length - 1, startIndex);
myLabel.Text = myTextBox.Text.Substring(startIndex, endIndex - startIndex);
Also, though it doesn't change your question/answer, you might want to look at using a TextBlock instead of a Label. It allows things like line wrapping. See some of the differences here: http://joshsmithonwpf.wordpress.com/2007/07/04/differences-between-label-and-textblock/ (in WPF, should be similar whatever you're doing, though)
My solution always displays the last 15 characters in the label
private void textBox1_TextChanged(object sender, EventArgs e)
{
string s = textBox1.Text.Replace("\r\n", "|");
int length = s.Length;
if (length > 15) {
label1.Text = s.Substring(length - 15);
} else {
label1.Text = s;
}
}
I also replace the line-breaks with |. Since your textbox is in multiline mode, line-breaks are entered when you hit <Enter>.

Finding and replacing text in c#

I need to add functionality in my program so that any file imported it will find the text within the "" of the addTestingPageContentText method as seen below. The two values on each line will then be added to a datagridview which has 2 columns so first text in first column then second in the 2nd column. How would i go about Finding the "sometext" ?
addTestingPageContentText("Sometext", "Sometext");
addTestingPageContentText("Sometext2", "Sometext2");
... continues n number of times.
Neither fast nor efficient, but it's easier to understand for those new to regular expressions:
while (!endOfFile)
{
//get the next line of the file
string line = file.readLine();
EDIT: //Trim WhiteSpaces at start
line = line.Trim();
//check for your string
if (line.StartsWith("addTestingPageContentText"))
{
int start1;
int start2;
//get the first something by finding a "
for (start1 = 0; start1 < line.Length; start1++)
{
if (line.Substring(start1, 1) == '"'.ToString())
{
start1++;
break;
}
}
//get the end of the first something
for (start2 = start1; start2 < line.Length; start2++)
{
if (line.Substring(start2, 1) == '"'.ToString())
{
start2--;
break;
}
}
string sometext1 = line.Substring(start1, start2 - start1);
//get the second something by finding a "
for (start1 = start2 + 2; start1 < line.Length; start1++)
{
if (line.Substring(start1, 1) == '"'.ToString())
{
start1++;
break;
}
}
//get the end of the second something
for (start2 = start1; start2 < line.Length; start2++)
{
if (line.Substring(start2, 1) == '"'.ToString())
{
start2--;
break;
}
}
string sometext2 = line.Substring(start1, start2 - start1);
}
}
However I would seriously recommend going through some of the great tutorials out there on the internet. This is quite a good one
The expression "\"[^"]*\"" would find each...

Categories