在RichTextBox控件中保留多行颜色是有问题的。
prior :当将新消息附加到RichTextBox控件并需要将消息绘制到特定颜色时,新消息之前的所有消息都将被绘制为灰色,并保持不变。
问题:当消息被追加时,我如何只绘制需要绘制的消息。
我有一个状态更新事件,它由一个后台线程触发,该线程将该更新的状态和消息添加到List<StatusEventArgs>中。然后,我清除我的RichTextBox控件并开始向它添加更新。我可能应该将它移到仅仅附加文本,而不是每次清除它和重建它;然而,我这样做是为了保留颜色。消息是一种向用户表示的日志形式,因此他们知道在这个长时间运行的过程中当前发生了什么。例如:
1. Running process.
2. Starting something.
3. Doing something else.
4. Doing something. SUCCESS.
5. Doing something. SUCCESS.
6. Doing something. SUCCESS.
7. Doing something. FAIL. Attempting to continue.
8. Doing some other thing.
9. Complete.在上面的例子中,第4-7行有附加的文本,如SUCCESS。这些行的开头应该是RichTextBox控件的正常RichTextBox颜色。附加的消息SUCCESS应该根据为该消息提供的Status进行着色。因此,例如,第4行和第6行表示SUCCESS,并以成功的状态接收。第5行表示成功,但遇到警告或小问题,并收到警告状态。接收到的第7行具有错误状态。正如您在下面提供的代码中所看到的,每个状态都有自己的颜色。同一行状态之前的文本应该保持Color.Black颜色。
重要注
消息和它的状态是分开发送的,这是由于每次操作花费了不同的时间,因此用户应该知道进程正在发生,到目前为止还没有来自该进程的状态报告。
电流码
private List<StatusEventArgs> events = new List<StatusEventArgs>();
private void StatusUpdate(object sender, StatusEventArgs e) {
events.Add(e);
rtb.Text = string.Empty;
foreach (StatusEventArgs sea in events) {
Status s = sea.Status;
rtb.SelectionStart = rtbConsole.TextLength;
rtb.SelectionLength = 0;
rtb.SelectionColor = rtbConsole.ForeColor;
if (s == UpdateStatus.Error || s == UpdateStatus.Warning || s == UpdateStatus.Success) {
rtb.Text = rtb.Text.TrimEnd('\n');
rtb.SelectionStart = rtbConsole.TextLength;
rtb.SelectionLength = 0;
switch (s) {
case Status.Error: rtb.SelectionColor = Color.DarkRed; break;
case Status.Warning: rtb.SelectionColor = Color.DarkGoldenrod; break;
case Status.Success: rtb.SelectionColor = Color.DarkGreen; break;
}
}
rtb.AppendText($"{sea.Message}\n");
}
rtb.ScrollToCaret();
}Dependencies
public enum UpdateStatus { NoOperation, Error, Warning, Success }
public class StatusEventArgs {
public UpdateStatus Status { get; set; }
public string Message { get; set; }
}我已经在StackOverflow上看过几个相关的问题,(这一个)是最接近我需要的问题;然而,post建议在一个特定的范围内选择文本,但这仍然不起作用。它做的和我现在的代码完全一样。请记住,在实现答案时,我没有循环或事件列表。我也不能利用该建议在RichTextBox中绘制特定的文本,因为正如我在上面的示例中所述,SUCCESS消息可以有两种不同的颜色,这取决于是否收到警告。
尝试代码
int previousLength = rtb.TextLength;
UpdateStatus s = e.Status;
bool status = s == UpdateStatus.Error || s == UpdateStatus.Warning || s == UpdateStatus.Success;
if (status)
rtb.Text = rtb.Text.TrimEnd('\n');
rtb.AppendText($"{e.Message}\n");
rtb.ScrollToCaret();
if (status) {
rtb.Select(previousLength, rtb.TextLength);
switch (s) {
case Status.Error: rtb.SelectionColor = Color.DarkRed; break;
case Status.Warning: rtb.SelectionColor = Color.DarkGoldenrod; break;
case Status.Success: rtb.SelectionColor = Color.DarkGreen; break;
}
rtb.Select(0, 0);
}老实说,我觉得我只是错过了一步,或者需要做点不同的事情。每次收到新的状态消息时,重新着色所有的状态消息,对于这样一个琐碎的任务来说,似乎有点麻烦。
更新
我测试了Dictionary<int[], UpdateStatus>方法,它的工作方式符合我的需要;然而,我认为对于如此简单的事情来说,这是太过分了:
private Dictionary<int[], UpdateStatus> selections = new Dictionary<int[], UpdateStatus>();
private void StatusUpdate(object sender, StatusEventArgs e) {
int previousLength = rtbConsole.TextLength;
UpdateStatus s = e.Status;
bool status = s != UpdateStatus.NoOperation;
if (status)
rtb.Text = rtb.Text.TrimEnd('\n');
rtb.AppendText($"{e.Message}\n");
rtb.ScrollToCaret();
if (status)
selections.Add(new int[] { previousLength, rtb.TextLength }, s);
// Set all basic text to black.
rtb.Select(0, rtb.TextLength);
rtb.SelectionColor = Color.Black;
// Color all status messages.
foreach (int[] selection in selections.Keys) {
rtb.Select(selection[0], selection[1]);
switch (selections[selection])
case Status.Error: rtb.SelectionColor = Color.DarkRed; break;
case Status.Warning: rtb.SelectionColor = Color.DarkGoldenrod; break;
case Status.Success: rtb.SelectionColor = Color.DarkGreen; break;
}
// Prevent messages in-between status messages from being colored.
rtb.Select(selection[1], rtb.TextLength);
rtb.SelectionColor = Color.Black;
}
}更新2
这是我对LarsTech在下面的文章的实现。它仍然将当前状态消息之前的所有内容绘制为黑色:
UpdateStatus s = e.Status;
if (s != UpdateStatus.NoOperation)
rtb.Text = rtb.Text.TrimEnd('\n');
Color textColor = Color.Black;
switch (selections[selection]) {
case Status.Error: textColor = Color.DarkRed; break;
case Status.Warning: textColor = Color.DarkGoldenrod; break;
case Status.Success: textColor = Color.DarkGreen; break;
}
rtb.Select(rtb.TextLength, 0);
rtb.SelectionColor = textColor;
rtb.AppendText($"{e.Message}\n");
rtb.ScrollToCaret();更新3
所以上面的更新2方法可以工作,问题是下面的代码行:
rtb.Text = rtb.Text.TrimEnd('\n');这一行代码会导致整个控件删除所有当前格式,这让我怀疑,如果我想保持我已经拥有的格式,我应该使用Rtf属性吗?我想我会试试的,然后找出答案。每MSDN
Text属性不返回应用于RichTextBox内容的格式设置的任何信息。若要获取富文本格式( Rtf )代码,请使用Rtf属性。可以在RichTextBox控件中输入的文本数量仅受可用系统内存的限制。
最终更新
在我的情况下,Rtf属性无法工作(只需将rtb.Text转换为rtb.Rtf。试过其他几种方法,但都没有用。但是,对于那些(像我一样)正在传递一条直线消息并在打印时附加一条新行的人,您可以采用前缀新行的方式。然后,您可以添加一些逻辑来防止它在不应该存在的情况下发生。这就消除了对TrimEnd的需求,因此所接受的答案将运行得很好:
// Field
bool firstUpdate = true;
private void StatusUpdate(...) {
UpdateStatus s = e.Status;
Color textColor = Color.Black;
switch (selections[selection]) {
case Status.Error: textColor = Color.DarkRed; break;
case Status.Warning: textColor = Color.DarkGoldenrod; break;
case Status.Success: textColor = Color.DarkGreen; break;
}
string newline = firstUpdate || s != Status.NoOperation ? string.Empty : "\n";
rtb.Select(rtb.TextLength, 0);
rtb.SelectionColor = textColor;
rtb.AppendText($"{newline}{e.Message}");
rtb.ScrollToCaret();
if (firstUpdate)
firstUpdate = false;
}发布于 2018-07-26 18:54:22
此问题是由替换Text属性中的字符串引起的:
rtb.Text = rtb.Text.TrimEnd('\n');这一行代码将导致整个控件删除所有当前格式的每MSDN。
Text属性不返回应用于RichTextBox内容的格式设置的任何信息。若要获取富文本格式( Rtf )代码,请使用Rtf属性。可以在RichTextBox控件中输入的文本数量仅受可用系统内存的限制。
否则,可以将代码简化为:
Color textColor = Color.Black;
switch (e.Status) {
case Status.Error: textColor = Color.DarkRed; break;
case Status.Warning: textColor = Color.DarkGoldenrod; break;
case Status.Success: textColor = Color.DarkGreen; break;
}
rtb.Select(rtb.TextLength, 0);
rtb.SelectionColor = textColor;
rtb.AppendText($"{e.Message}\n");
rtb.ScrollToCaret();https://stackoverflow.com/questions/51545498
复制相似问题