我在RichTextBox
控件中保留多行颜色时遇到问题。
问题 :将新消息附加到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
。这些行的开头应为Color.Black
控件的常规RichTextBox
颜色。附加消息SUCCESS
的颜色应基于为该消息提供的Status
进行着色。因此,例如,第4行和第6行说SUCCESS
,并且收到的状态为成功。第5行表示成功,但是遇到警告或次要问题,并且收到警告状态。收到第7行的错误状态。正如您将在下面提供的代码中看到的那样,每个状态都有其自己的颜色。该状态之前的同一行上的文本应保持Color.Black
颜色。
重要说明
该消息及其状态是分别发送的,这是由于每个操作所花费的时间不同,因此用户应该知道某个进程正在发生,并且到目前为止该进程还没有状态报告。 / p>
当前代码
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();
}
依赖项
public enum UpdateStatus { NoOperation, Error, Warning, Success }
public class StatusEventArgs {
public UpdateStatus Status { get; set; }
public string Message { get; set; }
}
我已经在StackOverflow上查看了几个相关的问题,(this one)是最接近我需要的问题;但是,该帖子建议选择特定范围内的文本,但仍然无法正常工作。它执行了与当前代码完全相同的操作。请记住,实现答案时我没有循环或事件列表。我也不能利用建议在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
因此上述 Update 2 方法有效,问题在于以下代码行:
rtb.Text = rtb.Text.TrimEnd('\n');
这行代码使整个控件删除所有当前的格式,这使我感到奇怪,如果我想保留现有格式,是否应该使用Rtf
属性?我想我会尝试一下并找出答案。 Per MSDN:
Text属性不会返回有关应用于RichTextBox内容的格式的任何信息。若要获取富文本格式(RTF)代码,请使用Rtf属性。在RichTextBox控件中可以输入的文本量仅受可用系统内存的限制。
最终更新
在我的情况下,Rtf
属性不起作用(将rtb.Text
交换为rtb.Rtf
。尝试了其他几种方法,但没有一种起作用。但是,对于像我这样的人传递一条直线消息并在打印时添加新行,可以采用带前缀的新行的方法,然后可以添加一些逻辑来防止它不应该出现在此处。这消除了{{1 }},因此可接受的答案就可以了:
TrimEnd
答案 0 :(得分:1)
此问题是由替换Text属性中的字符串引起的:
class CollectionTypeBaseModel<TObject extends YObject, YObject extends IConvertable> extends BaseCollectionTypeResponseModel<YObject>{
constructor(private converter: (obj: TObject) => any) { super(); }
private _responseData: Array<TObject> = []
get ResponseData(): Array<TObject>{
if (this._responseData == null || this._responseData.length < 1){
this.responseData.forEach((data)=>{
this._responseData.push(this.converter(data));
});
}
return this.responseData;
}
// ...
}
objectData = JSON.parse(jsonData);
var myCastedData = Object.assign(
new CollectionTypeBaseModel<PlatformFunctionalModel, PlatformResponseModel>(
(data) => Object.assign(new PlatformModel(), data)),
objectData)
这行代码使整个控件删除所有当前的格式Per MSDN:
Text属性不会返回有关应用于RichTextBox内容的格式的任何信息。若要获取富文本格式(RTF)代码,请使用Rtf属性。在RichTextBox控件中可以输入的文本量仅受可用系统内存的限制。
否则,代码可以简化为:
rtb.Text = rtb.Text.TrimEnd('\n');