C# Continue To Next Function Without Closing The Current Windows Form - c#

Is there any way that I can continue to the next function below (within the same function class) without closing the current m_frmLotEntry?
void ShowLotEntry()
{
DialogResult _dlgRet = DialogResult.None;
_dlgRet = m_frmLotEntry.ShowDialog(this);
// Next function here
}
Updates :
I managed to proceed with the code by changing the codes to below.
void ShowLotEntry()
{
DialogResult _dlgRet = DialogResult.None;
BeginInvoke(new System.Action(() => m_frmLotEntry.ShowDialog()));
// Next function here
}
The programs now proceed with the next step. However, I have another issue which is the next next function requires some data from the users key-in in the previous windows form. Therefore, is there any possible way that I can do to stop halfway through the next process?
void ShowLotEntry()
{
DialogResult _dlgRet = DialogResult.None;
BeginInvoke(new System.Action(() => m_frmLotEntry.ShowDialog()));
// Proceed with this function here.
// Stop before the function below.
if (_dlgRet == DialogResult.OK)
{
// Some codes here
}
Thanks.

ShowDialog is blocking operation and waits the return. You can start thread/task before showing the dialog and that would run "behind the scene" even though that the dialog is open. What about Show() would that be enough?
Check this answer for more details:
Async ShowDialog

Related

Can't click button when using Thread.Sleep in C#?

First, I want to check if my object is null. It will sleep 20 milliseconds in while loop gives user can be interactive with UI.
If the condition in while is correct, I can click the button in UI to break this loop and continues other code.
I tried with by original code:
while (browser.FindElementById("iframe") == null)
{
if(buttonWasClicked == true)
return;
Thread.Sleep(20);
}
I was tried with:
Task.Delay(10000).ContinueWith(x =>
{
while (browser.FindElementById("iframe") == null)
{
if(buttonWasClicked == true)
return;
}
});
Seem block code working in the background. So, it skips block code and executes all next line code.
I want it must check before executing next code.
So, I tried with another method. I using Timer but I don't usually use Timer in the application, I don't have any experience with this.
I created new Timer like timer1 and set interval = 20.
At timer1_Tick event, I add code:
private void timer1_Tick(object sender, EventArgs e)
{
if (browser.FindElementById("iframe") == null)
{
if(buttonWasClicked == true)
break;
}
else
timer1.Stop();
}
And at original code, I replace with:
timer1.Start();
At button_Clicked event, I set to:
timer1.Stop();.
Have any issues with my code?
In debug, it skip event timer1_Tick() and execute all code after line timer.Start().
Assuming your "wait for button push" code is in the GUI thread, this looks like an good use for await. Consider creating an awaitable event (manual or auto) and using that instead of buttonWasClicked. You main code would then look like:
evtButtonClicked = new AsyncManualResetEvent();
await evtButtonClicked.WaitAsync();
// continuation code...
Now, when you call evtButtonClicked.Set(), the continuation code will be queued up to execute on the GUI thread. Recall that the code after await is effectively wrapped into a Task.ContinueWith(), which means that your GUI thread will not block while waiting for the event.

stop a thread before closing form

Using .NET4, c#,
I have a grid on a winform. When I hit a process button, I need to perform a timely operation with a few steps on each row and then update the row with a result. This way the user can see progress while the next row is processing. If the user hits cancel or closes the form, I need to finish the process for the current row and then break or close form. Based on my research, here is what I came up with. I'm left with 2 issues.
Here's my code:
delegate void OperateGridMethodDelegate();
delegate void UpdateProgressDelegate(int rowIndex, string msg);
bool isCancelled = false;
private void ButtonOne_Click(object sender, EventArgs e)
{
isCancelled=false;
OperateGridMethodDelegate delegate = new OperateGridMethodDelegate(LongProcess);`
delegate.BeginInvoke(null,null);
}
private void ButtonTow_Click(object sender, EventArgs e)
{
MyButtonOne.Enabled=false;
isCancelled=true;
}
private void LongProcess()
{
foreach (DataRow row in dataTableOne.Rows)
{
//Do Lengthy Process
DisplayResult( rowIndex, "this is result of operation" );
if(isCancelled)
{
MyButtonOne.Enabled=true;
break;
}
}
}
private void DisplayResult(int rowIndex,string message)
{
if(myGrid.InvokeRequired == false)
{
myGrid.Rows[rowIndex].value = message;
}
else
{
UpdateProgressDelegate delegate = new UpdateProgressDelegate(DisplayResult);
myGrid.BeginInvoke(delegate, new object[] { rowIndex, message });
}
}
The 2 issues I'm left with are:
When the form is closing, I want to wait for the LongProcess (that was called by my OperateGridMethodDelegate) to finish the row it's up to and then cancel (same way I did with ButtonTwo), but I can't get this to work.
I notice a weird behavior when debugging and I'm not sure if this is a bug or correct, when a row is finished processing, the result does not get set. Instead, the next row is processed, and then the previous row result gets displayed. The second row result is displayed when the third row finishes etc. Then the last 2 row results get displayed basically at the same time.
Am I approaching this correctly?
If you've launched some task in a thread, to wait for it to finish before proceeding you can do:
yourThread.Join();
This will block until the thread exits. You may want to use something like this in your Form_Closing event
Alternatively, if you want the thread to automatically terminate if the application is terminated, you need to set the background property of the thread:
yourThread.IsBackground = true;
This means it is a background thread of your main (UI) thread, so it will end with the main thread.
Edit:
Using your own thread:
private Thread yourThread = new Thread(() = > LongProcess());
yourThread.IsBackground = true;
yourThread.Start(); // This will begin your 'LongProcess' function.
Now if you want to block somewhere and wait for this to complete, you can do what I mentioned first: yourThread.Join();
Also note that if you set IsBackground = false and someone closes the application, that thread will not exit immediately, it will continue until it is complete despite the UI window being closed.

killing a thread from within in a form

my application calls crystal reports viewer to display a report. I run the report viewer in a separate thread. It works just fine. it displays the report properly. my problem is i want to kill the report while it is processing if it is taking too long to run. While the report is processing the busy indicator is spinning and it seems to block any UI on the report viewer form. My report viewer form has a crystal reports viewer on it along with a close button at the bottom of the form itself. i would like to be able to click the close button and have it stop the processing. Here is my code to run the viewer in a single apartment thread
public void RunReportStep1(UAReport report)
{
UAReportService service = new UAReportService(report);
service.RunReport();
var reportDocument = service.ReportDocument;
Thread staThread = new Thread(r => { RunReportStep2((ReportDocument) r); });
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start(reportDocument);
staThread.Join();
}
public void RunReportStep2(ReportDocument reportDocument)
{
ReportViewerForm form = new ReportViewerForm(reportDocument);
form.BringToFront();
form.ShowDialog();
if (form.DialogResult == DialogResult.OK)
{
}
what is the best way to kill the thread from within the report viewer form while the processing is going on. Once the processing completes and the report is destroyed closing the report is no problem. It's only a problem while the processing is going on before the report is displayed. While the report is processing the close button is not responsive. sometimes if i click it repeatedly i can get a response and the report cancels. but it is not consistent and i have to click it repeatedly. that is not acceptable for my clients to have to do.
Call the service.RunReport() from another thread and wait for that thread to finish or for a certain amount of time to pass. I didn't write all the code for this, but anything I didn't write I a least described.
// Global so it can be reached from both threads
UAReportService service;
// Global variable that is written to when the report doc is ready:
ReportDocType reportDoc; //Can't use var here unfortunately
public void RunReportStep1(UAReport report)
{
service = new UAReportService(report);
Thread staThread = new Thread(r => { RunReportStep2((UAReportService)r); });
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start(service);
// Save time the thread started:
DateTime start = DateTime.Now;
// Display a loading box (label or something, up to you).
// Then this function will end and your user can do stuff in the UI again (like press Cancel).
myLoadingBox.Visible = true;
}
public void RunReportStep2(UAReportService service)
{
service.RunReport();
reportDocument = service.ReportDocument;
}
// Call this new function periodically to see if the service thread finished, maybe once every second.
public checkAndDisplay()
{
// If thread is finished or 30 seconds have passed.
if (staThread.IsAlive == false || (DateTime.Now - start).TotalSeconds > 30)
{
// Show a message saying report failed to run
}
else // Else you can now show your report viewer
{
ReportViewerForm form = new ReportViewerForm(reportDocument);
form.BringToFront();
form.ShowDialog();
if (form.DialogResult == DialogResult.OK)
{
}
}
}
// Code for cancelling the report service call if user presses cancel button.
private void CancelButton_Click(object sender, EventArgs e)
{
// Terminate the report service thread.
staThread.Abort();
// Abort() isn't the nicest way to do this so if you can set a timeout on the UAReportService object (where it quits running after so long) that would be better.
// Or have it periodically check a variable to see if it should quit (see link below)
}
MSDN example of gracefully closing a thread.

Upgraded winforms app now getting NullReferenceException calling menuitems PerformClick method, any ideas?

Background:
A WinForms app, originally written in .NET 1.1 migrated via Visual
Studio to .NET 4.0. Typical menu bar at the top of main app form (NB:
not migrated to ToolStripMenuItem), as you might expect there is a
File menuitem that contains an Exit menu item.
I have implemented Ctrl-L shortcut which will bring up a modal lock
form. I also have a timer component on the main form which will
automatically bring up the lock form if there's no activity for
configurable amount of time.
When the lock form is shown you can either unlock (requiring user to
login again) or quit. If you choose to quit then my code does a
fileExitMenuItem.PerformClick() call.
Problem:
For some strange reason after the migration if I quit from the lock
form which was displayed either automatically or due to Ctrl-L
shortcut then I get a NullReferenceException thrown on the
fileExitMenuItem.PerformClick() line of code.
fileExitMenuItem is not null; with break-when-exception-thrown
switched on I can browse all of fileExitMenuItems properties.
I can break in the designer code of the form and watch the click event
handler being attached. If I use the File >> Exit menu item directly I
can break on the code in the event handler.
So this is a total WTF moment. Any suggestions on what to look at will be greatly appreciated
[UPDATE 1] As requested here is some code - this method is called whenever the user presses Ctrl-L or the lock timer elapses:
private void LockApplication()
{
try
{
// Stop the timer so we don't get any more elapsed events whilst we are waiting
// for a user to respond to the lockdown dialog. In addition stop the callout reminder
// time as once we have locked we don't want that doing it's thing.
lockdownTimer.Stop();
calloutsReminderTimer.Stop();
// Clone the current identity so we can check it later.
var previousIdentity = (CudosIdentity)BOUtilities.CurrentIdentity.Clone();
// Show lockdown form.
System.Windows.Forms.DialogResult result;
using (var lockForm = new Forms.applicationLockedForm())
result = lockForm.ShowDialog(this);
if (result == DialogResult.OK)
{
// Did we unlock with a different login?
if (!previousIdentity.Equals(BOUtilities.CurrentIdentity))
{
// Yes, so lose all changes.
CloseOpenForms();
if (_currentLoadSpec != null)
_currentLoadSpec.CancelContent();
}
RefreshLockTimerSetting(null);
}
else
fileExitMenuItem.PerformClick();
}
catch (Exception ex)
{
Helper.LogError(ex);
}
finally
{
lockdownTimer.Start();
calloutsReminderTimer.Start();
}
}
This is the code for the exit menu item:
private void fileExitMenuItem_Click(object sender, System.EventArgs e)
{
Application.Exit();
}
When the following line in LockApplication method from above is called I get the NullReferenceException:
fileExitMenuItem.PerformClick();
[UPDATE 2] Call stack info when the above line is executed:
[External Code]
Cudos.exe!Cudos.mainForm.LockApplication() Line 1132 + 0x10 bytes C#
Cudos.exe!Cudos.mainForm.fileLockCudosMenuItem_Click(object sender, System.EventArgs e) Line 1594 + 0x8 bytes C#
[External Code]
Cudos.exe!Cudos.mainForm.Main() Line 1880 + 0x1d bytes C#
[External Code]
I am not sure, but I will try to remove the restart of the timers if you call the Perform_Click.
The Tick event could be called when there is no more the application because you call Application.Exit().
private void LockApplication()
{
try
{
lockdownTimer.Stop();
calloutsReminderTimer.Stop();
.....
if (result == DialogResult.OK)
{
......
lockdownTimer.Start();
calloutsReminderTimer.Start();
}
else
fileExitMenuItem.PerformClick();
}
catch (Exception ex)
{
Helper.LogError(ex);
lockdownTimer.Start();
calloutsReminderTimer.Start();
}
// remove the finally clause
}
In the end I gave up and just hacked it by changing the fileExitMenuItem.PerformClick() to Application.Exit(). So I still don't have any idea why it was throwing the exception but it at least now works. I guess if I put more logic into the fileExitMenuItem click handler I will just have to remember to extract it into a method and update this hack to call that method to.

Closing dialogs from another threads

I'm having a problem with threads. My code:
Task.Factory.StartNew(() =>
{
cts = new CancellationTokenSource();
var lines = File.ReadLines(Path.Combine(Environment.CurrentDirectory, "urls.txt"));
try
{
var q = from line in lines.AsParallel().WithDegreeOfParallelism(30).WithCancellation(cts.Token)
let result = Parse(line, cts.Token)
select new
{
res = result
};
foreach (var x in q)
{
if (x != null)
{
Console.WriteLine("{0}", x.res);
}
}
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
});
Now in Parse I have:
public String Parse(String url,CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
/* many lines of code */
InputForm iForm = new InputForm();
iForm.setPageData(pageData);
if (iForm.ShowDialog() == DialogResult.OK)
{
string userInput = iForm.textBox.Text;
/* code block */
return result;
} else {
return Parse(newUrl,ct);
}
}
I'm using ShowDialog because I need to get user input from iForm (this form has a timer and is auto closed after 60 seconds). Now, when I opened about 30 forms and click Cancel (on main form) this dialog forms need to be closed manualy. Is it posible to close this form after clicking Cancel?
I do this a lot.
What you're going to need to do is
create a way to communicate with your Main Thread Of Execution (MTOE)
call your main thread and wait for the response
in your main thread, display your dialog box
set the return value for your thread
signal your thread that you are done
A custom event handler works great for getting a message from your thread back to the MTOE.
A ManualResetEvent is good for your thread to know when the MTOE is complete.
A class instance can be passed in an event handler that the MTOE uses to fill a few data items and pass back to the thread whenever it is done.
Typically, when I create my special class, it contains the event handler and the ManualResetEvent object.
From your MTOE, if you close your form, you can signal all of your waiting dialog boxes to Cancel.
This would require a little redesign, but I think it would give you what you are after.
You may want to look at http://msdn.microsoft.com/en-us/library/system.windows.forms.application.openforms.aspx
You could iterate over the open open forms and call close on those that are of type InputForm
EDIT:
The below comment is correct this would throw an exception. You would actually need something like FormToClose.BeginInvoke(delegate ()=> FormToClose.Close());

Categories