How to use backgroundworker for opening a form? - c#

I have a mdi form which contains some child forms. One of this forms takes time to load. I need to use a backgroundworker to load this form. I tried this code, but I get cross-thread error. Actually I can't set mdiParent for my 'form' through backgroundworker.
any help will be appreciated.
code :
private void tsmiNewExpense_Click(object sender, EventArgs e)
{
tss_lbl1.Text = "Loading...";
if (!BW1.IsBusy)
{
BW1.RunWorkerAsync();
}
}
private void BW1_DoWork(object sender, DoWorkEventArgs e)
{
frmNewExpense frm = new frmNewExpense();
showChildForm(frm);
}
/// <summary>
/// Checks for one instance of this form is running
/// </summary>
/// <param name="frm">the form that will be shown.</param>
private void showChildForm(Form frm)
{
bool exists = false;
foreach (Form item in this.MdiChildren)
{
if (item.Name == frm.Name)
{
item.Activate();
item.StartPosition = FormStartPosition.CenterParent;
item.WindowState = formWindowState;
exists = true;
break;
}
}
if (!exists)
{
frm.MdiParent = this;//this line gets cross-thread error
frm.StartPosition = FormStartPosition.CenterParent;
frm.WindowState = formWindowState;
frm.Show();
}
}

This is using Synchronization context to update Form's lblTimer label:
CS:
public partial class MainForm : Form
{
private readonly SynchronizationContext _context;
public MainForm()
{
InitializeComponent();
// the context of MainForm, main UI thread
// 1 Application has 1 main UI thread
_context = SynchronizationContext.Current;
}
private void BtnRunAnotherThreadClick(object sender, EventArgs e)
{
Task.Run(() =>
{
while (true)
{
Thread.Sleep(1000);
//lblTimer.Text = DateTime.Now.ToLongTimeString(); // no work
UpdateTimerInMainThread(); // work
}
});
}
private void UpdateTimerInMainThread()
{
//SynchronizationContext.Current, here, is context of running thread (Task)
_context.Post(SetTimer, DateTime.Now.ToLongTimeString());
}
public void SetTimer(object content)
{
lblTimer.Text = (string)content;
}
}
Hope this help.

Related

How in main form Cancel backgroundWorker other form

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

Wait Form C# On Panel

I have 3 forms. Names: MainScreen, LoadingForm, MoviesInfo.
When I press the button on the MainScreen, it is doing some works and the LoadingForm is loading inside a panel on the MainScreen.I want to do when works done, show the MoviesInfo Form on the same panel on the MainScreen or panel on the LoadingForm. How can I do that?
I add the forms on the panel like that.
public static void AddFormToPanel(Form frm, Panel panel)
{
frm.TopLevel = false;
panel.Controls.Add(frm);
frm.Show();
frm.Dock = DockStyle.Fill;
frm.BringToFront();
}
//Loading Form
public partial class LoadingForm : Form
{
public Action Worker { get; set; }
public LoadingForm(Action worker)
{
InitializeComponent();
if (worker == null)
{
throw new ArgumentNullException();
}
Worker = worker;
}
private void btnCancel_Click_1(object sender, EventArgs e)
{
this.Close();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Task.Factory.StartNew(Worker)
.ContinueWith(t => { this.Close(); }, TaskScheduler.FromCurrentSynchronizationContext());
}
}
MainScreen Form
private void MovieOnButtonClick(object sender, EventArgs eventArgs)
{
using (loading = new LoadingForm(getMovieData))
{
loading.ShowDialog(this);
}
AddFormToPanel(moviesInfo, panelMain);
}
I don't want this line loading.ShowDialog(this); I want to add loading form inside the panel.
Move the action and the control to the MainForm:
private async void MovieOnButtonClick(object sender, EventArgs eventArgs)
{
var loadingForm = new LoadingForm(); // create a dummy loadingForm
AddFormToPanel(loadingForm, panelMain);
var work = Task.Factory.StartNew(Worker); // Worker = GetMovies or so
await work;
AddFormToPanel(moviesInfo, panelMain);
loadingForm.Dispose();
}
Remove all logic and events from the LoadingForm.
And it is better to use UserControl than Forms to place in that Panel.

