问题描述:

I have a RichTextBox for a simple chat where I add lines programmatically.

I make the usernames bold and the messages in regular style.

After some lines I want to delete the first lines to keep the chat in a acceptably length. But when I do so I lose the text format and everything appears bold. What am I doing wrong and how can I fix this?

EDIT

I could solve the problem where I wasn't able to delete the first line.

I had to set the the ReadOnly property to false. Even though I was able to add new lines it prevented deleting lines. So the following code works to delete lines. Thanks to @TaW!

if (ChatText.Lines.Length >= 10)

{

int p = 0; int count = 0;

do

{

p = ChatText.Text.IndexOf("\n\r");

if (p >= 0)

{

ChatText.SelectionStart = p;

ChatText.SelectionLength = 2; // length of "\n\r"

ChatText.SelectedText = "\n";

count++;

}

}

while(p >= 0);

int nll = 1; // <<=== pick the length of your new line character(s)!!

int pS = ChatText.Lines.Take(0).Select(x => x.Length + nll).Sum() - nll;

int pL = ChatText.Lines.Take(1).Select(x => x.Length + nll).Sum() - nll;

if (pS < 0) { pS = 0; pL++; }

ChatText.SelectionStart = pS;

ChatText.SelectionLength = pL - pS;

ChatText.Cut();

}

//////////////////////////////////

// now add new lines

//////////////////////////////////

string[] chatstr;

// string text is given as method parameter

chatstr = text.Split(new string[] { ": " }, 2, StringSplitOptions.None);

// go to the end of the text

ChatText.SelectionStart = ChatText.Text.Length;

ChatText.SelectionLength = 0;

// make text bold

ChatText.SelectionFont = new Font(ChatText.Font, FontStyle.Bold);

// add username (chatstr[0]) and colon

ChatText.AppendText(chatstr[0] + ": ");

// make text regular

ChatText.SelectionFont = new Font(ChatText.Font, FontStyle.Regular);

// add message (chatstr[1])

ChatText.AppendText(chatstr[1] + "\n");

// and finaly scroll down

ChatText.ScrollToCaret();

So deleting lines works and new lines are added as intended. Finaly!

solved :)

网友答案:

Never change the Text of a RichtTextBox if it contains any formatting.

Changing the Lines property (by Skip) is just another way to change the Text.

Instead only use the functions the RTB provides: Always start by selecting the portion, then apply one or more of the functions and/or set one or more of the properties..:

To delete portions use Cut.

Here is a function that will delete a number of entire lines:

void DeleteLines(RichTextBox rtb, int fromLine, int count)
{
    int p1 = rtb.GetFirstCharIndexFromLine(fromLine);
    int p2 = rtb.GetFirstCharIndexFromLine(fromLine + count);

    rtb.SelectionStart = p1;
    rtb.SelectionLength = p2 - p1;

    bool readOnly = rtb.ReadOnly;  // allow change even when the RTB is readonly
    rtb.ReadOnly = false; ;
    rtb.Cut();
    rtb.ReadOnly = readOnly;
}

Trying to keept the formatting alive yourself is a tedious and error-prone waste of your time.

In addition to font properties you would also have to resore all other things you can set with the SelectedXXX properties, like colors, alignment, spacing etc etc..

To delete the first 3 lines use:

DeleteLines(yourRTB, 0, 3);

To restrict the text to 10 lines use:

DeleteLines(yourRTB, 0, yourRTB.Lines.Length - 10);

Note that the function above should have a few checks for valid input; I left them out as the checks somehow need a decision what to do, if count or fromLine if greater than Lines.Length or if fromLine is negative..

While we are at it, here is how to append a bold line:

yourRTB.SelectionStart = yourRTB.Text.Length;
yourRTB.SelectionLength = 0;
using (Font font = new Font(yourRTB.SelectionFont, FontStyle.Bold))
    yourRTB.SelectionFont = font;
yourRTB.AppendText(yourNewLine + textOfNewLine);

Of course it really shold go into a reuseable function that the the bolding as a parameter..

