yo3hcv
yo3hcv

Reputation: 1669

c# ListView cross thread, delegate not working

Trying to make a cross thread log to ListView

private enum LogType {Info, Error, Warning};     
private delegate void AddItemCallback(string line, LogType lt);

private void LogAddInfo(string line, LogType lt)
{

    if (lvLog.InvokeRequired)
    {
        AddItemCallback d = new AddItemCallback(LogAddInfo);
        this.Invoke(d, new { line, lt }); // <-- here is the problem
    }
    else
    {
        // code that adds item to listView (in this case $o)
        lvLog.Items.Add(new ListViewItem(DateTime.Now.ToString(" HH:mm:ss ") + line, (int)lt));
    }

}

This will give a parameter count mismatch. How to declare the new object properly? Thank you in advance,

Upvotes: 1

Views: 751

Answers (2)

Jeroen van Langen
Jeroen van Langen

Reputation: 22038

You need to change this line:

this.Invoke(d, new { line, lt }); // <-- here is the problem

To:

this.Invoke(d, new object[] { line, lt });

// or

this.Invoke(d, line, lt);

Your version will create an anonymous class with 2 properties and will be passed as one parameter. Thats why the parameter mismatch.

You'll have to pass it as an array of objects or two separated parameters.


A usefull tip. Cross-threading modifying controls (with invoke) could slack up your thread code. The problem is, that foreach callback an invoke is made. A better way is to store the info in a queue and use a timer to process the queue.

For example:

private class LogMessage
{
    public DateTime CreatedOn {get;} = DateTime.Now;
    public string Line {get;set;}
    public LogType LogType {get; set;}
}

private List<LogMessage> _logQueue = new List<LogMessage>();


private void LogAddInfo(string line, LogType lt)
{
    lock(_logQueue)
        _logQueue.Add(new LogMessage { Line = line, LogType = lt });
}


private void Timer1_tick(object sender, EventArgs e)
{
    LogMessage[] messages;

    lock(_logQueue)
    {
        messages = _logQueue.ToArray();
        _logQueue.Clear();
    }

    foreach(var msg in messages)
    {
        // add to listview
    }
}

It will run much better due to less hui updates.

Upvotes: 1

Idle_Mind
Idle_Mind

Reputation: 39132

You can also just use an anonymous delegate and a recursive call like this:

    private void LogAddInfo(string line, LogType lt)
    {
        if (lvLog.InvokeRequired)
        {
           lvLog.Invoke((MethodInvoker)delegate ()
           {
               this.LogAddInfo(line, lt);
           });
        }
        else
        {
            // code that adds item to listView (in this case $o)
            lvLog.Items.Add(new ListViewItem(DateTime.Now.ToString(" HH:mm:ss ") + line, (int)lt));
        }
    }

Upvotes: 1

Related Questions