It doesn't show me anything inside the logBox, it just stays blank
namespace Clipboard_Logger
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
logBox.SelectionStart = logBox.TextLength;
logBox.ScrollToCaret();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (Clipboard.ContainsText(TextDataFormat.Text))
logBox.Text = logBox.Text + Clipboard.GetText(TextDataFormat.Text) + "\r\n";
}
}
}
}
You are using a background thread ( BackGroundWorker.DoWork ) to access controls on the UI thread. Controls can only be accessed from the UI thread.
Try adding a BackGroundWorker.ProgressChanged event and access your control from that. ProgressChanged runs from the UI thread.
Edit from your comment:
No, that's not what I meant, your're creating a new backgroundworker, you should use the existing one, like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ReportProgress(1);
}
Also, you need to copy text to the clipboard.
Related
I want to change the text in a textbox of a Windows Form with different error messages. These error messages are set to an output string using the same method, but i can't pass the string as a parameter.
Here's how i call a new backgroundworker to safely change the text in the textbox:
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync(argument: error));
Then i try to invoke the call:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
string output = e.Argument.ToString();
object[] par = new object[] { output };
Delegate del = new DELEGATE(changeErrortext);
this.Invoke(del,par);
}
private void changeErrorText()
{
textBoxError.Text = output.ToString();
}
I think i've got to assign the output in the object to the one in the changeErrorText, but i don't really know how to do it.
I've tried different methods, but none have worked. I'm new to C#, so tell me if and where i've messed up.
Instantiate the necessary event handlers
Place eventhandlers in the constructor of the form. For example:
public Form1()
{
InitializeComponent(); // Standard initialization component method...
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
Using the DoWork AND the RunWorkerCompleted events
The way you used 'worker.DoWork += worker_DoWork;' should be expanded with the "RunWorkerCompleted" event. The DoWork event for the operation and the RunWorkerCompleted to handle the completed operation. This event is also usefull when you want to use cancellation or exception handling. This also gets called automatically, so there is no need for a dedicated "changeErrorText"-method. Example code:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString();
}
You don't have to use a Delegate to pass parameters
The parameters can be passed the way you wrote the RunWorkAsync. The only thing you might want to do is placing that method in a (click) event. For example:
private void button1_Click(object sender, EventArgs e)
{
int value = 123; // example input
backgroundWorker1.RunWorkerAsync(argument: value);
}
The full code doc:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
private void button1_Click(object sender, EventArgs e)
{
int value = 123;
backgroundWorker1.RunWorkerAsync(argument: value);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string output = e.Argument.ToString();
e.Result = output;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString();
}
}
I hope i've helped you with the above explanations.
Sources
Sending Arguments To Background Worker?;
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netcore-3.1
On .NET Windows form, I have Background worker component that works fine. I have 5 forms, that has basically same Background worker on it with same code.
Can I extract this code to other class and somehow use it, considering this is an event? This is code I have on form. It takes 20 lines of code, and it would be nice if this can be refactored. Note: as you can see, I have already put it to other class BackgroundWorkerHelper, but can I also somehow refactor this events on Background worker, so that it is in other class as well, this way code is less and reused.
private void RunBackgroundWorker(string infoLabelText, int imageIndex)
{
BackgroundWorkerHelper.Run(backgroundWorker, progressBar, infoLabelText, imageIndex);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorkerHelper.DoWork(backgroundWorker);
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BackgroundWorkerHelper.ProgressChanged(sender, e, progressBar);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BackgroundWorkerHelper.RunWorkerCompleted(sender, e, progressBar);
}
Note: for now I would like to avoid using user control. I know I could do it, but then you have code that handles placing user control and so on. I am still not very good in it.
Here is solution, thanks to rory who gave me idea how to do it. First, I made this class:
public class BackgroundWorkerHelper
{
private static string _infoLabelText = string.Empty;
public BackgroundWorker _BackgroundWorker;
private BarEditItem _marqueeInfo;//this is marquee progress bar
public BackgroundWorkerHelper(BarEditItem marqueeInfo)
{
_marqueeInfo = marqueeInfo;
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.WorkerSupportsCancellation = true;
_BackgroundWorker.DoWork += backgroundWorker_DoWork;
_BackgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
_BackgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}
public void Run(string labelText, int imageIndex)
{
_marqueeInfo.Caption = labelText;
_marqueeInfo.ImageIndex = imageIndex;
if (!_BackgroundWorker.IsBusy)
_BackgroundWorker.RunWorkerAsync();
else
_marqueeInfo.Caption = "Busy processing saving data, please wait...";
}
public void DoWork()
{
for (int i = 0; i <= 5; i++)
{
_BackgroundWorker.ReportProgress(i); // call backgroundWorker_ProgressChanged event and pass i (which is e argument e.ProgressPercentage) to update UI controls
Thread.Sleep(250);
}
}
public void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_marqueeInfo.Visibility = BarItemVisibility.Always;
}
public void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_marqueeInfo.Visibility = BarItemVisibility.Never;
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DoWork();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressChanged(sender, e);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RunWorkerCompleted(sender, e);
}
then in FORM, in class level above constructor place
private readonly BackgroundWorkerHelper _backgroundWorkerHelper;
then in Form Constructor instantiate class
_backgroundWorkerHelper = new BackgroundWorkerHelper(marqueeInfo);
and then I just call it in my form
_backgroundWorkerHelper.Run("Saving", 14);
I have a Form where one can Sign In and it could take a while till the data gets loaded into the Form. So I wanted to create a seperate Form (loadScreen.cs) with a Progress Bar when the Form is loading. I tried this in the loadScreen.cs Form:
private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
percentFinished++;
worker.ReportProgress(percentFinished);
System.Threading.Thread.Sleep(50);
}
e.Result = percentFinished;
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}
I've read that the worker_DoWork method should have the code which takes longer to load. I don't know how to handle this since my button is in Form1. When it's clicked then I go to another class with
private void signIn_Click(object sender, EventArgs e)
{
var logIn = new LogIn(this);
logIn.checkUserInput(this);
}
and there I execute the operations which load certain things. How to connect everything? I need help!
I'm actually in the process of creating a general-purpose dialogue for this sort of thing. It's not going to be ready in time to be of use to you but I would suggest that you go along similar lines. Create your "Loading" dialogue so that it accepts a delegate and invokes it in the DoWork event handler. The main form can then contain a method that does the work and you can pass a delegate for that method to the dialogue. I'll post a very basic example.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private DataTable table;
private void button1_Click(object sender, EventArgs e)
{
var work = new Action(GetData);
using (var f2 = new Form2(work))
{
f2.ShowDialog();
this.dataGridView1.DataSource = this.table;
}
}
private void GetData()
{
this.table = new DataTable();
using (var adapter = new SqlDataAdapter("SELECT * FROM MyTable", "connectionstring here"))
{
adapter.Fill(table);
}
}
}
public partial class Form2 : Form
{
private Action work;
public Form2(Action work)
{
InitializeComponent();
this.work = work;
}
private void Form2_Load(object sender, EventArgs e)
{
this.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
this.work();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.DialogResult = DialogResult.OK;
}
}
Note that there's no real way to measure progress when using a data adapter so you could only really display a marquee progress bar in this case.
It's a C# winform .Net framework 2.0 project: The time-consuming network transferring jobs run in a BackupgroundWorker. In this backgroundwork thread, SynchronizationContext method is used to send the current job title to a message dialog in the main UI thread. However, in my code, thread racing problem happens and "NullReferenceException" is given sometime (not always, but randomly). Here is my code:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork +=new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
m_msgForm.ShowDialog(); //show as a modal dialog
}
And in the Background worker thread:
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//... some code
string msgText ="doing job: " +job.Title;
RestoreMsgForm.synContext.Send(m_msgForm.UpdateMsg, msgText); //send message to m_msgForm in the UI thread
//... some code
}
The problem is : sometime the "RestoreMsgForm.synContext.Send() in the backgroundworker thread will run before m_msgForm.ShowDialog() in the UI thead.
And in this case, NullReferenceException will be throw.
How to solve this problem? thanks.
the definition of RestoreMsgForm is
public partial class RestoreMsgForm : Form
{
public static SynchronizationContext synContext;
public RestoreMsgForm()
{
InitializeComponent();
}
private void RestoreMsgForm_Load(object sender, EventArgs e)
{
synContext = SynchronizationContext.Current;
}
public void UpdateMsg(object msg)
{
msgLabel.Text = (string)msg;
}
}
See if it works like this instead:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork += new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.WorkerReportsProgress = true;
myBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(myBackgroundWorker_ProgressChanged);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
}
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
worker.ReportProgress(-1);
//... some code
string msgText = "doing job: " + job.Title;
worker.ReportProgress(0, msgText);
//... some code
worker.ReportProgress(0, "...other text...");
//... some code
worker.ReportProgress(0, "...etc...");
}
void myBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == -1)
{
m_msgForm.ShowDialog(); //show as a modal dialog
}
else
{
m_msgForm.UpdateMsg(e.UserState.ToString);
}
}
*Note that we have to set .WorkerReportsProgress = true for the BackgroundWorker() so we can use ReportProgress() and receive the ProgressChanged() event.
I've run into some problems while using progress bars in Windows Forms. Say I have an algorithm with ten parts that runs on a button click. After each part, I'd want to update a progress bar on the form to 10% further along. However, when code is running, the Windows Form will not respond or update.
What is the correct do show progress on a form while code is running?
You need to use a BackgroundWorker.
A nice example can be found here: http://www.dotnetperls.com/progressbar
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
// Start the BackgroundWorker.
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
// Wait 100 milliseconds.
Thread.Sleep(100);
// Report progress.
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
}
}
Or you can use something like:
private void StartButtonClick(object sender, EventArgs e)
{
var t1 = new Thread(() => ProgressBar(value));
t1.Start();
}
private void ProgressBar(value1)
{
ProgressBar.BeginInvoke(new MethodInvoker(delegate
{
ProgresBar.Value++
}));
}
I suggest use TPL for such operations as more standardized, lightweight, robust and extendable
See for ex.: http://blogs.msdn.com/b/pfxteam/archive/2010/10/15/10076552.aspx