How to color specific lines in richTextBox in specific color depending on the color setting when using StringBuilder?

rhodanny 166 Reputation points
2023-12-30T20:46:01.86+00:00

I created a Logger module file class and in the class I extended it and added my own custom stringbuilder and I want to be able easy to color lines in the richTextBox1 control in form1 designer depending on the color setting in the Logger class for each stringbuilder line.

In the class Logger I extended the class to use my own custom stringbuilder with a color property.

// Logger.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Downloading_Files
{
    public class Logger
    {
        private RichTextBox richTextBoxLogger;
        private CustomStringBuilder sb;
        private TimeSpan elapsedTime; // Add a private variable to store elapsed time

        public Logger(RichTextBox richTextBoxLogger)
        {
            this.richTextBoxLogger = richTextBoxLogger;
            this.sb = new CustomStringBuilder(this);
        }

        public void LogStartDownload(string fileName, string fileUrl)
        {
            sb.AppendLine($"[Start Time]: {DateTime.Now}", Color.Yellow);
            sb.AppendLine($"[File Name]: {fileName}", Color.Yellow);
            sb.AppendLine($"[File URL]: {fileUrl}", Color.Yellow);
            sb.AppendLine($"[Status] Downloading in progress", Color.Yellow);
        }

        public void LogDownloadProgress(double percentage, double speed, long totalBytes, long contentLength, TimeSpan elapsedTime)
        {
            this.elapsedTime = elapsedTime; // Store elapsed time for later use
            sb.AppendLine($"[Progress]: {percentage}%", Color.Yellow);
            sb.AppendLine($"[Downloaded]: {FormatBytes(totalBytes)} / {FormatBytes(contentLength)}", Color.Yellow);
            sb.AppendLine($"[Download Speed]: {FormatBytes(speed)}/s", Color.Yellow);
            sb.AppendLine($"[Time Elapsed]: {FormatTimeSpan(elapsedTime)}", Color.Yellow);
        }

        public void LogDownloadCompleted(bool isSuccess, TimeSpan elapsedTime, bool isCancelled)
        {
            this.elapsedTime = elapsedTime; // Update elapsed time when the download completes
            if (!isCancelled)
            {
                sb.Replace($"[Status] Downloading in progress", $"[Status]: Download {(isSuccess ? "Completed Successfully" : "Failed")}", Color.Yellow);
                //logBuilder.AppendLine($"[Status]: Download {(isSuccess ? "Completed Successfully" : "Failed")}");
            }
            else
            {
                sb.Replace($"[Status] Downloading in progress", $"[Status]: Download Cancelled", Color.Red);
                //logBuilder.AppendLine($"[Status]: Download Cancelled");
            }
            sb.AppendLine($"[Elapsed Time]: {FormatTimeSpan(elapsedTime)}", Color.Yellow);
            sb.AppendLine($"[End Time]: {DateTime.Now}", Color.Yellow);
            sb.AppendLine("", Color.Yellow); // Add a space line between download logs
        }

        public void LogDownloadCompleted()
        {
            sb.AppendLine("[Status]: All downloads completed", Color.Yellow);
            sb.AppendLine("", Color.Yellow);
        }

        public void LogDownloadCancelled()
        {
            sb.AppendLine("[Status]: Operation cancelled and all file have been deleted", Color.Yellow);
            sb.AppendLine("", Color.Yellow);
        }

        public string GetLog(RichTextBox richTextBoxLogger)
        {
            richTextBoxLogger.Clear();
            string log = sb.ToString();
            richTextBoxLogger.AppendText(log); // Set RichTextBox text as RTF to preserve formatting
            //logBuilder.Clear(); // Clear the log after appending it once

            return log;
        }

        private string FormatBytes(double bytes)
        {
            const int scale = 1024;
            string[] units = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };

            if (bytes <= 0)
            {
                return "0 B";
            }

            int magnitude = (int)Math.Floor(Math.Log(bytes, scale));
            int index = Math.Min(magnitude, units.Length - 1);

            double adjustedSize = bytes / Math.Pow(scale, index);
            string format = (index >= 0 && index < 3) ? "F2" : "F0";

            return $"{adjustedSize.ToString(format)} {units[index]}";
        }

        private string FormatTimeSpan(TimeSpan timeSpan)
        {
            return $"{timeSpan.Hours:D2}:{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}";
        }

        private void UpdateRichTextBox()
        {
            richTextBoxLogger.Clear();
            foreach (var line in sb.Lines)
            {
                richTextBoxLogger.SelectionStart = richTextBoxLogger.TextLength;
                richTextBoxLogger.SelectionLength = 0;
                richTextBoxLogger.SelectionColor = line.Color;
                richTextBoxLogger.AppendText(line.Text + "\n");
                richTextBoxLogger.SelectionColor = richTextBoxLogger.ForeColor; // Reset color
            }
        }

        public class CustomStringBuilder
        {
            private Logger logger; // Reference to the Logger instance

            public CustomStringBuilder(Logger logger)
            {
                this.logger = logger;
            }

            public List<CustomStringBuilderItem> Lines { get; } = new List<CustomStringBuilderItem>();

            public void AppendLine(string text, Color color)
            {
                Lines.Add(new CustomStringBuilderItem { Text = text, Color = color });
            }

            public void Replace(string oldText, string newText, Color textColor)
            {
                var line = Lines.Find(l => l.Text.Contains(oldText));
                if (line != null)
                {
                    line.Text = line.Text.Replace(oldText, newText);
                    line.Color = textColor;
                }

                logger.UpdateRichTextBox(); // Call UpdateRichTextBox through the Logger instance
            }

            public override string ToString()
            {
                StringBuilder result = new StringBuilder();
                foreach (var line in Lines)
                {
                    result.AppendLine(line.Text);
                }
                return result.ToString();
            }
        }

        public class CustomStringBuilderItem
        {
            public string Text { get; set; }
            public Color Color { get; set; }
        }
    }
}

