I need to cancel the BakgroundWorker from the main form that was launched in another form. I am trying to solve this problem with the help of delegates. However, I cannot undo the BakgroundWorker. Help solve this problem. If there are other solutions, please write.
I give for example the code of the main form
public partial class MainForm : Form
{
public MainForm(string FIO)
{
//some code
}
public event EventHandler<EventArgs> Canceled;
private void Button5_Click(object sender, EventArgs e)
{
if (Canceled != null)
Canceled(sender, e);
}
}
Code of the form where it was launched backgroundWorker
public partial class CarriageForm : Form
{
public CarriageForm(ToolStripProgressBar toolStripProgressBar1, ToolStripLabel toolStripLabel1)
{
//some code
}
private void CarriageForm_Load(object sender, EventArgs e)
{
progBar.Visible = false;
if (!backgroundWorker1.IsBusy)
{
progBar.Visible = true;
progBar.Maximum = GetTotalRecords();
string GetCarriage = "Select dc.ID, dc.CarNumber [Номер вагона],dc.AXIS [Осность],do.ID [OwnerID], do.Name [Собственник],do.FullName [Собственник полное наименование] From d__Carriage dc Left Join d__Owner do on do.ID = dc.Owner_ID";
MainForm mainForm = new MainForm(null);
mainForm.Canceled += new EventHandler<EventArgs>(Button2_Click);
backgroundWorker1.RunWorkerAsync(GetCarriage);
}
//BackgroundWorker1_DoWork...
//BackgroundWorker1_ProgressChanged...
//BackgroundWorker1_RunWorkerCompleted..
public void Button2_Click(object sender, EventArgs e)
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
// Stop the Background Thread execution
Application.UseWaitCursor = false;
System.Windows.Forms.Cursor.Current = Cursors.Default;
backgroundWorker1.CancelAsync();
progBar.Value = 0;
progBar.Visible = false;
TlStpLabel.Text = "Пользователь умышленно отменил";
}
}
}
For clarity
Related
I have a form that is being shown using ShowDialog(), thus it is a modal window.
private void OpenForm(object sender, ItemClickEventArgs e)
{
MyForm testForm = new MyForm();
...
testForm.Enabled = true;
testForm.ShowDialog(this);
var dialogOk = testForm.DialogOK;
if(dialogOk)
{
//do some stuff 1
}
}
There is an "OK" button on the form. When OK is clicked, DialogOk is set to true. Inside MyForm class:
private void OkClick(object sender, EventArgs e)
{
// do some stuff 2
...
DialogOK = true;
Hide();
}
I need to convert this to a non-modal window. The solution seems to be to use Show() instead of ShowDialog(), but when I use Show(), the code does not stop and wait for the OK button to be clicked, so "do some stuff 1" is never called.
Using Show(), how can I keep the behavior to have "do some stuff 1" run after the OK button is clicked?
Update: Here is what I am trying now:
public partial class MyForm: XtraForm
{
public bool DialogOk;
private void OkClick(object sender, EventArgs e)
{
// do some stuff 2
...
DialogOk = true;
Close();
}
}
Method 1:
public partial class MyMainForm : XtraForm
{
private MyForm testForm;
private void OpenForm(object sender, ItemClickEventArgs e)
{
if(testForm == null)
{
testForm = new MyForm();
}
...
testForm.Enabled = true;
testForm.FormClosed += (s, a) => {
var dialogOk = testForm.DialogOk;
if (dialogOk)
{
// do some stuff 1
}
};
testForm.Show(this);
}
}
Method 2:
public partial class MyMainForm : XtraForm
{
private MyForm testForm;
private void OpenForm(object sender, ItemClickEventArgs e)
{
if(testForm == null)
{
testForm = new MyForm();
}
...
testForm.FormClosed += testForm_Closed;
testForm.Show(this);
}
private void testForm_Closed(object sender, EventArgs args)
{
var testForm = (Form)sender;
testForm.Closed -= testForm_Closed;
if (testForm.DialogResult == DialogResult.OK)
{
// do some stuff 1
}
}
}
You can handle Form.Closed event:
MyForm testForm = new MyForm();
testForm.Closed += testForm_Closed;
testForm.Show();
private void testForm_Closed(object sender, EventArgs args)
{
var testForm = (Form)sender;
testForm.Closed -= testForm_Closed;
if (testForm.DialogResult == OK)
// do some stuff 1
}
The easiest way is to move the code from OpenForm to the event handler OkClick. However, if this is not a good spot to put the code because you might want to use the same form for different tasks, you could add a handler for the FormClosed event, that is called after the form is closed and runs the code, e.g.:
private void OpenForm(object sender, ItemClickEventArgs e)
{
MyForm testForm = new MyForm();
...
testForm.Enabled = true;
testForm.FormClosed += (s, a) => {
var dialogOk = testForm.DialogOK;
if(dialogOk)
{
//do some stuff 1
}
};
testForm.Show(this);
}
You can use an async event handler tied to an TaskCompletionSource which listens and awaits the close of the form
private asyc void OpenForm(object sender, ItemClickEventArgs e) {
var source = new TaskCompletionSource<DialogResult>();
EventHandler handler = null;
handler = (s, args) => {
var form = (MyForm)s;
form.FormClosed -= handler;
source.SetResult(form.DialogResult);
}
var testForm = new MyForm();
testForm.FormClosed += handler; //subscribe
//...
testForm.Enabled = true;
testForm.Show();
var dialogOk = await source.Task;
if(dialogOk == DialogResult.Ok) {
//do some stuff 1
}
}
With that you can keep the logic in the OpenForm and allow the code to wait without blocking.
In the form when the button is clicked then all you need to do is set the dialog result and close the form.
public partial class MyForm: XtraForm {
//...
private void OkClick(object sender, EventArgs e) {
// do some stuff 2
// ...
DialogResult = DialogResult.Ok;
Cose();
}
}
This works for me, so not sure why it isn't for you (scratching head)... This form has two buttons, one which opens the same form again and another button that closes the form. The 'parent' form adds an event to the Closed event.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form1 test = new Form1();
test.FormClosed += Test_FormClosed;
test.Show();
}
private void Test_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show("closed -- do something else here!");
}
private void button2_Click(object sender, EventArgs e)
{
Close();
}
}
I received help yesterday from a few wonderful users and thought I would reach out again with another issue I am experiencing. I have always used this.Close(); to close my winform but in this instance I am returning the value from a variable and utilizing this to do such so when my code hits thett this.Close(); the winform is not closed as the this refers to something else
(or at least I think that is what the issue is) - what I am after is a way to close my winform TestData when the code hits the this.Close();
What is currently happening is an endless loop of the code that starts with
if (spreadsheetimported == true)
{
--if I need to show additional code I can--
public partial class TestData : Form
{
public CancelFileImport p1;
public TestData(CancelFileImport _p1)
{
p1 = _p1;
InitializeComponent();
}
private void Testing_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
if (ssi == true)
{
CancelFileImport CFI = new CancelFileImport(this);
CFI.StartPosition = FormStartPosition.CenterParent;
CFI.ShowDialog();
if (cnclsve)
{
this.Close();
}
else
{
//user did not mean to cancel the save
//go back to save method
}
}
}
}
}
public partial class CancelFileImport : Form
{
public static TestData _p1;
public CancelFileImport(TestData p1)
{
_p1 = p1;
InitializeComponent();
}
public CancelFileImport()
{
InitializeComponent();
}
private void btnYes_Click(object sender, EventArgs e)
{
_p1.ssi = true;
this.Close();
}
private void btnNo_Click(object sender, EventArgs e)
{
_p1.ssi = false;
this.Close();
}
}
Try to do it like this
Replace this.Close(); with TestData.ActiveForm.Dispose();
My main form is a mdi container with a menu strip. When I choose Options-Maintenance I want another mdi to appear. This kind of works. Instead of another mdi container along with the design, a regular smaller form appears and not sure why.
public partial class mdiMain : Form
{
static string sTo = ConfigurationManager.ConnectionStrings["connectionTo"].ToString();
public myDataAccess3 data;
public mdiMain()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
data = new myDataAccess3(sTo);
frmLogOn frmLogOn = new frmLogOn(data);
if (frmLogOn.ShowDialog().Equals(DialogResult.Cancel))
{
frmLogOn.Close();
frmLogOn = null;
Application.Exit();
return;
}
frmLogOn.Close();
frmLogOn = null;
this.Focus();
}
catch (Exception e1)
{
MessageBox.Show("There was an error " + e1);
}
}
private void maintenanceToolStripMenuItem_Click(object sender, EventArgs e)
{
mdiMaintenance maintenance = new mdiMaintenance(this,data);
maintenance.Enabled = true;
maintenance.Show();
}
}
public partial class mdiMaintenance : Form
{
private myDataAccess3 data;
private mdiMain mdiMain;
public mdiMaintenance()
{
InitializeComponent();
}
public mdiMaintenance(mdiMain mdiMain, myDataAccess3 data)
{
// TODO: Complete member initialization
this.mdiMain = mdiMain;
this.data = data;
}
private void mdiMaintenance_Load(object sender, EventArgs e)
{
}
Thanks for the help
If the form is intended to be an MDI Child then you need to set the MdiParent property:
private void maintenanceToolStripMenuItem_Click(object sender, EventArgs e)
{
mdiMaintenance maintenance = new mdiMaintenance(this,data);
maintenance.Enabled = true;
maintenance.MdiParent = this;
maintenance.Show();
}
I have two forms, Form 2 is inheriting from Form 1.
What I need to do is that when I close both Form 1 and Form 2 another form which asks if user is sure to quit appears. Then if user clicks Yes, another form which asks if the user wants to save the game appears if and only if the form which the user closes is Form 2 and not Form 1 since for Form 1 there is no saving necessary.
This is what I managed to do:
// These are the Form 1 closing and closed event handlers:
private void GameForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
SureClose sc = new SureClose();
sc.StartPosition = FormStartPosition.CenterScreen;
sc.Show();
}
private void GameForm_FormClosed(object sender, FormClosedEventArgs e)
{
MainMenu menu = new MainMenu();
menu.Show();
}
Then in Sure Close: // Please note that Tournament is Form 2 inheriting from GameForm (Form 1)
private void yesButton_Click(object sender, EventArgs e)
{
this.Hide();
if (GameForm.ActiveForm is Tournament)
{
SaveGame sg = new SaveGame();
sg.StartPosition = FormStartPosition.CenterScreen;
sg.Show();
}
else
{
GameForm.ActiveForm.Close();
}
}
private void noButton_Click(object sender, EventArgs e)
{
this.Hide();
}
// This is the SaveGame Form:
private void saveButton_Click(object sender, EventArgs e)
{
// Still to do saving!
}
private void dontSaveButton_Click(object sender, EventArgs e)
{
this.Hide();
GameForm.ActiveForm.Close();
}
The problem is that when in the yesButton event handler in SureClose Form I have GameForm.ActiveForm.Close(), this is going back to the GameForm Closing event handler therefore the SureClose dialog is appearing again.
I tried doing: if (e.CloseReason() == CloseReason.UserClosing)
but obviously it doesn't work either because the reason of closing will always be the user :/
How can I solve this?
Thanks a lot for any help !
Form1 :
private void GameForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(SureClose())
{
SaveChanges();
}
else
{
e.Cancel = true;
}
}
private bool SureClose()
{
using(SureClose sc = new SureClose())
{
sc.StartPosition = FormStartPosition.CenterScreen;
DialogResult result = sc.ShowDialog();
if(result == DialogResult.OK)
{
return true;
}
else
{
return false;
}
}
}
protected virtual void SaveChanges()
{
}
Form2:
protected override void SaveChanges()
{
using(SaveGame sg = new SaveGame())
{
sg.StartPosition = FormStartPosition.CenterScreen;
DialogResult result = sg.ShowDialog();
if(result == DialogResult.OK)
{
//saving code here
}
}
}
SureClose form and SaveGame form:
private void yesButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
}
private void noButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
hey i am new to c# plz help.
i am writing a program that sorts data in a file and it is a time consuming process so i thought that i should run it in a separate thread and since it has alot of step so i made a new class for it. the problem is that i want to show the progress in the main GUI and i know for that i have to use Invoke function but the problem is that the form control variables are not accessible it this class. what should i do ??????
sample code:
public class Sorter
{
private string _path;
public Sorter(string path)
{
_path = path;
}
public void StartSort()
{
try
{
processFiles(_path, "h4x0r"); // Just kidding
}
catch (Exception e)
{
MessageBox.Show("Error: " + e.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void processFiles(string Dir, string[] key)
{
/* sorting program */
}
and it is used as
public partial class Form1 : Form
{
Sorter sort;
public Form1()
{
InitializeComponent();
}
private void browseBtn_Click(object sender, EventArgs e)
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
textBox1.Text = folderBrowserDialog1.SelectedPath;
}
private void startBtn_Click(object sender, EventArgs e)
{
if (startBtn.Text == "Start Sorting")
{
Thread worker = new Thread(new ThreadStart(delegate() {
sort = new Sorter(textBox1.Text);
sort.StartSort(); }));
worker.start();
}
else
MessageBox.Show("Cancel");//TODO: add cancelling code here
}
}
plz help..
Add an Event to your class that is doing the multi-threaded work, that triggers when the progress changes. Have your form subscribe to this event and update the progress bar.
Note ProgressEventArgs is a little class that inherits EventArgs and has an Integer for the progress.
// delegate to update progress
public delegate void ProgressChangedEventHandler(Object sender, ProgressEventArgs e);
// Event added to your worker class.
public event ProgressChangedEventHandler ProgressUpdateEvent
// Method to raise the event
public void UpdateProgress(object sender, ProgressEventArgs e)
{
ProgressChangedEventHandler handler;
lock (progressUpdateEventLock)
{
handler = progressUpdateEvent;
}
if (handler != null)
handler(sender, e);
}
I would recommend you read up on the BackgroundWorker class. It is exactly for the problem you are trying to solve and makes things a lot easier than doing manual threading yourself.
Brief Example
public Form1()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 101; ++i)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
//Sort Logic is in here.
Thread.Sleep(250);
backgroundWorker.ReportProgress(i);
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy && backgroundWorker.WorkerSupportsCancellation)
backgroundWorker.CancelAsync();
}
You could do something like this:
public delegate void StatusReporter(double progressPercentage);
public class MainClass
{
public void MainMethod()
{
Worker worker = new Worker(ReportProgress);
ThreadStart start = worker.DoWork;
Thread workThread = new Thread(start);
workThread.Start();
}
private void ReportProgress(double progressPercentage)
{
//Report here!!!
}
}
public class Worker
{
private readonly StatusReporter _reportProgress;
public Worker(StatusReporter reportProgress)
{
_reportProgress = reportProgress;
}
public void DoWork()
{
for (int i = 0; i < 100; i++ )
{
// WORK, WORK, WORK
_reportProgress(i);
}
}
}
There are a few option available to solve this sort of issue. In any case, you will have to fiddle with Invoke to get the UI to update.
You could...
...add an event that fires on your new class which your UI can listen to, and Invoke as applicable - you'd still need to pass the data to your worker class (by constructor, properties, method call, etc)
...keep the method as a method on your form, and pas that to start your new thread from (after all, a new thread doesn't have to be starting in a different class)
...change the access modifiers on your controls to be (say) internal such that any class within the same assembly can Invoke changes to the controls, or read from them.
...make your worker class a child of the form it needs to access - it can then see the privates of its parent, as long as it is passed a reference to the instance.