I have windows application in which i need to save data into database and after saving data load crystal report to show report,all this happens on click of save button.
I have button named btn_Submit on click of this data is saved and display report, while saving it takes time so i want to show progress bar for mean time so that user get known that data is in process.how i can do with this windows application.
I gone through this link http://www.codeproject.com/Tips/83317/BackgroundWorker-and-ProgressBar-demo but don't get it exactly I want.
I am aware of background worker but never used it.
I have used background worker and progress bar as given in above link but progress bar does not stop at all once it started.
Can any one help me?can u give any example or link that demonstrate scenario?.
This code i added on Dowork();
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
PrintData arg = (PrintData)e.Argument;
SalesMaster sm = arg.SalesData;
BrokerMaster bm = arg.Broker;
CustomerMaster ctm = arg.Customer;
CompanyMaster cm = arg.Company;
ArrayList hb = arg.Arrardata;
int totunit = arg.totunit;
decimal globalamt = arg.golbamt;
SalesReport sreport = new SalesReport(sm, ctm, cm, bm, hb, totunit, glb_totalamt);
sreport .MdiParent = arg.parentf;
sreport .WindowState = FormWindowState.Maximized;
sreport .Show();
}
i get error on this line sreport .MdiParent = arg.parentf;
This error:
Cross-thread operation not valid: Control 'frmParent' accessed from a thread other than the thread it was created on.
what should be done here?
Suscribe to DoWork and RunWorkerCompleted events and
void btn_Submit_Click(object sender, EventArgs e)
{
btn_Submit.Enabled = false; // disable button while saving report
lbl_Status.Text = "Please wait..";
backgroundWorker.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// save report here
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn_Submit.Enabled = true; // enable button
lbl_Status.Text = "Report saved";
}
Instead of using label, you can show PictureBox with spinner wait image. I don't like to see progress bar, which does not show percentage of task - I expect that when progress bar will be filled, task will be completed. If you really want to use progress bar, then, I'd go with timer component (set timer's interval to desired refresh rate):
void btn_Submit_Click(object sender, EventArgs e)
{
btn_Submit.Enabled = false; // disable button while saving report
timer.Start();
progressBar.Visible = true;
// backgroundWorker.RunWorkerAsync(new object[] { "Foo", 42 });
// backgroundWorker.RunWorkerAsync(new CustomType("Foo", 42));
backgroundWorker.RunWorkerAsync(new { Foo = "Foo", Bar = 42 }););
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// object[] args = (object[])e.Argument;
// CustomType arg = (CustomType)e.Argument;
dynamic arg = (dynamic)e.Argument;
string foo = arg.Foo;
int bar = arg.Bar;
// save report here
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btn_Submit.Enabled = true; // enable button
timer.Stop();
progressBar.Visible = false;
}
void timer_Tick(object sender, EventArgs e)
{
if (progressBar.Value == progressBar.Maximum)
{
progressBar.Value = progressBar.Minimum;
return;
}
progressBar.PerformStep();
}
Try to reset the ProgressBar's Value property to 0, like in the code below:
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
Implemented event handler:
void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Value = 0;
}
To stop or hide the progressbar use background worker's completed event.
Related
I'm doing a simple login form that will show the login process after they click button. It will verify the user access rights and give true or false.
I got an error on this.Hide();
System.InvalidOperationException: 'Cross-thread operation not valid: Control 'formLogin' accessed from a thread other than the thread it was created on.'
Any help would be greatly appreciated. Thanks!!
private void btnLogin_Click(object sender, EventArgs e)
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
PBLogin.Style = ProgressBarStyle.Marquee;
PBLogin.MarqueeAnimationSpeed = 50;
bgw.RunWorkerAsync();
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
PBLogin.Style = ProgressBarStyle.Blocks;
PBLogin.MarqueeAnimationSpeed = 0;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
if (CheckAuthorization())
{
MessageBox.Show("Login Successfully");
TestScript next = new TestScript();
next.Show();
this.Hide();
}
else
{
MessageBox.Show("Login Failed");
}
From a background thread you can't reach the UI, that's what the exception is trying to tell you. In your background thread you should try to focus only on the Authorization logic itself. Try to avoid UI component manipulation.
In the RunWorkerCompleted event you can indeed modify the UI components. So, do the calculation on a dedicated background thread and do the UI manipulation inside the completion event.
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//TODO: Check against e.Error and e.Cancelled before you try to access e.Result
if((bool)e.Result)
{
//UI modification
}
else
{
//UI modification
}
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
var isAuthorized = CheckAuthorization();
e.Result = isAuthorized;
}
I have a parent form that send a data table to a child form by Delegate.
the Delegate is executed and i have also gotten the the table on the child side.
i want to append a text into my richtextbox control to announce the user what is going on and then run a backgroundworker. but i get the STAThread Exception. i know some thing about Invoke(Delegate) and about single-Thread but i do not know how can i overcome to this cross-threading. Any help is appreciated.
The codes from Appent To RichTextBox are not execute with debugging (i know it is possible with run of the *.exe file).
//What i am doing and trying:(SetDaTableAndFileNameFn is my received Delegate)
public void SetDaTableAndFileNameFn(System.Data.DataTable DataTable)
{
//Test The Parent Has Sent And Child Has Received.
MessageBox.Show("Ruger Parent...");
dt.Clear();
dt = DataTable;
//Check whether My dt Filled Correctly.
MessageBox.Show(dt.Rows[2][2].ToString());
richTxtBxExprtr.AppendText(">>> Creating And Transferring Data To The File...");
//BGWorker.
bGWExprtrLod.WorkerReportsProgress = true;
bGWExprtrLod.RunWorkerAsync();
}
private void ExportToTxtIrrigularly(System.Data.DataTable DataTable)
{
// Using Microsoft.Office.Interop.Word.Application to export datatable.
}
private void xBtnExprt_Click(object sender, EventArgs e)
{
SaveFileDialog svFDialXls = new SaveFileDialog();
svFDialXls.Filter = "Plain text(*.txt)| *.txt";
svFDialXls.Title = "Export Data As Text";
svFDialXls.InitialDirectory = #"Desktop";
if (svFDialXls.ShowDialog() == DialogResult.OK && svFDialXls.FileName != null)
{
WordFilePath = svFDialXls.FileName.ToString();
//Fire An EventHandler In The Parent Side To Fill A datatable With A DGV.
ExportImport ExportTxFile = new ExportImport(allRowsExprt, fRowTEndExprt, fRowTEndValExprt, FTRowExprt,
FTRowValFExprt, FTRowValTExprt, allFieldsExprt, visFieldExprt, slctdFieldExprt);
OnExportTxFile(ExportTxFile);
//Filled datatable Will Send Back from parent side with theSetDaTableAndFileNameFn
}
}
private void bGWExprtrLod_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke(new Action(() =>
{
richTxtBxExprtr.AppendText(">>> Start Processing...\n>>> Copying Data Take A Little Time.\n>>> Be Patient...\n>>> Loadind Data...\n-----------------------------------------------\n");
ExportToTxtIrrigularly(dt);
}));
}
private void bGWExprtrLod_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Invoke(new Action(() =>{richTxtBxExprtr.AppendText(">>> Line NO. [" + e.ProgressPercentage.ToString() + "] Is In Progress...\n");
richTxtBxExprtr.ScrollToCaret();
}));
}
private void bGWExprtrLod_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{this.Invoke(new Action(() => { richTxtBxExprtr.AppendText(">>> The Process Is Completed Successfuly.\n"); }));
}
the executed mthod in the BGWorker will open a savedialogbox() and then export my datatable to a text format using
Microsoft.Office.Interop.Word.Application
Not really sure what kind of setup you've got, but it will look something like this:
private BackgroundWorker bGWExprtrLod;
private void Form1_Load(object sender, EventArgs e)
{
bGWExprtrLod = new BackgroundWorker();
bGWExprtrLod.WorkerReportsProgress = true;
bGWExprtrLod.ProgressChanged += BGWExprtrLod_ProgressChanged;
bGWExprtrLod.RunWorkerCompleted += BGWExprtrLod_RunWorkerCompleted;
bGWExprtrLod.DoWork += BGWExprtrLod_DoWork;
}
public void SetDaTableAndFileNameFn(System.Data.DataTable DataTable)
{
// ... other code ...
bGWExprtrLod.RunWorkerAsync();
}
private void BGWExprtrLod_DoWork(object sender, DoWorkEventArgs e)
{
bGWExprtrLod.ReportProgress(0, ">>> Creating And Transferring Data To The File...");
// ... do some work ...
}
private void BGWExprtrLod_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string msg = e.UserState.ToString();
richTxtBxExprtr.AppendText(msg);
}
private void BGWExprtrLod_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
richTxtBxExprtr.AppendText("Transfer Complete!");
}
** EDIT **
Start by moving the call to you export method outside the Invoke() call:
private void bGWExprtrLod_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke(new Action(() =>
{
richTxtBxExprtr.AppendText(">>> Start Processing...\n>>> Copying Data Take A Little Time.\n>>> Be Patient...\n>>> Loadind Data...\n-----------------------------------------------\n");
}));
ExportToTxtIrrigularly(dt);
}
Unfortunately I was not able to find relevant answer to my problem. I have a object encoder that has an event "VideoEncoding". It passes custom EncodingEventArgs that include various Properties like Progress, Size etc. I can output this info to Console or write to text file. But when I try to utilize it in WinForms I'm not able to pass that information to UI like label or progress bar. I tried different approaches. Background Worker seems like a good idea, The problem is that Background Worker cannot subscribe to VideoEncoding event, neither it will take my custom EventArgs. This is what i was able to put together. Maybe there is a different way to do it using delegates that would communicate with UI. Any suggestions are welcome. Thank you.
public partial class Form1 : Form
{
private BackgroundWorker bw;
int _progress;
public Form1()
{
InitializeComponent();
this.bw = new BackgroundWorker();
this.bw.DoWork += new DoWorkEventHandler(bw_DoWork);
this.bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
this.bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
this.bw.WorkerReportsProgress = true;
this.button1.Click += new EventHandler(button1_Click);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.label1.Text = "The job is: " + e.Result.ToString();
this.button1.Enabled = true;
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.label2.Text = e.ProgressPercentage.ToString() + "% complete";
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
this.Encode
worker.ReportProgress(_progress);
e.Result = "Completed";
}
private void button1_Click(object sender, EventArgs e)
{
if (!this.bw.IsBusy)
{
this.bw.RunWorkerAsync();
this.button1.Enabled = false;
}
}
public void Encode()
{
var job = new EncodingJob();
//setup encoding job
//subscribe to an event
ffmpeg.VideoEncoding += GetProgress;
ffmpeg.DoWork(job);
}
public void GetProgress(object sender, EncodingEventArgs e)
{
_progress = (int)e.Progress;
}
}
Try to call the background workers ReportProgress in the GetProgress Method. How should the form know your progress if you don't signalize it?
My form contain two controls: button1 and timer1
timer1.Interval=1000; timer1.Enable=true;
While click button1, application on windows will start. Ex:notepad will show.
But timer1 is not running while notepad is showing.
How to timer1 so running ??.
My code:
private void button1_Click(object sender, EventArgs e)
{
Process pro = new Process();
pro.StartInfo.FileName = "notepad";
pro.StartInfo.Arguments = "";
pro.Start();
pro.WaitForExit();
}
private void timer1_Tick(object sender, EventArgs e)
{
DateTime dtime = DateTime.Now;
string date_time = dtime.ToString("dd/MM/yyyy HH:mm:ss");
textBox2.Text = date_time;
}
From Process.WaitForExit:
Instructs the Process component to wait indefinitely for the associated process to exit.
Your timer is trying to invoke timer1_Tick, but your UI Thread is currently stuck waiting for the process to exit, which it wont.
You have two choices to work around this:
Simply remove the call to WaitForExit if you dont really need to wait
If you do need to be notified when the process exits, set Process.EnableRaisingEvents to true and register to the Process.Exited event
The WaitForExit() is "blocking" your interface from refreshing,the call just waits there for the process to exit. As an alternative if you need to do something when the process as exited do this:
private void button1_Click(object sender, EventArgs e)
{
Process pro = new Process();
pro.StartInfo.FileName = "notepad";
pro.StartInfo.Arguments = "";
//if you need to do something when the process exits do this:
pro.EnableRaisingEvents = true;
pro.Exited += new EventHandler(pro_Exited);
pro.Start();
//pro.WaitForExit();
}
void pro_Exited(object sender, EventArgs e)
{
//do what you need here...
}
Instead you could start the process with a BackGroundWorker.
pro.WaitForExit(); makes UI thread to freeze so it can't update.
To stop user from actions, you can disable some controls, while process is running. You can subscribe to process.Exited event and enable your controls, when user closes the process.
private void button1_Click(object sender, EventArgs e)
{
Process pro = new Process();
pro.StartInfo.FileName = "notepad";
pro.StartInfo.Arguments = "";
pro.Start();
button1.Enabled = false;
pro.EnableRaisingEvents = true;
pro.Exited += pro_Exited;
}
void pro_Exited(object sender, EventArgs e)
{
button1.Invoke((MethodInvoker) delegate { button1.Enabled = true; });
}
Update
As another answer suggested you should set EnableRaisingEvents property to true.
Also pro_Exited method will run in a different thread, so you need to use Control.Invoke method to change UI.
Update 2
If can't delete pro.WaitForExit(); you can use another timer, because System.Windows.Forms.Timer is running in UI thread and is blocked with it.
private System.Threading.Timer timer = new System.Threading.Timer(Callback);
public Form()
{
InitializeComponent();
timer.Change(0, 1000);
}
private void Callback(object state)
{
DateTime dtime = DateTime.Now;
string date_time = dtime.ToString("dd/MM/yyyy HH:mm:ss");
button1.Invoke((MethodInvoker)delegate { textBox1.Text = date_time; });
}
It will not update the textBox, when process is opened, but the timer will run and can do some work.
Update 3
In case of multiple processes you can count them and check number of active processes in pro_Exited method.
private volatile int activeProcessCount = 0;
private void pro_Exited(object sender, EventArgs e)
{
activeProcessCount--;
if (activeProcessCount == 0)
{
button1.Invoke((MethodInvoker) delegate { button1.Enabled = true; });
}
}
private void button1_Click(object sender, EventArgs e)
{
//code
activeProcessCount = 2;
pro1.Start();
pro2.Start();
}
This case is using C# WPF. I want to instantly disable a button after clicking it to prevent clicking it twice in short succession. I disabled the button in OnClick_Event but still clickable.
Part of source is as below.
private void Button_Click_UpdateBurndownChart(object sender, RoutedEventArgs e)
{
if(threadNotWorking)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
private void startWorkThread()
{
... ...
//after finish required process
updateButton.IsEnabled = true;
}
Is there any way to accomplish this?
you may want to use a dispatcher, there is probably a threading problem (callback function running on seperate thread and trying to access ui which runs on another thread). try this . .
updateButton.Dispatcher.BeginInvoke(
new ThreadStart(() => updateButton.IsEnabled = false),
System.Windows.Threading.DispatcherPriority.Input, null);
instead of
updateButton.IsEnabled = false;
What happens if you were instead to change the order of your events from:
updateButton.IsEnabled = false;
startWorkThread();
To
startWorkThread();
updateButton.IsEnabled = false;
Let me know how this goes.
What it looks like is that you are starting your thread then immediatly enabling your button before your thread has finished. You would be better off using a BackgroundWorker and enable your Button in the RunWorkerCompleted Event. Though you can do something similar by enabling your button using a BeginInvoke at the end of your Process.
public void doWork()
{
System.Threading.Thread.Sleep(10000); //Simulating your Process
Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate() { updateButton.IsEnabled = true; }), System.Windows.Threading.DispatcherPriority.Background);
}
Example with BackgroundWorker
using System.ComponentModel;
public partial class MainWindow : Window
{
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
updateButton.IsEnabled = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000); //Simulating your work
}
private void startWorkThread()
{
bgw.RunWorkerAsync();
}
private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (bgw.IsBusy != true)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
}