I'm working on a WPF application, where I need a space to frequently display lines of colored text, in a console-like manner (new line displays at the bottom, the rest is moving up). I've decided to use a RichTextBox control named "outputBox" for that purpose:
<RichTextBox Name="outputBox"
Grid.Row="0"
Background="Black"
Foreground="White"
Margin="10"
FontSize="14"
IsReadOnly ="True"
Focusable="False"
FontFamily="Consolas"
VerticalScrollBarVisibility="Visible"
>
I've also created the following method for appending new text lines (each of different color) to the outputBox:
private void PrintMessage(string msg, MessageType type = MessageType.Default)
{
TextRange tr = new(this.Window.outputBox.Document.ContentEnd, this.Window.outputBox.Document.ContentEnd);
tr.Text = "\n" msg;
switch (type)
{
case (MessageType.Default):
tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.LightGray);
break;
case (MessageType.UserInput):
tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Aqua);
break;
case (MessageType.SystemFeedback):
tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.DarkSalmon);
break;
}
Window.outputBox.ScrollToEnd();
}
The issue is, when the outputBox reaches enormous amount of text lines (e.g. 100 000), the application suffers significant performance drop. To remedy this, I want to set a limit of text lines on the outputBox, so when it reaches that limit, the very first top line is removed/cleared without losing text formatting of the remaining text lines. How to accomplish this?
CodePudding user response:
The code will have better performance if a text to the RichTextBox will be added to the Inlines
collection of the Paragraph
.
But to improve performance when shrinking a document to limit some amount of lines in it I would recommend to limit the paragraph size.
The code below implements this logic:
public static class RichTextBoxExt
{
public static void PrintMessage(this RichTextBox rtb, string msg, MessageType type = MessageType.Default)
{
// Maximum of blocks in the document
int MaxBlocks = 500;
// Maximum of lines in one block (paragraph)
int InlinesPerBlock = 500;
SolidColorBrush brush = Brushes.LightGray;
switch (type)
{
case MessageType.UserInput:
brush = Brushes.Aqua;
break;
case MessageType.SystemFeedback:
brush = Brushes.DarkSalmon;
break;
}
// Get the latest block in the document and try to append a new message to it
if (rtb.Document.Blocks.LastBlock is Paragraph paragraph)
{
var nl = Environment.NewLine;
// If the current block already contains the maximum count of lines create a new paragraph
if (paragraph.Inlines.Count >= InlinesPerBlock)
{
nl = string.Empty;
paragraph = new Paragraph();
rtb.Document.Blocks.Add(paragraph);
}
paragraph.Inlines.Add(new Run(nl msg) { Foreground = brush });
}
if (rtb.Document.Blocks.Count >= MaxBlocks)
{
// When the number of lines more that (MaxBlocks-1)*InlinesPerBlock remove the first block in the document
rtb.Document.Blocks.Remove(rtb.Document.Blocks.FirstBlock);
}
}
}
And perhaps the paragraph Margin
should be adjusted for better visual:
<RichTextBox x:Name="rtb" Margin="3" VerticalScrollBarVisibility="Auto">
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="2,0,0,2"/>
</Style>
</RichTextBox.Resources>
<FlowDocument>
<Paragraph>
</Paragraph>
</FlowDocument>
</RichTextBox>
And finally to add a message:
rtb.PrintMessage("Some_Message");
rtb.ScrollToEnd();