0

I am having problems updating a ListView on C# WinForm. The application is a test harness which connects to a TCP Server and receives a data stream. The test harness will then be used to create multiple connections to the server to do load testing. Each task is started using the following command where the number of task is entered on the form:

for (int i = 0; i < noTasks; i++)
{
    bool saveAudio = noTasksCheckedListBox.GetItemCheckState(i) == CheckState.Checked; // true : false;
    taskArray[i] = Task.Run(() => SendMessage((initId + i).ToString(), saveAudio, audioServerIP));
}

The SendMessage function creates the TCP client connection and creates a AsyncCallBack to process the incoming stream.

void SendMessage(string id, bool saveWav, string serverIP)
{
    TcpClient tcpclientAudio = new TcpClient();
    string message = "GET /msg?id=" + id + "&type=0 HTTP/1.1\r\n";

    NetworkObject audioObj = new NetworkObject
    {
        tcp = tcpclientAudio,
        ConnectionType = FileType.Audio,
        URL = message
    };

    if (saveWav)
    {
        tcpclientAudio.BeginConnect(IPAddress.Parse(serverIP), 8008, new AsyncCallback(CallBackConnectMethod01), audioObj);
    }
    else
    {
        tcpclientAudio.BeginConnect(IPAddress.Parse(serverIP), 8008, new AsyncCallback(CallBackConnectMethod02), audioObj);
    }
}

The only difference between the CallBackConnctMethod is that 02 save the data to a file.

void CallBackConnectMethod02(IAsyncResult result)
{
    NetworkObject obj = (NetworkObject)result.AsyncState;
    TcpClient tcp = obj.tcp;
    NetworkStream ns = tcp.GetStream();

    startedProcessCount++;
    int thisTask = startedProcessCount;
    byte[] buffer = new byte[2048];
    string message = obj.URL;
    byte[] request = Encoding.ASCII.GetBytes(message);
    ns.Write(request, 0, request.Length);
    ns.Read(buffer, 0, 1000);
    RefreshTable(thisTask, "Incoming", null);
    ......
}

When debugging the app goes to the RefreshTable function a disappears when it goes in to the delegate section.

private void RefreshTable(int taskNo, string status, string misc)
{
    if (this.tasksListView.InvokeRequired)
    {
        this.Invoke((MethodInvoker) delegate { 
            DateTime nowDateTime = DateTime.Now;
            string now = nowDateTime.Year.ToString("D4") + nowDateTime.Month.ToString("D2") + nowDateTime.Day.ToString("D2") + "-" + nowDateTime.Hour.ToString("D2") + nowDateTime.Minute.ToString("D2") + nowDateTime.Second.ToString("D2");
            string taskId = "Task-" + taskNo.ToString("D3");

            if (status == "Incoming")
            {
                ListViewItem taskLVI = tasksListView.Items.Add(taskId);
                taskLVI.SubItems.Add(now);
                taskLVI.SubItems.Add(" ");
                taskLVI.SubItems.Add(" ");
                if (string.IsNullOrEmpty(misc))
                    taskLVI.SubItems.Add(" ");
                else
                    taskLVI.SubItems.Add(misc);
                taskLVI.SubItems.Add(" ");
            }
            else
            {
                switch (status)
                {
                    case "Lost":
                    case "Completed":
                    case "Exception":
                        ListViewItem listViewItem = null;
                        for (int i = 0; i < tasksListView.Items.Count; i++)
                        {
                            if (tasksListView.Items[i].SubItems[0].Text == taskId)
                            {
                                listViewItem = tasksListView.Items[i];
                                break;
                            }
                        }

                        if (listViewItem != null)
                            ToListViewItem(listViewItem, status, misc, now);
                        break;
                    default:
                        break;
                }
            }
            tasksListView.Refresh();
        });
    }
}
adig14
  • 65
  • 6
  • Does this answer your question? [How do I update the GUI from another thread?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) – JonasH Dec 11 '20 at 15:31
  • Hi JonasH have used similar code to that previously and it worked and that was my starting point for this code but had exactly the same problem so was trying a different method. – adig14 Dec 11 '20 at 15:51
  • Updating the UI from another thread than the UI thread is always wrong, even if it sometimes happen to work by pure luck. You have to call RefreshTable on the UI thread, and the linked question shows you how. – JonasH Dec 11 '20 at 15:54
  • But adig14 is doing an [Invoke](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke?view=net-5.0). If 'this' is a UI object the bulk of the RefreshTable method is on the UI thread, no? – Rich N Dec 11 '20 at 16:12
  • The only obvious thing I can see is that in the initial `SendMessage((initId + i).ToString()...` call i is likely to always have the same value. There's [another SO question](https://stackoverflow.com/questions/59540534/why-c-sharp-task-run-method-print-the-same-number-in-the-loop-in-console-app) that explains this and the fix. However, I'd be surprised if that is causing the crash. – Rich N Dec 11 '20 at 16:27

0 Answers0