Update:

since you are using WordWrap you may prefer this function. It deletes the actual lines, not the visible ones:

void DeleteLinesWW(RichTextBox rtb, int fromLine, int count)
{
    int nll = 1;  // <<===  pick the length of your new line character(s)!!
    int pS = rtb.Lines.Take(fromLine).Select(x => x.Length + nll).Sum() - nll;
    int pL = rtb.Lines.Take(fromLine + count).Select(x => x.Length + nll).Sum() - nll;
    if (pS < 0) { pS = 0; pL++; }
    rtb.SelectionStart = pS;
    rtb.SelectionLength = pL - pS ;

    bool readOnly = rtb.ReadOnly;
    rtb.ReadOnly = false;   // allow change even when the RTB is readonly
    rtb.Cut();
    rtb.ReadOnly = readOnly;  
  }

A word on NewLine: Do note that I have not used the Environment.NewLine constant as it not really a good idea. If you add multiline text to the RichTextBox in the designer and then look at it you will see that it uses simple '\n' new lines, no returns, no NL-CR, just '\n'. So this seems to be the generic way in a winforms RTB and I recommend using it..

The new function relies on all lines having a newline of the same length!

To make sure you can use this replacement function:

int RTBReplace(RichTextBox rtb, string oldText, string newText)
{
    int p = 0; int count = 0;
    do
    {
        p = richTextBox1.Text.IndexOf(oldText);
        if (p >= 0)
        {
            richTextBox1.SelectionStart = p;
            richTextBox1.SelectionLength = oldText.Length;
            richTextBox1.SelectedText = newText;
            count ++;
        }
    }
    while (p >= 0);
    return count;
}

Calling it like this:

RTBReplace(yourrichTextBox, "\r\n", "\n");

Update 2:

Here is an example how to add your chat lines:

private void button1_Click(object sender, EventArgs e)
{
    string cLine = "Taw:  Hello World";  // use your own lines!
    var  chatstr = cLine.Split(new string[] { ": " }, 2, StringSplitOptions.None);
    AppendLineBold(yourrichTextBox, "\n" + chatstr[0], true);
    AppendLineBold(yourrichTextBox, chatstr[1], false);
    yourrichTextBox.ScrollToCaret();
}

void AppendLineBold(RichTextBox rtb, string text, bool bold)
{
    rtb.SelectionStart = richTextBox1.Text.Length;
    rtb.SelectionLength = 0;
    using (Font font = new Font(rtb.SelectionFont, 
                                bold ? FontStyle.Bold : FontStyle.Regular))
        rtb.SelectionFont = font;
    rtb.AppendText(text);
}

Update 3:

Looks like the ReadOnly property disallows the use of Cut. So we need to temporatily allow changes.

Funny: SelectedText can't be set either, but AppendText works fine..

网友答案:

To keep text formatting, you can also try the following (it's a little shorter and should also do the trick)

string text = "Username: hello this is a chat message";
// delete the first line when after 10 lines
if (ChatText.Lines.Length >= 10)
{
    ChatText.SelectionStart = 0; // set SelectionStart to the beginning of chat text (RichTextBox)
    ChatText.SelectionLength = ChatText.Text.IndexOf("\n", 0) + 1; // select the first line
    ChatText.SelectedText = ""; // replace by an empty string
    ChatText.SelectionStart = ChatText.Text.Length; // set SelectionStart to text end to make SelectionFont work for appended text
}
// split the string in chatstr[0] = username, chatstr[1] = message
string[] chatstr = text.Split(new string[] { ": " }, 2, StringSplitOptions.None);
// make the username bold
ChatText.SelectionFont = new Font(ChatText.Font, FontStyle.Bold);
ChatText.AppendText(chatstr[0] + ": ");
// make the message regular
ChatText.SelectionFont = new Font(ChatText.Font, FontStyle.Regular);
ChatText.AppendText(chatstr[1] + Environment.NewLine);
ChatText.ScrollToCaret();
相关阅读:
Top