Reputation: 2977
I'm new to C# and .NET programming and I'm trying to develop a windows TaskManager-like windows form application. I first populate the listView by using infos gathered from System.Diagnostics.Process, then I start a System.Threading.Timer with a MethodInvoker delegate who try to refresh listview data every seconds.
The problem is that the application is really slow, and, for sure, I'm doing something noobish.
public partial class Form1 : Form
{
private System.Threading.Timer t;
private Process[] procVett;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.loadAppInfo(null);
TimerCallback timerDelegate = new TimerCallback(this.refresh);
this.t = new System.Threading.Timer(timerDelegate, null, 1000, 3000);
}
private void refresh(Object stateInfo)
{
procVett = Process.GetProcesses();
this.BeginInvoke( (MethodInvoker)delegate
{
ListView.ListViewItemCollection ic = this.appList.Items;
ListView.ListViewItemCollection procIc = this.procList.Items;
time.Text = DateTime.Now.ToLongTimeString();
int count = 0;
procList.BeginUpdate();
foreach (Process p in procVett)
{
try
{
string cputime = p.TotalProcessorTime.TotalMinutes.ToString();
procIc[count].SubItems[1].Text = ("");
procIc[count].SubItems[2].Text = (cputime);
procIc[count].SubItems[3].Text =(p.WorkingSet64.ToString());
procIc[count].SubItems[4].Text = ("");
}
catch (System.Exception)
{
}
count++;
}
procList.EndUpdate();
}, null);
}
}
Upvotes: 0
Views: 2499
Reputation: 20757
I'd suggest using VirtualMode on the ListView, which only displays the items currently in view. If you have 1,000,000 items, and it's only drawing #1,000-1020, it'll request those, you tell it how to get them from your item list in memory, and it draws them.
It's much faster than adding ListViewItems yourself, which is incredibly slow.
Here's a starter tutorial for it: Virtual Mode ListView
Also, I just started using ObjectListView this morning, and it's amazing.
Upvotes: 0
Reputation: 4320
You're basically on the right track, but I would avoid calling the BeginInvoke method until after you've looped through the processes in your array to collect the TotalProcessorTime time values.
Basically, the rule of thumb is to spend as little time on the UI thread as you can -- do as much work using background processes as you can.
Also, make sure you don't have threads or dispatches piling up in the thread pool. From the MSDN documentation for the Timer class:
If the SynchronizingObject property is a null reference (Nothing in Visual Basic), the Elapsed event is raised on a ThreadPool thread. If processing of the Elapsed event lasts longer than Interval, the event might be raised again on another ThreadPool thread. In this situation, the event handler should be reentrant.
So, watch for re-entrancy, and either ignore re-entrant timer events, or kill the processing on an older thread (using some thread-safe signaling mechanism).
Upvotes: 2
Reputation: 2272
Apart of what I said in comment:
I'd say create a separated collection of ListViewItems, parse process informations into that in a different thread (BackgroundWorker or simply anothery thread) and use that collection in your ListView after you finished updating it, that way the currently displayed list won't be frozen.
Upvotes: 1