How to pass custom EventArgs to UI controls - c#

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?

Related

Background worker on login page

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;
}

Background Worker, C# - Objects sharing between workers - Creating issues

I'm facing an serious issue where data from one record is copied to another record (Overlay). I'm using MQ request for communicating to Mainframe systems using my C# code. we are facing issue which is very random/rarer where sending update request to Mainframe for one record copy information of another record previously processed by that thread. I'm using below code Background worker approach to create multi-threading on my servers.
My Question here is : Can objects created by one worker being used by another work ? is that possible ? this may be one of reason of overlay data.
Please help with you suggestion !!
BackgroundWorker worker;
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync();
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
struct SOmeData { }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
SOmeData data = new SOmeData();
// backgroundWorker1 result
e.Result = data;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// backgroundWorker1 result
SOmeData data = (SOmeData)e.Result;
// start backgroundWorker2
backgroundWorker2.RunWorkerAsync(data);
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
// backgroundWorker1 result
SOmeData data = (SOmeData)e.Argument;
}
}

Using Backgroundworker with a Timer

I've looked in many places for this but still haven't found a solution. What I'm trying to achieve is being able to use BackgroundWorker on a timed basis. Here's an example:
public Main()
{
isDbAvail = new BackgroundWorker();
isDbAvail.DoWork += isOnline;
isDbAvail.RunWorkerCompleted += rewriteOnlineStatus;
}
private void rewriteOnlineStatus(object sender, RunWorkerCompletedEventArgs e)
{
Subs.Connection connection = new Subs.Connection();
changeStatus(connection.isDbAvail());
}
private void isOnline(object sender, DoWorkEventArgs e)
{
while (true)
{
Console.WriteLine("Checking database connection");
System.Threading.Thread.Sleep(8000);
}
}
public void changeStatus(bool status)
{
if (status)
{
serverStatusVal.Text = "Connected";
serverStatusVal.ForeColor = System.Drawing.Color.DarkGreen;
}
else
{
serverStatusVal.Text = "Not connected";
serverStatusVal.ForeColor = System.Drawing.Color.Red;
}
}
What's happening here is that the isOnline method checks if there is a connection to the database (just an example) every 8 seconds and changes the text accordingly. What I've noticed though, is that the while loop inside the isOnline method causes the rewriteOnlineStatus method never to fire because it runs indefinitely. Is there another workaround to this?
I suggest you use BackgroundWorker.ReportProgress, and check connectivity in the background thread.
Something like this:
public Main()
{
isDbAvail = new BackgroundWorker();
isDbAvail.WorkerReportsProgress = true;
isDbAvail.DoWork += isOnline;
isDbAvail.ProgressChanged += rewriteOnlineStatus;
isDbAvail.RunWorkerAsync();
}
private void rewriteOnlineStatus(object sender, ProgressChangedEventArgs e)
{
changeStatus((bool)e.UserState);
}
private void isOnline(object sender, DoWorkEventArgs e)
{
while (true)
{
Console.WriteLine("Checking database connection");
Subs.Connection connection = new Subs.Connection();
isDbAvail.ReportProgress(0, connection.isDbAvail);
System.Threading.Thread.Sleep(8000);
}
}
Now the BackgroundWorker is doing the work, and reporting back to the UI thread via ProgressChanged.

Why is a disabled button clickable?

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();
}
}
}

UI hangs when long search loop executed in c#

in my Winform a read file method is implemented on button click.when big files are read my Ui hangs until the loop is over.I need to have control on my UI all the time.
i know that is done before and i tried some but i am still having an
error when i try to use some form controls like this :(translated!)
the access of control element comboBox1 is from another thread rather than the thread in which it is created in !!!
private void button1_Click(object sender, EventArgs e)
{
//some code
using (StreamReader sr = new StreamReader(file, System.Text.Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
UpdateProgressBar(line.Length);
}
}
//some code
}
Add a BackgroundWorker to your class on Form (or Control) load. Then hookup its events:
BackgroundWorker _worker;
void Form_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
_worker.ProgressChanged +=_worker_ProgressChanged;
_worker.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
_worker.RunWorkerAsync(file);//pass on the file name
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
var file = e.Argument as String;
using (StreamReader sr = new StreamReader(file, Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
_worker.ReportProgress(line.Length);
}
}
}
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Report porogress bar change
UpdateProgressBar(e.ProgressPercentage);
}
private void _worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//do any stuff you want after reading the file.
}
Read more about this here.
Use a BackgroundWorker to do the work on a separate thread.
Use BackgroundWorker
[edit] Tutorial available here
You really should have consulted the documentation before asking a question. Anyways, here's an example of how you can do it using a BackgroundWorker:
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
worker.DoWork += ReadStream;
worker.RunWorkerAsync(comboBox1.Text);
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateProgressBar(e.ProgressPercentage);
comboBox1.Text = e.UserState.ToString();
}
private void ReadStream(object sender, DoWorkEventArgs doWorkEventArgs)
{
BackgroundWorker worker = sender as BackgroundWorker;
string line;
string comboBoxText = doWorkEventArgs.Argument.ToString();
using (StreamReader sr = new StreamReader("file", System.Text.Encoding.ASCII))
{
while (!sr.EndOfStream)
{
line = sr.ReadLine();
worker.ReportProgress(line.Length);
worker.ReportProgress(line.Length, "NEW COMBOBOX TEXT");
}
}
}
use backgroundworker to carry out heavy operations.
it has event to report progress of the task , so you can use it to update progress bar.

Categories