Emphyrio
Emphyrio

Reputation: 61

StringBuilder append and Listbox

I came across a perculiar problem with StringBuilder and Listbox.
I made a Listbox on a WinForm and called it lbOut and a StringBuilder string named log.

The code:

public partial class formMain : Form
{
 StringBuilder log = new StringBuilder();
 public formMain()
 {
   InitializeComponent();
   log.AppendLine("This is a test");
   lbOut.Items.Add(log);
   log.AppendLine("Second line");
 }
}

If I execute this code, I should get:

This is a test

Instead I get:

This is a testSecond line

Why is that?
I mean, "Second line" isn't even add to lbOut.


I working with Visual Studio 2010, .Net 4.0 on a Vista.

Update: Thanks everyone for the answers. I've learned a bit more today. I can't vote up (yet), but I was very pleased with the answers given.
I've forgat about the object references

Upvotes: 1

Views: 1422

Answers (7)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

ListBox.ObjectCollection.Add documentation provides a hint:

If the DisplayMember property does not have a member specified, the ListBox then calls the ToString method of the object to obtain the text to display in the list.

The call of ToString is not happening right away, when you add the object. It happens only when ListBox needs to render the object that you have added. It is the object, not its string representation, that ListBox keeps. Therefore, every time that you change the object, the text in the ListBox is going to change.

If you do not want this to happen, you can add an immutable object, such as a string, to your ListBox.

Upvotes: 1

Russ
Russ

Reputation: 124

This is because you are doing it in the constructor. Internally the CLR sees the 2 log.AppendLine calls both a applied so the value of log is actually "This is a testSecond line".

If you do the exact same code in Form_Load you will get what you expect.

Upvotes: 0

neo
neo

Reputation: 2042

You add the log object to the list. And it gets updated by the second text change. If you want add log.toString() to the list, then you should get what you want.

Upvotes: 0

Patrick Hofman
Patrick Hofman

Reputation: 156928

That happens because you pass in the StringBuilder, and not the string.

Hence, when it is shown in the UI, it calls ToString on the StringBuilder, which in the meantime has changed it's value.

A possible solution is to pass in the string:

lbOut.Items.Add(log.ToString());

Or, even better, create a Log method that logs the string, and adds it to the ListBox.

Something like the method below. Note I use Invoke if required, so the Log method is thread safe:

private void Log(string text)
{
    log.AppendLine(text);

    if (lbOut.InvokeRequired)
    {
        lbOut.Invoke((MethodInvoker)delegate()
        {
            lbOut.Items.Add(text);
        });
    }
    else
    {
        lbOut.Items.Add(text);
    }
}

Upvotes: 2

workabyte
workabyte

Reputation: 3755

when you add log to lbOut you are adding a pointer to the object log not the value of log.

you would want to print/log that out before the next append or add the value of log

lbOut.Items.Add(log.ToString());

Upvotes: 0

Sean
Sean

Reputation: 62472

Your listbox has a reference to the StringBuilder and after you added it to the listbox you updated the StringBuilder. Just use a regular string for this.

Upvotes: 0

Koen
Koen

Reputation: 644

But, you add log to your lbOut. Than you add something to log. It would be weird if it was not changed on your lbOut.

Upvotes: 0

Related Questions