How to run a windows form from a backgroundworker runnig through another backgroundworker?

this is the simplified plan for a solution:
for some reasons i need to run a windows form through a backgroundworker that is runnig by another backgroundworker, when the new windows form loads, the older backgroundworker must pause. i write the code like this :
creating a class with name : temp
public class temp
{
static public BackgroundWorker backgroundWorker1 = new BackgroundWorker() { WorkerSupportsCancellation = true };
static public EventWaitHandle ew = new EventWaitHandle(false, EventResetMode.ManualReset);
static public BackgroundWorker back = new BackgroundWorker() { WorkerSupportsCancellation = true };
}
the codes for form1 are :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
temp.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
temp.back.DoWork += new DoWorkEventHandler(back_DoWork);
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
Form2 f = new Form2();
f.Show();
}
private void button1_Click(object sender, EventArgs e)
{
temp.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
temp.back.RunWorkerAsync();
if (temp.backgroundWorker1.CancellationPending)
temp.ew.WaitOne();
}
}
}
and the codes of form2 goes here :
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
temp.backgroundWorker1.CancelAsync();
temp.ew.Reset();
}
}
}
by clicking the button1 from form1 the temp.backgroundworker1 runs and then in the DoWork of temp.backgroundworker1, the temp.back runs and then FORM2 LOADS BUT THE FORM2 HANGS AND BECOMES USELESS AND YOU CANNOT USE THAT ANY MORE.
where did i wrong ?
the whole plan that i'm going to execute is :
we have a For loop that processes every row of a DataGridView.
each time in a certain point, another windowsform opens
and it stops the loop until the user inserts the information and then click on OK button, the windowsform closes and the loop keep on working. i dont know what to do.......
even if i dont cancel working of the temp.backgroundworker in form2load like the code below, the Form2 is useless
private void Form2_Load(object sender, EventArgs e)
{
}
Do not use any UI operation in the work thread (DoWork method). Maybe that's why you set the CheckForIllegalCrossThreadCalls property, but your app will not work properly just suppresses the error when the debugger is attached.
See my answer here for the correct usage of the BackgroundWorker (that is about canceling but you can see the operations in UI and worker thread).
In this particular case what you can use a similar volatile bool to sign the UI thread that the form can be shown. Or, if you want to send different messages between the threads, use a ConcurrentQueue<T> to write and read messages:
public partial class Form1 : Form
{
private enum Message
{
ShowForm2,
SuspendWork,
ResumeWork,
FinishWorker1
// ... and whatever you want
}
private Timer timer;
private ConcurrentQueue<Message> messagesToUI = new ConcurrentQueue<Message>();
private ConcurrentQueue<Message> messagesToWorker = new ConcurrentQueue<Message>();
public Form1()
{
InitializeComponent();
timer = new Timer(this);
timer.Interval = 10;
timer.Tick += PollUIMessages;
timer.Enabled = true;
}
void PollUIMessages(object sender, EventArgs e)
{
// do we have a new message?
Message message;
if (messagesToUI.TryDequeue(out message))
{
switch (message)
{
case Message.ShowForm2:
Form2 f = new Form2();
f.Show();
// todo: in Form2.Close add a Resume message to the messagesToWorker
break;
// ... process other messages
}
}
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
// Here you are in the worker thread. You can send a message to the
// UI thread like this:
messagesToUI.Enqueue(Message.ShowForm2);
bool isWorking = true;
// and here you can poll the messages to the worker thread
while (true)
{
Message message;
if (!messagesToWorker.TryDequeue(out message))
{
// no message: idle or work
if (isWorking)
DoSomeWork(); // do whatever you want
else
Thread.CurrentThread.Sleep(10);
continue;
}
switch (message)
{
case Message.FinishWorker1:
// finishing the worker: jumping out
return;
case Message.SuspendWork:
isWorking = false;
break;
case Message.ResumeWork:
isWorking = true;
break;
}
}
}

Can not Focus() onto a Form?

I tried:
Form myForm = new EULA();
myForm.Show();
this.WindowState = FormWindowState.Minimized;
myForm.BringToFront();
myForm.Activate();
myForm.Focus();
This code brings it to the front, but for some reason I still have to click on the Form for it to have focus, can anyone tell me why?
The form may be focused already, perhaps you want a control inside it (like a textbox or a combo) to be selected instead?
I'll use this code at the form's load method:
private void Form_Load(object sender, System.EventArgs e)
{
controlName.Select();
}
Hi leaf68 just follow my codes. try to figure it out :)
Let say we have MainForm and LoginForm
In our project we have a Static Class we called Program -> The main entry point for the application. as default class to run our projects.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LoginForm());
if (LoginForm._loginSuccess)
{
var m = new MainForm();
Application.Run(m);
}
else
Application.Exit();
}
public static bool UserLogin() //Add some parameter
{
//You Logic here
LoginForm._loginSuccess = true;
return LoginForm._loginSuccess;
}
}
then this is our LoginForm codes
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
public static bool _loginSuccess { get; set; }
public event EventHandler Login;
private void loginButton_Click(object sender, EventArgs e)
{
if (Program.UserLogin())
{
Close();
Dispose();
if (Application.OpenForms.Count > 0)
if (Application.OpenForms["MainForm"].Name == "MainForm")
{
Application.OpenForms["MainForm"].WindowState = FormWindowState.Normal;
Application.OpenForms["MainForm"].Enabled = true;
}
if (Login != null)
Login(this, EventArgs.Empty);
}
}
}
then assuming that we successfully Login To MainForm so this is our MainForm codes
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void logOutButton_Click(object sender, EventArgs e)
{
//Hide();
Enabled = false;
WindowState = FormWindowState.Minimized;
var f = new LoginForm();
f.Login += loginShow;
f.Show();
f.Activate();
}
private void loginShow(object sender, EventArgs args)
{
Show();
}
}
I hope it helps you :)
I have a form not visible, so only the tray icon.
I just use:
this.ShowInTaskbar = true;
this.WindowState = FormWindowState.Normal;
this.Activate();
In the above order. Program comes to the front and is activated, meaning typing actually writes in the active field.
This works:
when program appears automatically and
when user selects tray menu item.

