I have the code below where it update the data within listview, I was trying to pass parameter into main code which update the listview table.
I have external code that can send burst data stream and it shown same value on the list.
Below is based on anonymous method which work but earlier data get overwritten.
The listview is too slow which slow down main program (not listed here) the burst data goes to this code and use separate thread to handle the display (about 20 set). The burst data is about 20 set of dataTX array, etc.
I'm open for suggestion how to fix this.
==========================================================================
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
this.ReqAddMessageThread = new Thread(delegate() { ReqAddMessageThreadProc(isCRCIncluded, dataTX, d); }); //anonymous method
this.ReqAddMessageThread.Start();
}
#endregion
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
test1Callback del = new test1Callback(ReqAddMessageThreadProc);
this.Invoke(del, new object[] { isCRCIncluded, dataTX,d });
return;
}
if (this.Visible == true)
{
SendMessage(this.Handle, WM_SETREDRAW, false, 0);
}
int length = myLinTools.LINDataLength;
int pCRC = 0;
elem = new ListViewItem(m_Item.ToString());
elem.SubItems.Add(d.Date.ToShortDateString());
elem.SubItems.Add(d.ToShortTimeString() + ":" + d.Second.ToString());
elem.SubItems.Add("");
for (int i = 0; i < length + 1; i++)
{
elem.SubItems.Add(dataTX[i].ToString("X2"));
pCRC = i;
}
for (int i = length; i < 8; i++)
{
elem.SubItems.Add(" "); // fill gaps
}
if (isCRCIncluded == true) // Does the message contains processed CRC data?
{
elem.SubItems.Add(dataTX[pCRC + 1].ToString("X2"));
}
else // No, then make one for display only!!
{
Byte CRC = myLinTools.CRC_Processor(false);
elem.SubItems.Add(CRC.ToString("X2"));
}
this.OutputView.Items.Add(elem);
this.OutputView.EnsureVisible(m_Item);
if (myLinTools.IsRequestResponse == true) // Request Message Only
{
if (this.Visible == true) // Is form open?
{
SendMessage(this.Handle, WM_SETREDRAW, true, 0);
this.Refresh();
}
}
m_Item++;
}
=================================================================================
Thanks KazR, I modified the code which worked fine, however it slow down the other high level program (call it main program), that making data transfer to this program that display data. One of the requirement that the main program stream the data without delay or pause cause by listview in this display program. That why I'm looking for way to use thread, so it release the control back to main program and thus operates faster, but however there is issue in keep data from being over-written by next thread, since listview is slow. Perhaps I should consider a buffer, which update only when there is no activity in main program.
I do not wish to use virtual, I'm open for alternative suggestion.
==================================================================================
delegate void ReqAddMessageTCallback(bool isCRCIncluded, byte[] dataTX, DateTime d);
#region//==================================================LIN_Request_Add_Message
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
ReqAddMessageThreadProc(isCRCIncluded, dataTX, d);
}
#endregion
#region//==================================================ReqAddMessageThreadProc
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
ReqAddMessageTCallback del = new ReqAddMessageTCallback(ReqAddMessageThreadProc);
this.BeginInvoke(del, new object[] { isCRCIncluded, dataTX, d });
return;
}
if (this.Visible == true)
{
SendMessage(this.Handle, WM_SETREDRAW, false, 0);
}
int length = myLinTools.LINDataLength;
int pCRC = 0;
elem = new ListViewItem(m_Item.ToString());
From your code example it appears that you're creating a new Thread object each time you receive data and all this thread is doing is calling the ReqAddMessageThreadProc method. Assuming that calls to LIN_Request_Add_Message are not being made in the main UI thread, you could try removing the Thread creation & start calls, replace them with a direct call the ReqAddMessageThreadProc and use BeginInvoke rather than Invoke.
e.g.
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
ReqAddMessageThreadProc(isCRCIncluded, dataTX, d);
}
#endregion
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
test1Callback del = new test1Callback(ReqAddMessageThreadProc);
this.BeginInvoke(del, new object[] { isCRCIncluded, dataTX,d });
return;
}
etc...
The BeginInvoke call is the async version of Invoke, this should negate the need to use separate new Thread objects each time you receive new data.
You should make your ListView virtual. Then you can operate at full speed with your threads and the listview will display only the items which are currently visible. If you do append things to the ListView and not force to make it visible every time something is added you can let the user decide when he does want to scroll down. He does see when things are appended because the scrollbar is becoming smaller and smaller while items are added.
This way you can display millions of entries without any issues and you will get rid of ivoking, redrawing, refreshing the listview via SendMessage. Here is a virtual list view sample how you can change it.
By doing so your code will become simpler and faster because you do not mix background data processing with UI stuff. E.g. sorting can be done at raw data array level without doing anything in the UI except to trigger it.
Related
I am working on a serial monitor project based on the arduino serial monitor, but with a lot of more functionalities. Serial monitor UI with messages every 2 seconds
There are 2 basic functions - reading serial data and visualising it to the richtextbox, writing serial data and also visualising it to the richtextbox. There is a problem when the communication is very intensive (arduino sends a line as fast as it can) and the user input cuts some of the recieved lines in halves. I made an algorythm to separate the input strings from the output strings in the richtextbox by setting sending/receiving flags. However this made the program act strange. Sometimes the command sent to the arduino just doesn't visualise in the richtextbox, but the data from the arduino is visualised just fine. I tried setting breakpoints on the user input visualising method and they were rarely activated. What is more I also set breakpoints on the visualisation of the read data method and they were also rarely activated. However the form was not lagging or freezing and the commands were flying in the richtextbox, contrary to the observed behaviour of the breakpoints. The printing algorythm is heavy, because I implemented multi-color printing.
What I tried:
-tried the blockingcollection approach. Just putting the printing actions in a queue and executing the actions one by one using 2 additional threads to the main one.(Task.Factory.StartNew). The problem was the queue was filled with over 3000 actions in the matter of a minute and the whole thing was lagging behind with like 15 seconds.
-i tried starting a new Task.Factory for every receive/send methods for every printing of a new command and locking the thread until the command is printed in the richtextbox. This is also too slow.
Finally I came up with the idea of just setting flags and allowing or not the print event. Using this approach the user input is almost never printed in the richtextbox. :(
Printing algorythm:
void printToConsole(string print, Color txtColor, string part, Color partColor, bool isMsg, bool endNL)
{
while (print.Contains('\r'))
print = print.Replace("\r", "");
for (int i = 0; i < print.Length; i++)
{
if (newString)
{
newString = false;
printTimeAndPart(part, partColor);
}
else if (i == 0 && !prevStrHadNL && isMsg != prevWasMsg)
{
printNLTimeAndPart(part, partColor);
}
else if (i == 0 && prevStrHadNL)
{
printNLTimeAndPart(part, partColor);
}
else if (i == print.Length - 1)
{
if (print[i] == '\n')
{
if (endNL && isMsg != prevWasMsg)
AppendText("\n");
prevStrHadNL = true;
break;
}
else
{
if (endNL)
AppendText("\n");
prevStrHadNL = false;
}
}
if (print[i] == '\n')
{
printNLTimeAndPart(part, partColor);
}
else
AppendText(print[i].ToString(), txtColor);
}
prevWasMsg = isMsg;
}
private void printNLTimeAndPart(string part, Color partColor)
{
AppendText("\n");
printTimeAndPart(part, partColor);
}
private void printTimeAndPart(string part, Color partColor)
{
if (timestamp == 1)
AppendText(DateTime.Now.ToString("HH:mm:ss.fff"), timeColor);
AppendText(part, partColor);
}
private void AppendText(string txt, Color clr)
{
if (serialControl.ReadAllowed)
{
if (rtArea.InvokeRequired)
{
MethodInvoker mi = delegate ()
{ rtArea.AppendText(txt, clr); };
Invoke(mi);
}
else
rtArea.AppendText(txt, clr);
}
}
private void AppendText(string txt)
{
if (serialControl.ReadAllowed)
{
if (rtArea.InvokeRequired)
{
MethodInvoker mi = delegate ()
{ rtArea.AppendText(txt); };
Invoke(mi);
}
else
rtArea.AppendText(txt);
}
}
Serial read data print:
wait1 and wait2 - flags. When wait1 is false there is no current user input message being printed.
When wait2 is false user input is free to print. If the access to a print operation was denied it is performed after the operation which blocked it. (At least that is the idea)
private void Port_dataRecieved(string a)
{
if (!consoleUsed)
{
newString = true;
consoleUsed = true;
}
inputVal = a;
if (!wait1)
{
inputAccDenied = false;
wait2 = true;
PrintInput(inputVal);
wait2 = false;
}
else
inputAccDenied = true;
if (msgAccDenied)
{
PrintMsg(msgVal);
msgAccDenied = false;
}
}
private void PrintInput(string input)
{
printToConsole(input, inTxtColor, inPrefix, inPrefColor, false, false);
}
This is the dataReceived method from my serial controller class:
I tried several ways to read serial data and the uncommented one is the most stable one I tried.
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
/* byte[] buffer = new byte[blockLimit];
Action kickoffRead = null;
kickoffRead = delegate
{
port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar)
{
try
{
int actualLength = port.BaseStream.EndRead(ar);
byte[] received = new byte[actualLength];
Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
string rcv = Encoding.Default.GetString(received);
dataRecieved(rcv);
}
catch (IOException exc)
{
//handleAppSerialError(exc);
}
kickoffRead();
}, null);
};
kickoffRead();
*/
if (ReadAllowed)
{
string a = port.ReadExisting();
//port.DiscardInBuffer();
dataRecieved(a); //Raise event and pass the string to Form1 serial_dataReceived
}
}
So, I need some advice on how exactly to print the multi-color messages without cutting into each other, printing every time, fast, with low cpu load (now it is like 35% on full load (arduino intensive transmittion) on i5-4310m). I would be glad if you could provide some examples, too.
I made a SUDOKU solver. When is solve a number it should be written to the screen but it's happen only when the solver is done. Why only then the screen is refreshing when it is done?
ii is the number, jj is the row, ll is the column
private void MainForm_Load(object sender, EventArgs e)
{
...
Thread tr2 = new Thread(adatbszal);
tr2.Start();
}
private void adatbszal()
{
while (fut)
{
Thread.Sleep(10);
if (adat[jj, ll] != 0)
{
SetText(jj, ll, adat[jj, ll].ToString());
}
else
{
SetText(jj, ll, "");
}
}
}
private void SetText(int i, int j, string adat2)
{
if (adatB[i,j].InvokeRequired)
{
valami d = new valami(SetText);
Invoke(d, new object[] { i, j, adat2 });
}
else
{
adatB[i, j].Text = adat2;
}
}
...
Thread th = new Thread(solver);
th.Start();
full project: https://drive.google.com/file/d/1sZTA4Ledfwl3romBY2UTvUoU9MZfY35g/view?usp=sharing
I would suggest putting a breakpoint on Invoke(d, new object[] { i, j, adat2 }); to verify that it is being reached, and when it is, add a breakpoint to if (adatB[i,j].InvokeRequired) before stepping, then make sure that the same parameters are being received to verify that the Invoke is triggering SetText as expected. (Step Into would be simpler, but I'm not sure that would work on an Invoke Line.)
If all that is working, then check if the display updates then adatB[i, j].Text = adat2; executes in the debugger.
I think your code is updating the display like you want it to, but it's not working properly in other regards. In one of my tests, it is just setting the first cell to "2" over and over again, so you won't see any change. You need to verify that the code is doing the right work before you verify that the display is updating properly. I think you may see many problems from having 3 threads running simultaneously which can all update or read the same global values when other threads are assuming these values (like jj) are not changing between lines. I would suggest not sharing data between threads if you are not very experienced with multi-threaded programming. Perhaps you can re-implement this as a single-threaded program that calls an update function periodically instead.
I have a WPF project (VS2010, .NET4.0) in which I create a rather big ModelVisual3D object (read from custom format STL file, process info, create mesh, etc.) This takes about 3-4 sec. to be created and another 2-3 sec. to do a mainViewport.Children.Add(ModelVisual3D).
I do this all in a custom class and call this method:
class My3DModel
{
...
public MyModelVisual3D createModelVisual3D(MyTypes tType, int tNumber)
{
this.myModelVisual3D = new MyModelVisual3D(tType, tNumber);
for (int i = 0, j = 0; i < this.Triangles.Length; i++)
{
this.mesh.Positions.Add(this.Triangles[i].Vertex1);
this.mesh.Positions.Add(this.Triangles[i].Vertex2);
this.mesh.Positions.Add(this.Triangles[i].Vertex3);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.Normals.Add(this.Triangles[i].Normal);
this.mesh.TriangleIndices.Add(j++);
this.mesh.TriangleIndices.Add(j++);
this.mesh.TriangleIndices.Add(j++);
}
this.model3DGroup.Children.Add(new GeometryModel3D(this.mesh, material));
this.myModelVisual3D.Content = this.model3DGroup;
return this.myModelVisual3D;
}
}
The return value is also a custom class I created:
class ToothModelVisual3D : ModelVisual3D
{
//VARIABLES
private MyTypes myType;
private int number;
//OPERATORS
public MyTypes MyType
{get { return myType; } set { myType = value; }}
public int Number
{get { return number; } set { number = value;}}
public ToothModelVisual3D() { }
public ToothModelVisual3D(MyTypes tType, int tNumber) { MyType = tType; Number = tNumber; }
}
All I want to do is the following once in the beginning of the program:
{
My3DModel myModel;
myModel = new My3DModel();
myModel.readFileBytes("C:\\registered\\" + 1 + ".stl");
myModel.loadTriangles();
mainViewport.Children.Add(myModel.createModelVisual3D(MyTypes.Sometype, 1);
}
If I do it on the main thread the UI hangs. If I do it on a worker thread and invoke mainViewport.Children.Add(...) it says it cannot access the resourses created on that worker thread. Help?!
From what I understand I've reached a point where I have two threads and resources belonging to each of them (mainViewport => UIThread & myModel => WorkerThread). Neither thread can access directly the other's resource but creating and using myModel on the UIThread makes it hang... All I want to do is have enough responsiveness from the UI, so the user may minimize the program while waiting for it to load the models, nothing more. How can I do that? Is there a way to do all the CPU heavy work on the UIThread, so no resource conflicts arise and have a worker thread that only handles UI for that time?
PS: I've tried with Thread, BackgroundWorker & Task<TResult> classes. Results were similar if not to say the same.
PPS: The full version will load massive models which will load more than 30-40 sec...
I recently came across the same issue when porting an XNA application to WPF.
In my case I partially resolved this by using a background thread to load the positions, normals, and indices from file. Then in that same thread, construct a memory stream containing XAML for the Model3DGroup with the GeometryModel3D and MeshGeometry3D.
Then, in the UI thread, once the memory stream is available, load the model...
Model3DGroup model = System.Windows.Markup.XamlReader.Load(memoryStream) as Model3DGroup;
There is still a delay, but as file access is done in a background thread, it is not as severe.
Sorry for the late answer, but I actually managed to workaround the problem long time ago the following way:
delegate void myDelegate();
private void fileOpenButton_Click(object sender, RoutedEventArgs e)
{
try
{
Thread ViewportLoaderThread = new Thread(loadViewportItemsAsync);
ViewportLoaderThread.IsBackground = true;
ViewportLoaderThread.Start();
}
catch (Exception err) { UtilsProgram.writeErrorLog(err.ToString()); }
}
private void loadViewportItemsAsync()
{
try
{
//TRY to browse for a file
if (!browseForFile()) return;
Dispatcher.Invoke(new Action(() => { myStatusBar.Visibility = System.Windows.Visibility.Visible; menuItemHelpDemo.IsEnabled = false; }), null);
//Load file, unpack, decrypt, load STLs and create ModelGroup3D objects
UtilsDen.DenModel = new DenLoader(UtilsDen.Filename, UtilsDen.Certificate, UtilsDen.PrivateKey, this);
//Load the models to viewport async
myDelegate asyncDel = new myDelegate(sendModelsToViewportAsync);
this.Dispatcher.BeginInvoke(asyncDel, null);
}
catch (Exception err) { MessageBox.Show(UtilsProgram.langDict["msg18"]); UtilsProgram.writeErrorLog(err.ToString()); }
}
private void sendModelsToViewportAsync()
{
for (int i = 0; i < UtilsDen.DenModel.StlFilesCount; i++)
{
//Add the models to MAIN VIEWPORT
ModelVisual3D modelVisual = new ModelVisual3D();
GeometryModel3D geometryModel = new GeometryModel3D();
Model3DGroup modelGroup = new Model3DGroup();
geometryModel = new GeometryModel3D(UtilsDen.DenModel.StlModels[i].MeshGeometry, UtilsDen.Material);
modelGroup.Children.Add(geometryModel);
modelVisual.Content = modelGroup;
mainViewport.Children.Add(toothModelVisual);
}
}
The key was to use the this.Dispatcher.BeginInvoke(asyncDel, null); as it works on the main thread, but does not lag it, because it is executed asynchronously.
Using a delegate still appears to introduce a lag on the UI, a better solution is create the model in a worker thread and then freeze it. The model can then be cloned by the UI thread without the annoying exception. This works for me with models which take 25 seconds or more to load. The only issue I've found with this is that it doesn't work if the model contains a texture.
im making file transfer (Server-Client) TCP
i've already looked for same questions like this one .. but no answer worked for me ..
ProgressBar doesn't update with backgroundworker .. i've searched for tutorials to do this .. and i exactly followed the steps.
the form lags while sending and after the file sent.. the progressbar goes to 100%
The file sent succesfully the send method code works fine... my problem just with updating the progressbar .. how do i fix that ??
Here i call (backgroundWorker1.RunWorkerAsync)
public void Send(string destPath)
{
if (listView1.Items.Count > 0)
{
List<String> job = new List<string>();
job.Add(listView1.Items[0].ToolTipText);
job.Add(destPath);
backgroundWorker1.RunWorkerAsync(job);
}
}
DoWork Method
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
List<string> job = (List<string>)e.Argument;
SendFile(job[0],job[1]);
}
here's the SEND method which i use (backgroundWorker1.ReportProgress)
private void SendFile(string srcPath, string destPath)
{
string dest = Path.Combine(destPath, Path.GetFileName(srcPath));
using (fs = new FileStream(srcPath, FileMode.Open, FileAccess.Read))
{
try
{
long fileSize = fs.Length;
sizeAll = fileSize;
long sum = 0;
int count = 0;
data = new byte[fs.Length];
SendCommand("receive<" + dest + "<" + fs.Length.ToString());
while (sum < fileSize)
{
if (fileSize - sum < packetSize)
{
count = fs.Read(data, 0, (int)(fileSize - sum));
network.Write(data, 0, (int)(fileSize - sum));
}
else
{
count = fs.Read(data, 0, data.Length);
network.Write(data, 0, data.Length);
}
fs.Seek(sum, SeekOrigin.Begin);
sum += count;
sumAll += count;
backgroundWorker1.ReportProgress((int)((sum * 100) / fileSize));
}
network.Flush();
}
finally
{
CloseTransfer();
}
}
}
and here is backgroundWorker1_ProgressChanged
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBarFile.Value= e.ProgressPercentage;
}
Seems strange the you're able to assign a value to the a UI control from another thread without getting any exception. Or that is, may be, a real issue.
Like a first thing I would do, if the code of ProgressChanged is inside (say) WindowsForm class, write like this:
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.Invoke(new Action(()=>
progressBarFile.Value= e.ProgressPercentage;
));
}
Something like this.
Did you set?
worker.WorkerReportsProgress = true;
(Either in code or in the properties window)
EDIT:
Shouldn't you be reading like this
count = fs.Read(data, 0, packetSize);
instead of reading data.Length bytes? Since you set data = new byte[fs.Length] the file will be read all at once, instead of in little pieces, which is required in order to see the progress bar change progressively.
thanks for [Nikola Markovinović] his answer was:
the line that causes the error was :
data = new byte[fs.Length];
after correcting the code :
data = new byte[packetSize];
while (sum < fileSize)
{
count = fs.Read(data, 0, data.Length);
network.Write(data, 0, count);
sum += count;
backgroundWorker1.ReportProgress((int)((sum * 100) / fileSize));
}
network.Flush();
The background worker is dorking around on a new thread, therefore, calling anything back to the original form and its controls (or original thread) will require some delegation to accomplish what you desire. I have several code examples that I can share but they would take up far too much space here on this site. As a quick assistance, try some of this information from this site.
Background Worker with Delegates
Of course, you can always Google for more regarding threading, delegates, etc. I hope that helps.
Well it may be something quirky but always worth checking the steps its easy to overlook first:
Have you set WorkerReportsProgress on the BackgroundWorker to true?
Have you hooked up the ProgressChanged event to your event handler?
Finally just compare your solution to a sample at the link below - it may remind you of something you've forgotten to do:
http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
In a WinForms application, I have a datagrid that is associated to a datasource. As and when data comes in through a background thread the dataset needs to be updated which in turn automatically updates the datagrid. Now, the updates can be in the order of say, 7000 updates per 20 seconds.
The probelm is that the UI hangs when such an update happens because it has to happen on the main thread. Is there a know solution for this problem?
Generally, how can you design highly performant enterprise applications in WinForms where the UI is being updated continuously without the application freezing?
Adding a scenario to explain this:
Consider this scenario. You have a tree view which you are using to represent some hierarchical data. Now the data update on the tree is async. The server can publish one or 1000 updates at the same same time. The update can be a modification of an existing item or addition of new nodes. The point to be noted is that the update cannot be delayed. The nodes represent a real time entity somewhere. Delaying the update will give the user the perception that the event itself was delayed. So this cant be done. If it was possible (from the business logic point of view) I would have done it a long time back.
There is a key point here: All the data need not be visible at the same time.
So that people dont suggest this anymore:
Adding a background worker thread WILL NOT HELP because the thread has to switch to the main thread to perform the update. The worker thread will make no difference.
You cannot, unless you want to use DirectX.
Windows Forms is not designed for real-time information display. As many others have pointed out, you can get very close, but due to the way the Windows message loop works, you absolutely cannot guarantee that what is on the screen will be "real-time", even if you create a Timer that ticks at 60hz. Even if you do this in an event-driven manner, Windows will still queue up a WM_PAINT message that will inevitably be late if you are looking for real-time display.
If you truly want a extremely-close-to-realtime display, you will need to implement something similar to a Game Loop.
For an explanation of why the Windows Message loop won't work for real-time display, and what a game loop is, see:
http://www.mvps.org/directx/articles/writing_the_game_loop.htm
A computer game cannot have any perceivable delay, so most computer games try to optimize performance so that they approach a framerate at or above the holy grail of 60hz. (Movies are only projected at 24hz, do you perceive them to be "delayed"?)
Writing an application with a real-time display is non-trivial and I would highly suggest considering compromising with what Windows provides in any of the following ways:
Create a timer that queues up screen updates at an acceptable rate (10 or more times per second). The user will not perceive the event as delayed, because a user cannot perceive delays that occur at a small fraction of a second.
Raise an event when the underlying data changes and let Windows to decide when to update the display (this will virtually always be acceptable).
If possible, come up with an alternate display that is not grid-based. Perhaps a scrolling console, or some other interface that displays the relevant information without overwriting old information. This may not be applicable, but coming up with another interface idea is often a good approach when the interface you want won't work.
If you really, really still want a very high-performance user interface, and write a game loop, you can do so in C# and paint a grid on your own to a DirectX surface. Once you get the hang of DirectX, drawing a grid is fairly easy, it's just a bunch of lines. With this approach you will avoid dealing with the Windows message loop, and potentially approach real-time performance.
Here's a great tutorial on how to use DirectX and how to render on a Windows form:
http://www.godpatterns.com/2005/02/using-directx-and-c-sharp-to-create.html
In your comment you say that your heavy processing is reporting progress very often and you can't drop any report (because the report is real data that needs showing).
What you should do is implement (double) buffering, report progress to a buffer and only synchronize the buffer with GUI every once in a while.
Pseudocode follows:
DataGrid Grid; // This displays the data
List<object> DataBuffer; // Frequent updates are performed on this list
void BackgroundThreadLoop()
{
while(true) // This loop iterates 7000 times in 20 seconds
{
var result = DoSomeHeavyCalculations();
// Depending on the nature of the result, you can either just add it to list
// or perhaps modify existing entries in the list in some way.
DataBuffer.Add(result); // The simple case
PerformSomeUpdating(DataBuffer, result); // The complicated case
}
}
Timer RefreshTimer;
override void OnLoad()
{
RefreshTimer = new Timer();
RefreshTimer.Interval = 500; // easy to experiment with this
RefreshTimer.Tick += (s, ea) => DrawBuffer(DataBuffer);
}
void DrawBuffer(List<object> DataBuffer)
{
// This should copy DataBuffer and put it in the grid as fast as possible.
// How to do this really depends on how the list changes and what it contains.
// If it's just a list of strings:
Grid.DataSource = DataBuffer.ToList(); // Shallow copy is OK with strings
// If it's a list of some objects that have meaningful Clone method:
Grid.DataSource = DataBuffer.Select(o => o.Clone).ToList();
// If the number of elements is like constant and only some values change,
// you could use some Dictionary instead of List and just copy values.
}
If you give more precise info, I might be able to help further.
UPDATE
With the new details, I'd suggest buffering individual changes made to objects. The most general way of representing a change to some objects structure would be a function (perhaps parameterless Action). While receiving changes, you construct the update-functions modifying directly the view-bound data and store them in the buffer:
List<Action> UpdateBuffer;
void OnUpdateReceived(MyType objToModify, object newValue)
{
// The point is to make the lambda (below) as efficient as you can;
// finding the object and preparing the update should be done here, so that
// no time is wasted during redraw in the main thread.
UpdateBuffer.Add(() => objToModify.ApplyNewValueInSomeWay(newValue));
// some other method should be constructed to add data to the view, but you get the point
}
Now the DrawBuffer (name no longer fully adequate, but no matter) method would be easy:
void DrawBuffer()
{
List<Action> bufferCopy;
lock(UpdateBuffer) // the other thread should also lock the buffer for adding
{
bufferCopy = UpdateBuffer.ToList();
UpdateBuffer.Clear();
}
view.SuspendLayout();
foreach(Action a in bufferCopy)
a();
view.ResumeLayout();
}
Obviously I have not tried this exact solution, but it gives you the ability to control redraw frequency and redraw whole batches instead of single updates.
I just made an sample application, that will fill its internal list through a BackgroundWorker and the data will be displayed within a DataGridView. You can change the speed of the inserts to find out if it meets your requirements:
The most interesting part should be the code within the form itself:
public partial class FormMain : Form
{
private List<Person> _Persons;
private Random _Random;
private int _TimeoutBetweenInserts;
public FormMain()
{
InitializeComponent();
// Initialize our private fields
_Random = new Random();
_Persons = new List<Person>();
_TimeoutBetweenInserts = (int)numericUpDownTimeoutBetweenInserts.Value;
// Attach the list to the binding source and get informed on list changes.
personBindingSource.DataSource = _Persons;
personBindingSource.ListChanged += (sender, e) => labelDataGridViewCount.Text = _Persons.Count.ToString();
}
private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
var spinner = new SpinWait();
var worker = (BackgroundWorker)sender;
// Should we abort our adding?
while (!worker.CancellationPending)
{
// Create a new entry ...
var person = new Person();
person.Index = _Persons.Count;
person.Born = new DateTime(_Random.Next(1950, 2012), _Random.Next(1, 13), _Random.Next(1, 28));
person.FirstName = "Hello";
person.LastName = "World";
// ... and add it to the list
_Persons.Add(person);
// Do a little waiting ... (to avoid blowing out the list)
for (int i = 0; i < _TimeoutBetweenInserts; i++)
{
spinner.SpinOnce();
}
spinner.Reset();
}
}
private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Stop the gui updater, cause the background worker also stopped.
timerGuiUpdater.Stop();
}
private void OnCheckBoxToggleWorkerCheckedChanged(object sender, EventArgs e)
{
// Update the "button" according to the state
checkBoxToggleWorker.Text = checkBoxToggleWorker.Checked ? "&Pause" : "&Start";
if (checkBoxToggleWorker.Checked)
{
if (!backgroundWorker.IsBusy)
{
// Start the gui updater and the background worker
timerGuiUpdater.Start();
backgroundWorker.RunWorkerAsync();
}
}
else
{
// Stop the background worker
backgroundWorker.CancelAsync();
}
}
private void OnNumericUpDownTimeoutBetweenInsertsValueChanged(object sender, EventArgs e)
{
// Update the internal value, to let it propagate into the background worker
_TimeoutBetweenInserts = (int)numericUpDownTimeoutBetweenInserts.Value;
}
private void OnTimerGuiUpdaterTick(object sender, EventArgs e)
{
// Tell the BindingSource it should inform its clients (the DataGridView)
// to update itself
personBindingSource.ResetBindings(false);
}
}
To let you access all these fields within the form, here comes the Designer.cs:
partial class FormMain
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.dataGridView = new System.Windows.Forms.DataGridView();
this.Index = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.lastNameDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.firstNameDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.bornDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.ageDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.personBindingSource = new System.Windows.Forms.BindingSource(this.components);
this.backgroundWorker = new System.ComponentModel.BackgroundWorker();
this.labelDataGridViewCountText = new System.Windows.Forms.Label();
this.labelDataGridViewCount = new System.Windows.Forms.Label();
this.labelSpinsBetweenInsertsText = new System.Windows.Forms.Label();
this.numericUpDownTimeoutBetweenInserts = new System.Windows.Forms.NumericUpDown();
this.checkBoxToggleWorker = new System.Windows.Forms.CheckBox();
this.timerGuiUpdater = new System.Windows.Forms.Timer(this.components);
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.personBindingSource)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownTimeoutBetweenInserts)).BeginInit();
this.SuspendLayout();
//
// dataGridView
//
this.dataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dataGridView.AutoGenerateColumns = false;
this.dataGridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Index,
this.lastNameDataGridViewTextBoxColumn,
this.firstNameDataGridViewTextBoxColumn,
this.bornDataGridViewTextBoxColumn,
this.ageDataGridViewTextBoxColumn});
this.dataGridView.DataSource = this.personBindingSource;
this.dataGridView.Location = new System.Drawing.Point(12, 12);
this.dataGridView.Name = "dataGridView";
this.dataGridView.Size = new System.Drawing.Size(560, 212);
this.dataGridView.TabIndex = 0;
//
// Index
//
this.Index.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells;
this.Index.DataPropertyName = "Index";
this.Index.HeaderText = "Index";
this.Index.Name = "Index";
this.Index.Width = 58;
//
// lastNameDataGridViewTextBoxColumn
//
this.lastNameDataGridViewTextBoxColumn.DataPropertyName = "LastName";
this.lastNameDataGridViewTextBoxColumn.HeaderText = "LastName";
this.lastNameDataGridViewTextBoxColumn.Name = "lastNameDataGridViewTextBoxColumn";
//
// firstNameDataGridViewTextBoxColumn
//
this.firstNameDataGridViewTextBoxColumn.DataPropertyName = "FirstName";
this.firstNameDataGridViewTextBoxColumn.HeaderText = "FirstName";
this.firstNameDataGridViewTextBoxColumn.Name = "firstNameDataGridViewTextBoxColumn";
//
// bornDataGridViewTextBoxColumn
//
this.bornDataGridViewTextBoxColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells;
this.bornDataGridViewTextBoxColumn.DataPropertyName = "Born";
this.bornDataGridViewTextBoxColumn.HeaderText = "Born";
this.bornDataGridViewTextBoxColumn.Name = "bornDataGridViewTextBoxColumn";
this.bornDataGridViewTextBoxColumn.Width = 54;
//
// ageDataGridViewTextBoxColumn
//
this.ageDataGridViewTextBoxColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells;
this.ageDataGridViewTextBoxColumn.DataPropertyName = "Age";
this.ageDataGridViewTextBoxColumn.HeaderText = "Age";
this.ageDataGridViewTextBoxColumn.Name = "ageDataGridViewTextBoxColumn";
this.ageDataGridViewTextBoxColumn.ReadOnly = true;
this.ageDataGridViewTextBoxColumn.Width = 51;
//
// personBindingSource
//
this.personBindingSource.DataSource = typeof(WindowsFormsApplication.Person);
//
// backgroundWorker
//
this.backgroundWorker.WorkerSupportsCancellation = true;
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.OnBackgroundWorkerDoWork);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.OnBackgroundWorkerRunWorkerCompleted);
//
// labelDataGridViewCountText
//
this.labelDataGridViewCountText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelDataGridViewCountText.Location = new System.Drawing.Point(12, 230);
this.labelDataGridViewCountText.Name = "labelDataGridViewCountText";
this.labelDataGridViewCountText.Size = new System.Drawing.Size(50, 23);
this.labelDataGridViewCountText.TabIndex = 1;
this.labelDataGridViewCountText.Text = "Count:";
this.labelDataGridViewCountText.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// labelDataGridViewCount
//
this.labelDataGridViewCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelDataGridViewCount.Location = new System.Drawing.Point(68, 230);
this.labelDataGridViewCount.Name = "labelDataGridViewCount";
this.labelDataGridViewCount.Size = new System.Drawing.Size(82, 23);
this.labelDataGridViewCount.TabIndex = 2;
this.labelDataGridViewCount.Text = "0";
this.labelDataGridViewCount.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// labelSpinsBetweenInsertsText
//
this.labelSpinsBetweenInsertsText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.labelSpinsBetweenInsertsText.Location = new System.Drawing.Point(265, 230);
this.labelSpinsBetweenInsertsText.Name = "labelSpinsBetweenInsertsText";
this.labelSpinsBetweenInsertsText.Size = new System.Drawing.Size(155, 23);
this.labelSpinsBetweenInsertsText.TabIndex = 3;
this.labelSpinsBetweenInsertsText.Text = "Spins between inserts:";
this.labelSpinsBetweenInsertsText.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// numericUpDownTimeoutBetweenInserts
//
this.numericUpDownTimeoutBetweenInserts.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.numericUpDownTimeoutBetweenInserts.Increment = new decimal(new int[] {
10,
0,
0,
0});
this.numericUpDownTimeoutBetweenInserts.Location = new System.Drawing.Point(426, 233);
this.numericUpDownTimeoutBetweenInserts.Maximum = new decimal(new int[] {
500,
0,
0,
0});
this.numericUpDownTimeoutBetweenInserts.Minimum = new decimal(new int[] {
10,
0,
0,
0});
this.numericUpDownTimeoutBetweenInserts.Name = "numericUpDownTimeoutBetweenInserts";
this.numericUpDownTimeoutBetweenInserts.Size = new System.Drawing.Size(65, 20);
this.numericUpDownTimeoutBetweenInserts.TabIndex = 4;
this.numericUpDownTimeoutBetweenInserts.ThousandsSeparator = true;
this.numericUpDownTimeoutBetweenInserts.Value = new decimal(new int[] {
500,
0,
0,
0});
this.numericUpDownTimeoutBetweenInserts.ValueChanged += new System.EventHandler(this.OnNumericUpDownTimeoutBetweenInsertsValueChanged);
//
// checkBoxToggleWorker
//
this.checkBoxToggleWorker.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.checkBoxToggleWorker.Appearance = System.Windows.Forms.Appearance.Button;
this.checkBoxToggleWorker.Location = new System.Drawing.Point(497, 230);
this.checkBoxToggleWorker.Name = "checkBoxToggleWorker";
this.checkBoxToggleWorker.Size = new System.Drawing.Size(75, 23);
this.checkBoxToggleWorker.TabIndex = 6;
this.checkBoxToggleWorker.Text = "&Start";
this.checkBoxToggleWorker.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.checkBoxToggleWorker.UseVisualStyleBackColor = true;
this.checkBoxToggleWorker.CheckedChanged += new System.EventHandler(this.OnCheckBoxToggleWorkerCheckedChanged);
//
// timerGuiUpdater
//
this.timerGuiUpdater.Tick += new System.EventHandler(this.OnTimerGuiUpdaterTick);
//
// FormMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(584, 262);
this.Controls.Add(this.checkBoxToggleWorker);
this.Controls.Add(this.numericUpDownTimeoutBetweenInserts);
this.Controls.Add(this.labelSpinsBetweenInsertsText);
this.Controls.Add(this.labelDataGridViewCount);
this.Controls.Add(this.labelDataGridViewCountText);
this.Controls.Add(this.dataGridView);
this.MinimumSize = new System.Drawing.Size(600, 300);
this.Name = "FormMain";
this.Text = "DataGridView Performance Tester";
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.personBindingSource)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownTimeoutBetweenInserts)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.DataGridView dataGridView;
private System.ComponentModel.BackgroundWorker backgroundWorker;
private System.Windows.Forms.BindingSource personBindingSource;
private System.Windows.Forms.Label labelDataGridViewCountText;
private System.Windows.Forms.Label labelDataGridViewCount;
private System.Windows.Forms.Label labelSpinsBetweenInsertsText;
private System.Windows.Forms.NumericUpDown numericUpDownTimeoutBetweenInserts;
private System.Windows.Forms.CheckBox checkBoxToggleWorker;
private System.Windows.Forms.Timer timerGuiUpdater;
private System.Windows.Forms.DataGridViewTextBoxColumn Index;
private System.Windows.Forms.DataGridViewTextBoxColumn lastNameDataGridViewTextBoxColumn;
private System.Windows.Forms.DataGridViewTextBoxColumn firstNameDataGridViewTextBoxColumn;
private System.Windows.Forms.DataGridViewTextBoxColumn bornDataGridViewTextBoxColumn;
private System.Windows.Forms.DataGridViewTextBoxColumn ageDataGridViewTextBoxColumn;
}
Last but not least my little person class that is used for the payload:
public class Person
{
public int Age
{
get
{
// ToDo: better algorithm to determine real age is left as an exercise to the reader. ;-)
var age = (int)((DateTime.Now - Born).TotalDays / 365);
return Math.Max(0, age);
}
}
public DateTime Born { get; set; }
public string FirstName { get; set; }
public int Index { get; set; }
public string LastName { get; set; }
}
Is the bottleneck in processing data from server or in actually putting it to DataGridView? If latter, VirtualMode can help you: http://msdn.microsoft.com/en-us/library/2b177d6d.aspx.
Are you using a BackgroundWorker? Put the code that makes you application to freeze in DoWork event:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
YourFreezingCodeHere
}
And start backgroundWorker like
backgroundWorker1.RunWorkerAsync();
You can do this using BackgroundWorker. In the DoWork method you can iterate update the datagrid.
To update datagrid from Non-UI thread you will need to as follows
Create an extension method like
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
if (control.InvokeRequired) control.Invoke(new MethodInvoker(action), null);
else action.Invoke();
}
}
Update data grid as (assuming dataGrid is your control id and dataSource is your data source)
dataGrid.Invoke(() => { dataGrid.DataSource = dataSource; };
Hope this works for you.
The UI will always be updated by the main/UI thread. That is the way WinForms works.
What you can do is prevent the UI-thread from doing too much. To do that:
Make sure you execute all other processing on one or more different threads.
Only update the UI when useful for the user. I can't see/read a number that is changing every 3 ms so skip DISPLAYING the update.
Note that I use the terms ViewModel, View and Model is the remainder of this answer. I am not forcing you to use MVVM but it makes explaining things easier. You could use MVP or MVC in the same way.
You could create a special kind of ViewModel that raises an event after x milliseconds to check for a 'dirty bits' and raise appropriate PropertyChanged events. This would require you to set the dirty bits in the property setters and NOT raise the PropertyChanged event in the setters.
Perhaps even better might be to keep track of the last time a ViewModel was updated; when it is longer than x milliseconds ago, update the ViewModel from the Model, otherwise don't. This guarantees the UI to be in-sync with the ViewModel. But you have to realize that the ViewModel is not in-sync with the Model. Of course it is possible to create methods to sync the models explicitly.
The choice between these two might depend on how you think about the View-ViewModel relation and how much time this all is costing.
Application.DoEvents();
Use this method inside timer.
private void timer1_Tick(object sender, EventArgs e)
{
Application.DoEvents();
}
You should start timer where your UI freezes or you can start it in form_Load and set small number to your timer's interval to make it ticks frequently. For Example set it to ten.
timer1.Start();
timer1.Interval = 10;
I've done a lot of high volume data transfers (hundreds per second) like this and I think a DataGrid just isn't the control you want. It's designed to present data and let the user edit it, it's not really optimized to be an information stream. At this volume it won't do a user much good to view the data in real time, it's just going to be a stream of data too large and fast to make sense of.
I suggest you continue to use a background worker to do the work (like you said you are) and use the ReportProgress method to send a % done back to a progress bar. You can also update a label on the page with the file you're working on. The label will update automatically and not freeze your UI. To do this, create an instance variable in the class your background worker calls. On your UI, create an instance of that class and in the background worker's ProgressChanged method set your UI label to your class instance variable. It will update each time you call backgroundworker.ReportProgress()
Then put all the info in a log so someone can look at it later. It's just not that beneficial to try to visually take in 350 changes/second.
One solution to this problem is to update your data model periodically, i.e. to batch update them from the communication thread every x milliseconds. A little more information on how you are accessing the server data would be helpful in giving you a more explicit suggestion.
At the same time you should be using virtualised controls (especially the datagrid). Using a virtual grid basically means visible cells are rendered on the fly. Hence you only need to update the data that is currently displayed. As each cell becomes visible it will access the data model to obtain the relevent value at that time. See this link as a starting point to implementing a virtual grid.
By combining these two approaches you should be able to minimise the amount of updates to the grid.
re: The problem is that the UI hangs when such an update happens because it has to happen on the main thread. Is there a known solution for this problem?
no, as you're seeing
Generally, how can you design highly performant enterprise applications in WinForms where the UI is being updated continuously without the application freezing?
At the scale you're describing, you can't. Try limiting the scope of your UI control so it doesn't try to display everything happening everywhere at once, but forces the user to pick an area to focus on that you can update at an acceptable rate.
I recommend to use two layers for handling this scenario.
Non-UI Data Layer:
This layer can grab all updates from your background thread and generate final Data object (Let's call it ObjectX) which is the latest and most current state of data. This layer should run on it's own thread which will not effect UI at all. Also after receiving any update you can have one boolean variable (Let's call it NewUpdateExist) and set it to true which indicates that new changes has been received. Don't forget to use Thread-safe locking mechanism to set this variable to True, to avoid concurrency issues.
UI Sync Layer:
This layer also can run on separated thread. You can have a timer which will trigger on specific interval(*) to checks if there is any new data since last UI update by checking out NewUpdateExist variable and if there is, then Set NewUpdateExist to false and generate new sub-set of data which only required to display on Screen (***). Don't forget to use Thread-safe locking while generating sub-set of data and updating NewUpdateExist variable.
After generating your sub-set of data then you need to Invoke another method by control (In UI Thread) to apply this sub-set of data to the control. This is where UI thread will block until process done so you need to make this method as light as possible! All heavy stuff needs to be done outside of Invoke and only part that relate to UI control needs to be in that method.
(*) As "Hans Passant" mentioned in his comment, human eye only can process 50 milliseconds refresh, but I even suggest to increase this to 100 msec. You can get some ideas from this thread:
What is the shortest perceivable application response delay?
(**) Tricky part in this case is how to update your control with only data that is required instead of pushing all data at once to the UI. I really recommend to implement custom controls to handle this part instead of using standard controls; because you'll have full access to how and when to update UI and you can achieve best performance. For example on Grid you can find out first visible item and number of items that can be displayed on UI and just update that portion instead of trying to update control with all data.
Sorry, I know I suppose to explain solution in short message but this is the shortest version that I can came across. I hope this helps :-)
There is an article posted on MSDN regarding Asynchronous Call Pattern for Windows Forms. I hope this helps.
Use backgroundWorker, it will run inserted code in separated thread, so the application will not freeze.
public void backgroundWorkerPinger_DoWork(object sender, DoWorkEventArgs e)
{
Ping ping = new Ping();
try
{
PingReply pingreply = ping.Send("46.4.106.10", 500);
string active = pingreply.Status.ToString();
if (active == "Success")
{
//Pokud je spojení aktivni pak se nastavi barva labelu na zelenou a vypise se aktivni
ActiveOrNotLabel.ForeColor = Color.Green;
ActiveOrNotLabel.Text = "Aktivní";
// MessageBox.Show("vyjimka2");
if (connection_enabled == false)
{
admini.Enabled = true;
connection_enabled = true;
}
}
if (active != "Success") {
ActiveOrNotLabel.ForeColor = Color.Red;
ActiveOrNotLabel.Text = "Neaktivní";
admini.Enabled = false;
connection_enabled = false;
}
}
catch
{
//Jinak na cervenou a neaktivni
//MessageBox.Show("vyjimka");
ActiveOrNotLabel.ForeColor = Color.Red;
ActiveOrNotLabel.Text = "Neaktivní";
admini.Enabled = false;
connection_enabled = false;
}
}