I want to implement something that programmatically changes the background of the text when provided with a documentline.(Something that looks very similar to a block selection of a text. I'm going to be using this for debug breakpoints of an IDE I'm designing). I don't want to have to use selection as it causes the textbox to scroll.
I think I need to make use of DocumentColorizingTransformer but I'm not 100% sure how to go about this.
public class ColorizeAvalonEdit : ICSharpCode.AvalonEdit.Rendering.DocumentColorizingTransformer
{
protected override void ColorizeLine(ICSharpCode.AvalonEdit.Document.DocumentLine line)
{
int lineStartOffset = line.Offset;
string text = CurrentContext.Document.GetText(line);
int start = 0;
int index;
if (line.LineNumber == LogicSimViewCodeWPFCtrl.currentLine)
{
while ((index = text.IndexOf(text, start)) >= 0)
{
base.ChangeLinePart(
lineStartOffset + index, // startOffset
lineStartOffset + index + text.Length, // endOffset
(VisualLineElement element) =>
{
element.TextRunProperties.SetBackgroundBrush(Brushes.Red);
});
start = index + 1; // search for next occurrence
}
}
}
}
currentLine is the portion that will be highlighted.
The above code does work properly.. only problem is if the currentLine ever changes while I am viewing that line, it doesn't highlight the updated line until I scroll to another portion of the document (hiding the updated line), and come back to the updated line.
Also, how do I make the line numbers start from zero?
Since it was their creation, I peeked at SharpDevelop's source and how they did it.
They defined a bookmark type (BreakpointBookmark) and added bookmark to the line.
bookmark itself sets the color of the line in CreateMarker method. It is strange that it is not possible to configure colors of the break-point in SharpDevelop.
Hope it helps.
protected override ITextMarker CreateMarker(ITextMarkerService markerService)
{
IDocumentLine line = this.Document.GetLine(this.LineNumber);
ITextMarker marker = markerService.Create(line.Offset, line.Length);
marker.BackgroundColor = Color.FromRgb(180, 38, 38);
marker.ForegroundColor = Colors.White;
return marker;
}
I found the answer
TxtEditCodeViewer.TextArea.TextView.Redraw();
Isn't this a duplicate of this question?
However it looks like you should call InvalidateArrange() on the editor or InvalidateVisual() on each changed visual.
Related
After setting my RichTextBox's text to the string T, the Caret Position in the RichTextBox is "lost" (it goes to the start of it). Here's what I'm doing to try to "restore" it after it is "lost":
public static int GetCaretIndex(RichTextBox C)
{
return new TextRange(C.Document.ContentStart, C.CaretPosition).Text.Length;
}
...
int CaretIndex = GetCaretIndex(C); // Get the Caret position before setting the text of the RichTextBox
new TextRange(C.Document.ContentStart, C.Document.ContentEnd).Text = T; // Set the text of the RichTextBox
C.CaretPosition = C.Document.ContentStart.GetPositionAtOffset(CaretIndex, LogicalDirection.Forward); // Set the Caret Position based on the "Caret Index" variable
This code, however, does not work. The "restored" Caret is at a different position than the "original" one (always behind the "original" one for some reason).
"Saving" the RichTextBox's CaretPosition as a TextPointer doesn't seem to work either.
Can anyone provide me with an alternative way of "restoring" the Caret, or a way to fix the code above?
Seems to work (for me):
C.CaretPosition = C.Document.ContentStart;
C.CaretPosition = C.CaretPosition.GetPositionAtOffset(CaretIndex, LogicalDirection.Forward);
(I hate RichTextBox by the way.)
I was dealing with a similar issue recently and there is my solution. In my case, I'm creating a new RichTextBox.Document content and when I do this, I want to keep the caret position.
My idea was that caret offset functions are biased thanks to data structures used for text representation (Paragraphs, Runs, ...) which are also somehow calculated to offset position.
TextRange is a good approach to get exact caret position in the text. The problem lays in its restoration. But it gets easy when I know from which components my document is constructed. In my case, there are just Paragraphs and Runs.
What remains is to visit document structure, find an exact run where the caret should be and set the caret to correct position of found run.
Code:
// backup caret position in text
int backPosition =
new TextRange(RichTextBox.CaretPosition.DocumentStart, RichTextBox.CaretPosition).Text.Length;
// set new content (caret position is lost there)
RichTextBox.Document.Blocks.Clear();
SetNewDocumentContent(RichTextBox.Document);
// find position and run to which place caret
int pos = 0; Run caretRun = null;
foreach (var block in RichTextBox.Document.Blocks)
{
if (!(block is Paragraph para))
continue;
foreach (var inline in para.Inlines){
{
if (!(inline is Run run))
continue;
// find run to which place caret
if (caretRun == null && backPosition > 0)
{
pos += run.Text.Length;
if (pos >= backPosition){
caretRun = run;
break;
}
}
}
if (caretRun!=null)
break;
}
// restore caret position
if (caretRun != null)
RichTextBox.CaretPosition =
caretRun.ContentEnd.GetPositionAtOffset(backPosition - pos, LogicalDirection.Forward);
The code is not tested. I assembled it from various parts of my application. Let me know if you find any issue.
In my situation I have a RichTextBox with a single Paragraph that only allows entering text and line breaks. I change the structure of the RichTextBox ( by creating different coloured Run instances ) but not the text and restore after the change.
public static class CaretRestorer
{
public static void Restore(RichTextBox richTextBox, Action changer)
{
var caretPosition = GetCaretPosition(richTextBox);
changer();
Restore(richTextBox, caretPosition);
}
private static string GetFullText(RichTextBox richTextBox)
{
return new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text;
}
private static int GetInlineTextLength(Inline inline)
{
if(inline is LineBreak)
{
return 2;
}
return new TextRange(inline.ContentStart, inline.ContentEnd).Text.Length;
}
private static void Restore(RichTextBox richTextBox,int caretPosition)
{
var inlines = GetInlines(richTextBox);
var accumulatedTextLength = 0;
foreach (var inline in inlines)
{
var inlineTextLength = GetInlineTextLength(inline);
var newAccumulatedTextLength = accumulatedTextLength + inlineTextLength;
if (newAccumulatedTextLength >= caretPosition)
{
TextPointer newCaretPosition = null;
if(inline is LineBreak)
{
newCaretPosition = inline.ContentEnd;
}
else
{
var diff = caretPosition - accumulatedTextLength;
newCaretPosition = inline.ContentStart.GetPositionAtOffset(diff);
}
richTextBox.CaretPosition = newCaretPosition;
break;
}
else
{
accumulatedTextLength = newAccumulatedTextLength;
}
}
}
private static int GetCaretPosition(RichTextBox richTextBox)
{
return new TextRange(richTextBox.Document.ContentStart, richTextBox.CaretPosition).Text.Length;
}
private static Paragraph GetParagraph(RichTextBox RichTextBox)
{
return RichTextBox.Document.Blocks.FirstBlock as Paragraph;
}
private static InlineCollection GetInlines(RichTextBox RichTextBox)
{
return GetParagraph(RichTextBox).Inlines;
}
}
Okay, so i am working in xna and i want to open this textfile which should open in a textfile. Here is the code:
if (Keyboard.GetState().IsKeyDown(Keys.G) == true)
{
var fileToOpen = "Name.txt";
var process = new Process();
process.StartInfo = new ProcessStartInfo()
{
UseShellExecute = true,
FileName = fileToOpen
};
process.Start();
process.WaitForExit();
}
However an error occurs and cant find the textfile to open. I did this in a normal consol application and just added a new item textfile to the project and it worked fine in the console application, however in XNA it does not seem to work at all.
Also im really not well educated in file directory things and need a quick fix. The text files are placed in this area:
I hope this is of somehelp im trying to give as much information as possible. Just to note streamwriting to textfiles in the directory location shown in the image link works perfectly fine and i just give the name of the file as shown below:
if (player.GetRec.Intersects(map.sharkRec))
{
using (StreamWriter writer = new StreamWriter("CurrentScore.txt"))
{
writer.Write(time);
}
player.Position = new Vector2(64,100);
mCurrentScreen = ScreenState.leaderboard;
}
However it just didt seem to work when i want to open the textfile in notepad and allow for typing to be done in the textfile in notepad. The reason why i want to open a text file for typing is the user entering there name and i dont have knowledge or the time to do XNA textbox input creation which seems complicated from the tutorials i have seen, which is why i want to open the textfile in notepad for editing. Furthermore this is going to be used on other people's computers so if directorys have to be used i need a directory that will work on other computers as well as my own, just to note directory entering seems to confuse me.
Hope i have given enough information and i really hope someone can help this beginner out here :)
For this to work, your code should be something like this:
using(StreamWriter ...)
{
show textbox so the user can see what he's typing
for each keypress add the letter
exit on ESC button (for example)
delete char on Backspace
etc...
}
Now, I personaly don't recommend this type of code. Open the file, do what you have to do with it and close it. The code below is how I programmed textbox for my game. I hope it helps (you could say this is more a little tutoial for better approach to the problem instead an answer to the exact problem you put up for yourself).
namespace Acircalipse.MenuClasses
{
public class TextBox:MenuItem
{
private string originTitle;
public string Text
{
get
{
return title.Replace(originTitle, "").Replace(Menu.separator, "");
}
}
public int index, max;
public TextBox (string Title, string text = "", int MaxCharacters = 14)
{
originTitle = Title;
index = 0;
max = MaxCharacters;
SetText(text);
}
public void SetText (string text)
{
if (text.Length > max) text = text.Substring(0, max);
title = originTitle + Menu.separator + text;
}
public void ChangeIndex (int howMuch)
{
index += howMuch;
ChangeCar(index);
}
public void AddChar (int index = 0)
{
SetText(Text + Menu.Wheel(index));
}
public void ChangeCar (int index)
{
if(Text.Length > 0)
SetText(Text.Substring(0, Text.Length - 1) + Menu.Wheel(index));
}
public void DeleteChar ()
{
if (Text.Length > 0)
SetText(Text.Substring(0, Text.Length - 1));
}
}
}
Where the Menu.Wheel(int index) is simply an array of available character for user to type in
public static char Wheel(int index)
{
string charWheel = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int max = charWheel.Length;
while (index > max) index -= max;
while (index ("less than" sign here) 0) index += max;
return charWheel[index];
}
Where Menu.separator is a string ": ".
The code is pretty self explanatory.
Now, when using this class, you'll have to have one bool to see if the user has activated this textbox, and if he has, add text on keypress. If the textbox is inactive, just continue your normal update
What you have to understand is that textbox is a simple string witch is updated when textbox is active, and just showed when not active.
Using this simple and on point definition of TextBox, you can simply create your own class that will work the way you want to.
If this answer helped you resolve your problem, please mark it as the solution.
I'm working on a text editor which includes a RichTextBox. One of the features that I want to implement is to show in a TextBox the current Line and Column of the caret of the forementioned RichTextBox at any moment.
Here's part of the code that I use (the rest of my code has nothing to do with my issue):
int selectionStart = richTextBox.SelectionStart;
int lineFromCharIndex = richTextBox.GetLineFromCharIndex(selectionStart);
int charIndexFromLine = richTextBox.GetFirstCharIndexFromLine(lineFromCharIndex);
currentLine = richTextBox.GetLineFromCharIndex(selectionStart) + 1;
currentCol = richTextBox.SelectionStart - charIndexFromLine + 1;
At this point, I should mention that when someone is using a RichTextBox, there are three ways that the caret can change location:
By changing the Text of the RichTextBox
By using the arrow keys on the keyboard
By clicking anywhere on the RichTextBox
The code that I posted above works with no issues in the first two cases. However, it doesn't really work in the third case.
I tried using the Click event and I noticed that the selectionStart variable would always get the value of 0, which means that I always get the same and wrong results. Moreover, using the same code on other events like MouseClick and MouseUp did not solve my problem since selectionStart is 0 even in the duration of these events.
So, how can I get the current Line and column everytime the user clicks on the RichTextBox?
You want something like:
private void richTextBox1_MouseUp(object sender, MouseEventArgs e)
{
RichTextBox box = (RichTextBox)sender;
Point mouseLocation = new Point(e.X, e.Y);
box.SelectionStart = box.GetCharIndexFromPosition(mouseLocation);
box.SelectionLength = 0;
int selectionStart = richTextBox.SelectionStart;
int lineFromCharIndex = box.GetLineFromCharIndex(selectionStart);
int charIndexFromLine = box.GetFirstCharIndexFromLine(lineFromCharIndex);
currentLine = box.GetLineFromCharIndex(selectionStart) + 1;
currentCol = box.SelectionStart - charIndexFromLine + 1;
}
It seems to me that what you really want is to handle the TextBoxBase.SelectionChanged event. Then any action that causes the selection to change will invoke your code, and as an added benefit the current selection will have been updated by the time your event handler is called, and you'll be assured of getting correct values.
If that does not address your specific need, then I must not be understanding the question. In that case, please provide a good, minimal, complete code example that shows clearly what you're trying to do, with a precise description of what that code does and how that's different from what you want it to do.
I'm calling a public method from another class. It takes in a List as a parameter, and goes through the list printing out each item into a text field. The problem is the text field is remaining empty!. I've checked that the list is populated by outputing the item to the console before I put it into the text box, and the text is coming up fine there.
The list contains strings, and should output each string to the textfield followed by a semi colon.
This is the method which is being called:
public void fillAttachment(List<string> attachList)
{
for (int i = 0; i < attachList.Count; i++)
{
Console.WriteLine("List: " + attachList[i]);
txtAttach.Text += attachList[i] + ";";
}
}
I would solve it in this way:
foreach(var attach in attachList)
{
Console.WriteLine(attach);
txtAttach.AppendText(string.Format("{0};", attach));
}
Setting the text property on a text box and it not displaying could be one of the following:
You are not looking at the same control as you are setting the text in
Could you have instantiated a second copy of the form object and it is this form that you are setting the txtAttach text property in?
Could the control that you are expecting to be populated be a different one? Right click the text box that you want the text to appear in click properties and check the name.
Something else is clearing the textbox after you set it
Right click the txtAttach.Text and click Find All References, this will show you all the places that the Text property is referenced - written and read - in your project. This is a very useful way to locate other interaction with this control.
Fomatting is making the text box appear empty
Is the Font too small, or in the same colour as the background. Can you select the text in the text box?
The easiest way to test all of the above is to create a new text control on your form with a different name, change your code to populate it, check that it is indeed populated, then replace the old one.
As an aside, you could also reduce the code with a single line as follows:
public void fillAttachment(List<string> attachList)
{
txtAttach.Text = String.Join(";", attachList.ToArray());
}
Although this obviously skips out the console write line function.
Not sure why yours doesn't work but I would have done it like this...
public void fillAttachment(List<string> attachList)
{
string result = "";
//OR (if you want to append to existing textbox data)
//string result = txtAttach.Text;
for (int i = 0; i < attachList.Count; i++)
{
Console.WriteLine("List: " + attachList[i]);
result += attachList[i] + ";";
}
txtAttach.Text = result;
}
Does that work for you? If not then there is something else very wrong that is not obvious from your code
I am programatically adding text in a custom RichTextBox using a KeyPress event:
SelectedText = e.KeyChar.ToString();
The problem is that inserting text in such a way doesn't trigger the CanUndo flag.
As such, when I try to Undo / Redo text (by calling the Undo() and Redo() methods of the textbox), nothing happens.
I tried programatically evoking the KeyUp() event from within a TextChanged() event, but that still didn't flag CanUndo to true.
How can I undo text that I insert without having to create lists for Undo and Redo operations ?
Thanks
I finally decided to create my own undo-redo system using stacks.
Here's a quick overview of how I did it :
private const int InitialStackSize = 500;
private Stack<String> undoStack = new Stack<String>(InitialStackSize);
private Stack<String> redoStack = new Stack<String>(InitialStackSize);
private void YourKeyPressEventHandler(...)
{
// The user pressed on CTRL - Z, execute an "Undo"
if (e.KeyChar == 26)
{
// Save the cursor's position
int selectionStartBackup = SelectionStart;
redoStack.Push(Text);
Text = undoStack.Pop();
// Restore the cursor's position
SelectionStart = selectionStartBackup;
}
// The user pressed on CTRL - Y, execute a "Redo"
if (e.KeyChar == 25)
{
if (redoStack.Count <= 0)
return;
// Save the cursor's position
int selectionStartBackup = SelectionStart + redoStack.ElementAt(redoStack.Count - 1).Length;
undoStack.Push(Text);
Text = redoStack.Pop();
// Restore the cursor's position
SelectionStart = selectionStartBackup;
return;
}
undoStack.Push(Text);
SelectedText = e.KeyChar.ToString();
}
It's just an idea but what if you set the caret position to where you would insert your text and instead of modifying the Text property, just send the keys?
SendKeys.Send("The keys I want to send");
There are bound to be quirks but as I said, it's just an idea.
You can use TestBox.Paste. The documentation in the class overview, saying "Sets the selected text to the specified text without clearing the undo buffer.", seems confusing. I have just tried it and it sets the Undo as expected.
Is spite of its name it has no relation to Clipboard at all, it just replaces the currently selected text with the text you provide as an argument, and therefore seems just to do what the question asks for, in very simple manner.