0

I'm trying to add items to a DataGrid using WPF in a way the UI doesn't freeze.

Background: I have a list of IP addresses. With these IP addresses further information should be determined, e.g. the ping. This means, I go through every item of the IP list, determine the data based on the IP and insert the result into a new list. The content of this new list should be displayed in the DataGrid.

Now it is like this, that there are about 4000 IPs. It is estimated that about 15 entries per second will be added to the DataGrid list. However, the list will only be displayed after ALL items from one list have been processed and added into the new list.

My goal is to make it look something like this: https://www.youtube.com/watch?v=xWC1GvfCI0I

Do you perhaps have an idea how to solve this best? This is the last way I tried it:

public void Get()
    {
        Task.Run(() =>
        {
            using (var client = new WebClient())
            {
                var ips = client.DownloadString("http://monitor.sacnr.com/list/masterlist.txt");

                using (var reader = new StringReader(ips))
                {
                    for (string ip = reader.ReadLine(); ip != null; ip = reader.ReadLine())
                    {
                        this.Servers.Add(this._sacnr.GetServerProperties(ip));
                    }
                }
            }
        });
    }

Thank you.

  • Does this answer your question? [Change WPF controls from a non-main thread using Dispatcher.Invoke](https://stackoverflow.com/questions/1644079/change-wpf-controls-from-a-non-main-thread-using-dispatcher-invoke) – ndogac Sep 29 '20 at 21:39
  • ok, i do understand you now, you want the UI updated simultaneously with your get function, that is the datagrid should automatically update as its retrieving the values as shown in the YouTube link attached. You would need to use the IProgress interface. @N.Dogac already posted a link above. I would delete my answer now – upsidedownwf Sep 29 '20 at 22:19
  • I tried to do it this way now: `Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => this.Get()));` but it still freezes. I left the Get() method as it was. Did I understand something wrong maybe? – hobbykellerit Sep 29 '20 at 22:29
  • Make your method async and try to await on DownloadStringAsync or ReadlineAsync, that should do the trick. Or simply await Task.run(). I would try all 3 options to see which one fits best. – LuckyLuke82 Sep 30 '20 at 06:27
  • And If you want to update list like in video, I would bind to ObservableCollection instead regular List and just add Items in a loop to It. That would result in smooth updating of DataGrid, ObservableCollection implements InotifyProperty by default. – LuckyLuke82 Sep 30 '20 at 06:32
  • @LuckyLuke82 Sorry for the late reply. Unfortunately none of the three options did work. In every case the UI still freezes. Using ObservableCollection causes a NotSupportedException: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread. If I use the Add-line like this: `Application.Current.Dispatcher.Invoke( new Action(() => { this.Servers.Add(sacnr.GetServerProperties(ip)); }));` I don't get the exception when using an ObservableCollection, but still the UI freezes. – hobbykellerit Oct 01 '20 at 20:52
  • @hobbykellerit, because you probably did It wrong. I see your answer, I'm glad It works for you. However I still think that proper use of async/await with combination of Task.Run is better option. Specially for exception handling, which you have none. And that might be a big problem if something goes wrong. – LuckyLuke82 Oct 02 '20 at 21:25
  • @LuckyLuke82 I use hobbykellerit's solution and it works. But can you explain how to use async/await to implement it? – Dennys Feb 21 '23 at 16:38

1 Answers1

0

I did it this way now.

I call my Method like this: Task.Factory.StartNew(() => this.Get());

And then I use my method like this:

public void Get()
{
    var sacnr = new SacnrConnector();

    using (var client = new WebClient())
    {
        var ips = client.DownloadString("http://monitor.sacnr.com/list/masterlist.txt");

        using (var reader = new StringReader(ips))
        {
            for (string ip = reader.ReadLine(); ip != null; ip = reader.ReadLine())
            {
                var server = sacnr.GetServerProperties(ip);

                // Here I use BeginInvoke to add elements to my ObservableCollection
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new ParameterizedThreadStart(AddItem), server);
            }
        }
    }
}

private void AddItem(object server)
{
    this.Servers.Add((Server)server);
}

And it works!

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92