this is my code :
protected void BtnImport_Click(object sender, EventArgs e)
{
List<DataTable> tb = Helper.SplitTableToManyTables(FileTb, 50); // a list containing multiple DataTable each have 50 rows
int importedRowsCount = 0;
for (int KLoop = 0; KLoop < tb.Count; KLoop++)
{
...
if (QueriesDataHelper.ImportContacts(resTb, int.Parse(TxtHiddenGroupId.Value), Session))
{
importedRowsCount += resTb.Rows.Count;
var script = "DisplayProgressMsg(" + importedRowsCount + ")";
ScriptManager.RegisterStartupScript(this, GetType(), "MyScript", script, true);
if (KLoop == tb.Count - 1)
MessageBox.Show(importedRowsCount.ToString()+" Contacts imported successfully");
}
}
}
QueriesDataHelper.ImportContacts is a function that take a dataTable containing 50 rows and send it to a stored procedure
my problem is RegisterStartupScript is beeing executed after all the dataTables are beiing inserted i want to display a ms after the first DataTable finish tha
Ok,
Http is request/response protocol. But you want send do multiple responses for single request.
I see two ways for you. In both, you will have to run your long running process in separate thread. Then
1) use ajax, to ask server about progress every N seconds, and display result. Client's will track progress with some delay
2) web sockets(SignalR is great for such purposes). In this way you will be able to display progress in real time, but i think it's harder to implement
Related
I'm working on a program that's supposed to establish "n" many SSH connections with a remote Linux server, and run time consuming commands on each connection. The "time consuming operation" is basically running a script that sets up Wireshark and listens to the traffic.
For this I'm using the SharpSSH library for C# and n many BackgroundWorkers as threads. Also for simplicity, the code below has n=2 BGW threads and SSH connections.
Code:
// runs when start is pressed
private void startButton_Click_1(object sender, EventArgs e)
{
sb = new StringBuilder();
DateTime timeNow = DateTime.Now;
clickTime = timeNow.ToString("yyyyMMddhhmmssfff"); // store the exact time of the click
bw = bwArray[0];
int index = 0; // ignore these 2 constants
foreach (BackgroundWorker bgw in bwArray)
{
if (bgw.IsBusy != true)
{
bgw.RunWorkerAsync();
// runWorkerAsync for every BackgroundWorker in the array
//index++;
}
}
}
// runWorkerAsync leads the BGWorker to this function
private void bw_doWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (worker.CancellationPending == true)
{
e.Cancel = true;
}
else
{
// let the UI know of button changes
int p = 0;
object param = "something"; // use this to pass any additional parameter back to the UI
worker.ReportProgress(p, param);
// UI notifying part ends here
// for the simplex case
if (numberOfConnections == 1)
startOperation();
// for the multiplex case
else if (numberOfConnections > 1)
{
//while (p < numberOfConnections)
//{
multiStartOperation();
// p++;
//}
}
Thread.Sleep(500);
}
}
// will be called for all ssh connections (in multiplex case)
private void multiStartOperation()
{
string[] command1Array = { "host2", "host2" };
string[] command2Array = { clickTime + "_h2", clickTime + "_h2" };
for (int index = 0; index < numberOfConnections; index++)
{
// shellArray is an array of SshExec objects
shellArray[index] = new SshExec(IPAddress, username, password);
try
{
shellArray[index].Connect();
}
catch (JSchException se)
{
Console.Write(se.StackTrace);
System.Windows.Forms.MessageBox.Show("Couldn't connect to the specified port.", "Connection Error!");
}
sb.Append(shellArray[index].RunCommand(command1Array[index]) + Environment.NewLine);
// first command is host3, or host4 etc.
// below is the time consuming command to run
string command = "./logcap.sh -c " + command2Array[index] + " -z";
// sb is a global stringBuilder object,
// to which the command output is appended
sb.Append(shellArray[index].RunCommand(command));
}
}
My problem is the following:
When I press the start button on the GUI, both connections should start and run the script. Whereas in the code given above, the first index of shellArray (which consists of SshExec objects) gets connected, prepares the commands and runs the time consuming command, at which point the program goes back to the UI, without even starting the second connection. This is obviously because of the for loop, but I couldn't figure out how to work around this yet.
I need to get the other backgroundworker to establish and run the second command with the second server, so that when I press the stop button on the GUI all connections and threads can stop all together.
PS: The commands will not stop running unless the user clicks stop, which sends a Ctrl-C signal to the server.
I'm relatively new to all the multithreading and networking concepts, so if there is any confusion or mistake please let me know.
Have a nice day.
Thank you for your answers, and the welcome. :)
The problem indeed was not being able to run multiple backgroundworkers at the same time.
I managed to solve the issue. It turns out that all I had to figure out was how to assign backgroundworkers to SSH connections. To do that, I created a class as follows:
class BGW
{
private BackgroundWorker bgw;
private int index;
//getters, setters, constructors...
}
After this, I converted bwArray which was an array of BackgroundWorkers into an array of BGW objects. At initialization, I assigned each BGW object an index.
Instead of having the stupid loop within multiStartOperation(), I sent an integer parameter to multiStartOperation() and that function used that index to reach the allocated backgroundworker.
So far it seems to work.
Have a nice day.
I have an if statement, inside that if statement is a foreach, for accessing each string from a string[].
The strings are some parameters for an NPC which are read from a file. The first one represents the NPC type which are two : "battle" and "teach", the last string[] for a "teach" NPC is "end", and the rest of parameters represents photo names, which I want to load in a "dialog" PictureBox.
My test file looks like this:
teach
poza1
poza2
end
So I have 2 photos to load in the dialog PictureBox. The idea is that I must pause for 5 seconds that foreach statement, otherwise the dialog PictureBox pictures will be loaded too fast, and I won't see them.
So I tried to do that, and here is how the code looks:
if (date[0].Equals("teach")) //the first line of the date[] string, date represent the text from the file
{
foreach (string parametru in date) // i think that you know what this does
{
if (parametru != "teach" && parametru != "end") // checking if the parameter isn't the first or the last line of the file
{
dialog.ImageLocation = folder + "/npc/" + score_npc + "/" + parametru + ".png"; //loading the photo
System.Threading.Thread.Sleep(5000);
}
}
//other instructions , irelevants in my opinion
}
In my attempt of debugging this, I realised that if I use a MessageBox, the function will load the both photos. Also I'm sure of the fact that the parameters will pass the if statement.
It seems so easy to fix this error, but I can't figure out how to do it.
What you're doing now just freezes the UI. Use a System.Windows.Forms.Timer instead. Drop a Timer from the toolbox onto your Form.
Then create some fields that the Timer can access, to store your pics and the current pic position:
private List<string> pics = new List<string>();
private int currentPic = 0;
Finally, load it up with the pics you want to display, and start the Timer to go through them:
pics.Clear();
pics.AddRange(date.Where(x => x != "teach" && x != "end"));
timer1.Interval = 5000;
timer1.Start();
Then you'll have to tell your Timer to display the next picture. Increase the counter, and reset it when necessary. Something like this should work. Modify as necessary.
private void timer1_Tick(object sender, EventArgs e)
{
dialog.ImageLocation = string.Format("{0}/npc/{1}/{2}.png", folder, score_npc, pics[currentPic]);
currentPic++;
if (currentPic >= pics.Count)
currentPic = 0;
// Alternatively, stop the Timer when you get to the end, if you want
// if (currentPic >= pics.Count)
// timer1.Stop();
}
You probably need to issue a PictureBox.Refresh and/or a DoEvents command for the picture box to actually get a chance to load and display the picture.
The MessageBox automatically performs a DoEvents ... which is why it is working during debugging.
I am new to Visual C#, using Visual Studio. I have some calculations that are done in a parallel for loop, and I want to output something to the output text box inside this loop (keeps all output from the calculations on screen). I simply want to be able to append text to the text box (or add text to what is already there).
I found the following link on thread safe calls to windows forms. They show two examples both work for the case they present. They are simply displaying a message in the text box. I want to append data to the text box. I am able to get the following code (first way) to work:
private void setTextSafeBtn_Click(object sender, EventArgs e)
{
this.demoThread =
new Thread(new ThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
}
private void ThreadProcSafe()
{
int length = 10;
Parallel.For(1, length, j =>
{
//this.textBox1.Text = this.textBox1.Text + "Simulation Replication " + j.ToString() + "\r\n";
this.SetText("Simulation Replication " + j.ToString() + "\r\n");
}); // Parallel.For
}
private void SetText(string text)
{
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = this.textBox1.Text + text;
}
}
This will produce the desired output:
Simulation Replication 4
Simulation Replication 2
Simulation Replication 7
...
However, if I try to use the first line inside (currently commented) the parallel for loop instead of the second line, I get the following error:
"Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on."
First question is, being new to parallel computing (done some in Matlab) and being new to Visual C#, what exactly is this error trying to tell me? Is there another way to accomplish what I am trying to do? Is it easier than the method that is working right now?
Now, I also have another issue. I attempted to use the other strategy suggested for this, which is using a background worker, with very similar code. If I just display a message and do not append, everything works fine. If I attempt to append, it does 5 parallel calculations and displays them and then the GUI gangs... just freezes, no error or anything, just stuck. The code I am using looks like this (the SetText is the same as in the code above):
private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e)
{
this.backgroundWorker2.RunWorkerAsync();
}
private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
int length = 10;
Parallel.For(1, length, k =>
{
//this.textBox1.Text = this.textBox1.Text + "Simulation Replication " + k.ToString() + "\r\n";
this.SetText("Simulation Replication " + k.ToString() + "\r\n");
}); // Parallel.For
}
It actually outputs the text for the first 5 and the indexes are 1,2,4,6,8. Is there something to the fact that these are strictly increasing?
I would like to understand why this code works in the above case, but seems to screw up using the background worker. I read that using background workers is the better way to go. Can someone show me a way to get the desired behavior using the background worker?
Finally, I actually just want to know what the best way to do this is. If I am executing a parallel for loop with many calculations (say it takes about 5 seconds to do all the calcs for one iteration), and I want to display the current index number when the iteration begins, what is the best way to do that within the parallel for loop? I want to keep all the data that was in the text box before getting to these parallel calculations...hence the need to append..
Any help or advice here would be much appreciated. It was easy to just tell it to write some output to the console in a console app, that is no problem (using both Matlab and C#). Using the text boxes seems to create an issue. Please Help!
I have a problem that has been driving me nuts for days.. I've tried so many tutorials and code snippets from this and many other websites. I am building a P2P application and i have problems accessing the main thread.
Here is the simple flow of my application:
1. frmMain is shown - user clicks on login button
2. frmlogin is shown - user enters his name
3. after "logging in" - two threads are created (threadTCPlistener and threadUDPlistener)
4. frmDataGrid is shown
Server listen = new Server();
Thread listeningUDPThread = new Thread(new ThreadStart(listen.startUDPServer));
listeningUDPThread.IsBackground = true;
listeningUDPThread.Start();
Thread listeningTCPThread = new Thread(new ThreadStart(listen.startTCPServer));
listeningTCPThread.IsBackground = true;
listeningTCPThread.Start();
frmDataGrid dg = new frmDataGrid();
dg.Show();
5.Threads work in one separate class called "Server". In there they wait for incoming connections, and when TCP thread accepts a connection it starts receinving a file. Upon receiving the file, I would like to change the GUI in the frmDataGrid to add a new row to grid view. I've done something like this:
public void downloadFile()
{
//--receiving of the file--
frmDataGrid fdg = new frmDataGrid();
//filename is the name of received file, and 100's are just for testing (for now).
fdg.verifyUIRequest(fileName, 100, 100);
}
I am calling a method from frmDataGrid VerifyUIRequest that looks like this:
public void verifyUIRequest(string filename, int done, int percent)
{
if (dgvDown.InvokeRequired)
{
dgvDown.Invoke((MethodInvoker)delegate { updateDownDgv(filename, done, percent); });
}
else
{
updateDownDgv(filename, done, percent);
}
After this, the main thread should call the "updateDownDgv" method but the problem is that nothing is happening with my data grid. Here is the code for updating:
public void updateDownDgv(string filename, int done, int percent)
{
foreach (DataGridViewRow r in dgvDown.Rows)
{
if ((string)r.Cells[0].Value == filename)
{
r.Cells[1].Value = done;
r.Cells[2].Value = percent;
}
dgvDown.Invalidate();
return;
}
DataTable tab = (DataTable)dgvDown.DataSource;
DataRow row = tab.NewRow();
row[0] = filename;
row[1] = percent;
row[2] = done;
//MessageBox.Show(done.ToString());
tab.Rows.Add(row);
dgvDown.DataSource = null;
dgvDown.DataSource = tab;
}
I have tried doing this withh begin invoke, with some lambda expressions but nothing succeded. Can anyone please point me to an error or help in some other way? I would really appreciate it.
PS This is my first post, so if it is poorly formatted, i apologize in advance. :)
EDIT:
So the problem is obviously with instances, so I've done something like this:
from Server class where I create an instance of my frmDataGrid class, i now call it's constructor that takes 3 arguments.
frmDataGrid fdg = new frmDataGrid(fileName, 100, 100);
in that constructor, in frmDataGrid, I call verifyUIRequest. But then another error occurs, and I can't seem to figure it out. It stops at
if (dgvDown.InvokeRequired)
{...
error is as folows:
"Object reference not set to an instance of an object", i.e. NullReferenceException. What could be the error?
You are creating a brand new data grid in your downloadFile method. You should be updating the main grid and calling methods on that from your thread methods, not creating a new one that you drop on the floor when the downloadFile method exits.
I have SP in SqlServer (2005+) which uses cursor and do some stuff (on 10,000 rows)
I need something like a progress bar ( to see in my c# web site.).
I can take the 10,000 divided by 100 in such that each 1000 records it will do ( using sql print command) : process of another 1000 is done !!!
But I want to catch that print ( each new print) in my asp.net web site.
how can I catch the print commands / or is there any better way of doing this ?
The process we used to implement something similiar is a little more complicated, but it does work.
We created a webservice that had a number of methods, for example;
[SoapDocumentMethod(OneWay = true), WebMethod]
public void StartUpdate(string reference)
[WebMethod]
public ProcessStatus GetProcessStatus(string reference)
A call to the asynch webmethod would start the process running and passed in a reference number to identify itself. This in turn called a stored procedure which used an update on iterations of the cursor to update another table with 'reference', 'count' and 'status'
The GetProcessStatus would then pass this reference in and pass back the current count and status. If the status was completed, we could move on, alternatively, you could use the 'count' at this point to update the status. An example of this;
protected void Page_Load(object sender, EventArgs e)
{
this.JavaScript.Text = string.Format("<script>setTimeout(\"{0}\", {1});</script>",
this.Page.GetPostBackClientEvent(this.btnRefresh, ""), 3000);
ProcessStatus status= Class.GetProcessStatus(reference);
lblCount.Text = status.Count.ToString();
if (import.Status == (int)ProcessStatus.Status.Complete)
{
Globals.GoUrl("/Import/File/Map.aspx");
}
}
Hope that helps.