Simple Thread Programming

I have started to play with threads in c#, but need now help, here is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoCount();
}
public void DoCount()
{
for (int i = 0; i < 100; i++)
{
objTextBox.Text = i.ToString();
Thread.Sleep(100);
}
}
}
its a simple win forms with a textbox, i want to see the "counting", but as you see in my code, the textbox shows me 99, it count till 99 and then shows up.. i`ll think, i have to manage this in a new thread but dont know how!
Use a BackgroundWorker. There is a BackgroundWorker overview on MSDN.
Here is an example of how your code might look:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker backgroundWorker = (BackgroundWorker)sender;
for (int i = 0; i < 100; i++)
{
backgroundWorker.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox1.Text = e.ProgressPercentage.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
}
Other notes:
Remember to set WorkerReportsProgress in the designer if you want the progress to work.
When using a BackgroundWorker it is also often useful to use the ProgressBar control.
If you want to be able to cancel the background worker, that is possible too. See CancelAsync and WorkerSupportsCancellation.
When the background worker completes it fires the RunWorkerCompleted event.
This might be what you are looking for:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoCount();
}
public void DoCount()
{
Thread t = new Thread(new ThreadStart(delegate
{
for (int i = 0; i < 100; i++)
{
this.Invoke((Action) delegate { objTextBox.Text = i.ToString(); });
Thread.Sleep(1000);
}
}));
t.IsBackground = true;
t.Start();
}
}
Notes
Uses a basic Thread not a BackgroundWorker
Uses Invoke to update the textbox on the UI thread
Sets IsBackground to true so the program exits if the form is closed before the loop is done.
You may want to try out the SynchronizationContext to do this.
Here's a quick example I threw together a while back:
public partial class Form1 : Form
{
private SynchronizationContext c;
private Thread t;
private EventWaitHandle pause =
new EventWaitHandle(false, EventResetMode.ManualReset);
public Form1()
{
this.InitializeComponent();
this.c = SynchronizationContext.Current;
}
private void Form1Activated(object sender, EventArgs e)
{
this.t = new Thread(new ThreadStart(delegate
{
this.pause.Reset();
while (this.t.IsAlive && !this.pause.WaitOne(1000))
{
this.c.Post(
state => this.label1.Text = DateTime.Now.ToString(),
null);
}
}));
this.t.IsBackground = true;
this.t.Start();
}
private void Form1Deactivate(object sender, EventArgs e)
{
this.pause.Set();
this.t.Join();
}
/// <summary>
/// Button1s the click.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void Button1Click(object sender, EventArgs e)
{
this.Close();
}
}
You don't need a thread to do this kind of thing at all - consider changing your code to be event driven and use a System.Windows.Forms.Timer object to implement your timings. Using timers for this has a huge advantage - it doesn't cost 1MB of memory (a thread does), and you don't need to synchronize them - windows does it for you.
After Thread.Sleep, try this:
this.Update();
Don't call DoCount directly, call ThreadPool.QueueUserWorkItem(DoCount). This will run DoCount in a new thread. (There are other ways that also work, but this is the simplest that works well.)
The next problem is that you can't directly set the textbox from the thread.
To solve this, use code similar to:
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
See http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx for the full example.
My solution is virtually the same as Mark's. The only difference is I check InvokeRequired in my ProgressChanged event. Here's my sample code:
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace tester
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
if (!backgroundWorker1.IsBusy)
backgroundWorker1.RunWorkerAsync();
}
/// <summary>
/// This delegate enables asynchronous calls for setting the text property on a control.
/// </summary>
delegate void SetTextCallback(string status);
private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void BackgroundWorker1ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (label1.InvokeRequired)
Invoke(new SetTextCallback(SetLabelText), new object[] { e.ProgressPercentage.ToString()});
else
SetLabelText(e.ProgressPercentage.ToString());
}
private void SetLabelText(string text)
{
label1.Text = text;
}
}
}
Multithreading could solve this but for something as simple as this counter it is unnecessary.
Another user recommended this.Update(). This works to make the numbers appear because the UI will redraw itself. But it doesn't address the fact that the window is not responsive (you can't move it around).
The third solution and my recommendation for this particular program is Application.DoEvents(). What this does is tell the underlying native window to execute its ProcessMessages method on the message pool. The message pool contains event messages that Windows has sent to it when the window needed to be redrawn, mouse has moved, the form has been moved, minimized, etc. Those instructions were sent by Windows and have been queued. The problem is that the program will not process them until the UI is idle. You can force it by calling this method.
Application.DoEvents() will yield a window which responds as expected in 100 ms intervals. It may be a tad choppy (threads would be more responsive) but it is very easy to put it in and is often sufficient.
for (int i = 0; i < 100; i++)
{
objTextBox.Text = i.ToString();
Application.DoEvents();
Thread.Sleep(100);
}
Here's my shot at a simple example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Action countUp = this.CountUp;
countUp.BeginInvoke(null, null);
}
private void CountUp()
{
for (int i = 0; i < 100; i++)
{
this.Invoke(new Action<string>(UpdateTextBox), new object[] { i.ToString() });
Thread.Sleep(100);
}
}
private void UpdateTextBox(string text)
{
this.textBox1.Text = text;
}
}

Categories