for example this line I want it to be colored in Red in the richTextBox1 I have in the form1 designer:

sb.Replace($"[Status] Downloading in progress", $"[Status]: Download Cancelled", Color.Red);

this is how I'm using it in form1

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Logger_Testing
{
    public partial class Form1 : Form
    {
        Logger logger;
        private TimeSpan _elapsedTime;

        public Form1()
        {
            InitializeComponent();

            logger = new Logger(richTextBox1);

            logger.LogStartDownload("Hello", "Hello World");
            logger.GetLog(richTextBox1);
        }

        private void btnReplace_Click(object sender, EventArgs e)
        {
            logger.LogDownloadCompleted(false, _elapsedTime, true);
            logger.LogDownloadCancelled();

            logger.GetLog(richTextBox1);
        }
    }
}

but the result is when I click the button to replace the text is that the replaced text is also in yellow like all the rest of the text in the richTextBox1 and not in red for the specific replace line.

The line [Status]: Download Cancelled, should be colored in red.

nocolor1

Developer technologies | Windows Forms
Developer technologies | C#
{count} votes

Accepted answer
  1. WayneAKing 4,931 Reputation points
    2023-12-31T00:21:02.05+00:00

    I haven't tested your code, notably the areas where you are selecting text and colorizing.

    But going back to the code you posted in your earlier thread and altering the example I posted in that thread of the GetLog changes, modifying it to color the Status line should work. Provided there is no conflict with what you are now doing in your latest code to alter colors in the RichTextbox.

        richTextBoxLogger.Clear(); // erase old contents
    
        string log = logBuilder.ToString();
        richTextBoxLogger.AppendText(log);
        
        //logBuilder.Clear(); // Clear the log after appending it once
    
        int loc = 0;
        loc = richTextBoxLogger.Text.IndexOf("[Status]", loc);
        richTextBoxLogger.Select(loc, richTextBoxLogger.Text.IndexOf('\n', loc) - loc);
        richTextBoxLogger.SelectionColor = System.Drawing.Color.Red;
        richTextBoxLogger.SelectionLength = 0;
    
        return log;
    
    
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.