c# - RichTextBox多行颜色(仅适用于部分行颜色)

标签 c# winforms richtextbox

我在保留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颜色。

重要的提示

消息及其状态是分别发送的,这是由于每个操作所花费的时间不同,因此用户应该知道一个进程正在发生,并且到目前为止,该进程还没有状态报告。

当前代码

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。尝试了几种其他方法,但没有任何作用。但是,对于那些(像我一样)传递直接信息并附加新内容的人当您打印它时,您可以采用添加新行作为前缀的方法,然后可以添加一些逻辑来防止它不应该出现在该行中,从而消除了对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;
}

最佳答案

该问题是由替换Text属性中的字符串引起的:

rtb.Text = rtb.Text.TrimEnd('\n');


这行代码使整个控件删除所有当前的格式Per 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();

关于c# - RichTextBox多行颜色(仅适用于部分行颜色),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51545498/

相关文章:

c# - 使用扩展 ASCII 在 WinForms RichTextBox 控件中创建树

c# - 如何使用C#添加保存文件对话框

c# - 如何将表单添加到控制台应用程序以便用户可以选择文件?

c# - TcpClient应该用于这种情况吗?

c# - 如何知道我的 DependencyProperty 是否设置为 NULL?

c# - 跨进程事件 - 可靠地释放所有服务员

c# - 如何使用c#绘制图表?

winforms - 表单加载时停止触发组合框的 selectedIndexChanged 事件

winforms - 使用更新的数据更新数据 GridView

richtextbox - 在 Sitecore 富文本字段中插入动态文本