my aim is that in the function "Dummy" i can change the controls like labels etc of the form from which the thread is initiating..how to do it..please don't suggest completely different strategies or making a worker class etc...modify this if you can
Thread pt= new Thread(new ParameterizedThreadStart(Dummy2));
private void button1_Click(object sender, EventArgs e)
{
pt = new Thread(new ParameterizedThreadStart(Dummy2));
pt.IsBackground = true;
pt.Start( this );
}
public static void Dummy(........)
{
/*
what i want to do here is to access the controls on my form form where the
tread was initiated and change them directly
*/
}
private void button2_Click(object sender, EventArgs e)
{
if (t.IsAlive)
label1.Text = "Running";
else
label1.Text = "Dead";
}
private void button3_Click(object sender, EventArgs e)
{
pt.Abort();
}
}
}
what i plan is that i could do this in the "Dummy" function
Dummy( object p)
{
p.label1.Text = " New Text " ;
}
You could do this, supposing you're passing an instance of the form to the thread method using the t.Start(...) method:
private void Form_Shown(object sender)
{
Thread t = new Thread(new ParameterizedThreadStart(Dummy));
t.Start(this);
}
....
private static void Dummy(object state)
{
MyForm f = (MyForm)state;
f.Invoke((MethodInvoker)delegate()
{
f.label1.Text = " New Text ";
});
}
EDIT
Added thread start code for clarity.
You can't do this. You can only access a UI control on the same thread that created it.
See the System.Windows.Forms.Control.Invoke Method and the Control.InvokeRequired property.
Can use something like this:
private void UpdateText(string text)
{
// Check for cross thread violation, and deal with it if necessary
if (InvokeRequired)
{
Invoke(new Action<string>(UpdateText), new[] {text});
return;
}
// What the update of the UI
label.Text = text;
}
public static void Dummy(........)
{
UpdateText("New text");
}
Related
I have a Windows Form application and managed DLL in one solution. DLL contains some time consuming functions during which I wish to update the Form contents (callback from the DLL to the Form with progess updates). I have the following code:
Form code, where I initialize the DLL and give it a callback function in the Initialize method. I also start a separate Thread to periodicly check the message_queue for new messages from the DLL. The DLL function is also called in a separate Thread (non blocking for the UI).
private LibraryDLL library_dll;
private ConcurrentQueue<string> message_queue;
public MainForm()
{
InitializeComponent();
library_dll = new LibraryDLL();
message_queue = new ConcurrentQueue<string>();
library_dll.Initialize(ProcessMessage);
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
string message;
if (message_queue.TryDequeue(out message))
{
PrintMessage(message);
}
}).Start();
}
private void ProcessMessage(string message)
{
message_queue.Enqueue(message);
}
private void PrintMessage(string message)
{
this.Invoke((MethodInvoker)delegate
{
listBox_rows.Items.Add(message);
});
}
private void button_send_Click(object sender, EventArgs e)
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
library_dll.DoWork();
}).Start();
}
In DLL code, I use the callback method to report progress:
private CallBack callback;
public delegate void CallBack(string message);
public LibraryDLL() { }
public void Initialize(CallBack callback)
{
this.callback = callback;
}
public void DoWork()
{
callback("working...")
Thread.Sleep(500);
callback("working...")
Thread.Sleep(500);
callback("working...")
Thread.Sleep(500);
}
My problem is, that instead of string "working" appearing every 500ms, it appears 3 times after 1500ms (only after the Thread in which the DoWork method is running ends). I also tried the Invalidate()-Update()-Refresh() sequence in the Form's PrintMessage function, but without any effect.
Thanks for the advice!
EDIT1:
I modified the code to use the BackgroundWorker, however, the problem remains (nothing for 1500ms, than all 3 strings at once).
BackgroundWorker bck_worker;
public MainForm()
{
InitializeComponent();
library_dll = new LibraryDLL();
library_dll.Initialize(bck_worker);
bck_worker = new BackgroundWorker();
bck_worker.ProgressChanged += new ProgressChangedEventHandler(bckWorker_ProgressChanged);
bck_worker.WorkerReportsProgress = true;
bck_worker.WorkerSupportsCancellation = true;
}
private void bckWorker_DoWork(object sender, DoWorkEventArgs e)
{
library_dll.DoWork();
}
private void bckWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
PrintMessage((string)e.UserState);
}
private void button_send_Click(object sender, EventArgs e)
{
bck_worker.DoWork += new DoWorkEventHandler(bckWorker_DoWork);
bck_worker.RunWorkerAsync();
}
private void PrintMessage(string message)
{
listBox_rows.Items.Add(message);
}
And the DLL:
private BackgroundWorker bck_worker;
public LibraryDLL() { }
public void Initialize(BackgroundWorker bck_worker)
{
this.bck_worker = bck_worker;
}
public void DoWork()
{
bck_worker.ReportProgress(25, "working...");
Thread.Sleep(500);
bck_worker.ReportProgress(50, "working...");
Thread.Sleep(500);
bck_worker.ReportProgress(75, "working...");
Thread.Sleep(500);
}
EDIT2:
OK, I now tried to add the Invalidate-Update-Refresh sequence at the end of the PrintMessage function and it finaly works (with the BackgroundWorker approach)!
Use background worker and workers's report progress to update your UI: background worker doc
I have several textboxes in my wpf application. The LostFocus-Event of each textbox starts a backgroundworker to send the data to a connected serial port.
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
At this point, everything is working fine.
But sometimes I have to send two data-points directly after each other and there I have a problem.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
The Backgroundworker is still running and I get an exception thrown.
Is it possible to wait after the first online_mode_send_worker.RunWorkerAsync(data) until it has finished and then start the second online_mode_send_worker.RunWorkerAsync(data)?
while(online_mode_send_worker.isBusy); is not working because the main-thread is blocking and the RunWorkerCompleted() is not thrown and so the Backgroundwoker is always busy.
I have found something like this, but Application.DoEvents() is not available in wpf.
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
Here is a rough idea of what I mentioned in the comments.
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
You have a queue to hold on to messages that were sent while the background worker was busy and have the worker check the queue for any pending messages when it has completed doing its work.
The messenger can be used like this.
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
It seems that I missed the serial stuff. So what you want to do is synchronize your asynchronuouscalls:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
also see:
https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
ORIGINAL ANSWER
You can use Task instead of Backgroundworker.
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
I also would like to suggest that you make real objects instead of passing string arrays as arguments.
For Example send BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
and a corresponding method:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
I think your should use RunWorkerCompleted event and add a delegate:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
Make sure you put there a condition to avoid infinite loop.
I'd say that if you MUST wait until after the first "job" is done, that what you want is Task.ContinueWith() and change your interface accordingly. The msdn page is good for it IMO, but watch out that you're waiting on the "correct" task object. Hint: it's the return value of ContinueWith() that you should call Wait() on. This is a good pattern to do for launching a Task and then waiting for it later as long as you can keep the Task that is returned so you can wait on it.
For a more generic "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding." I would suggest using a BlockingCollection<Action> with only one thread consuming them. An example of how to do that is found in this other answer.
Update:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
Is not good aproach, because UI will be blocked on time of waiting. Better:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
Original answer:
I advice not to use construction: while (bw.Busy) { ... } (it consumes cpu time), use synchronization objects, for example, ManualResetEvent
BackgroundWorker is great class, but does not support waiting. Just create addition object for waiting:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
If you need only call twice you can do this:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
But if you need to queue commands you need rewrite in another way Using Task.
One Task where inside it you will have a for-loop where you will send your data through serial port sequentially.
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx
While using timers, stopwatches and threads is the standard way, I was wondering if there was a way to create a Winform Application in c# which had a label with initial value as 0 and which automatically kept on incrementing once a button is clicked and when the same button is clicked again it should pause. Personally, I feel that the trick is to use multicast delegates. But I am stuck as to how to proceed.
NOTE: Possible use of method callback and InvokeRequired().
this code dose not use timer or stopwatch.
i have wrote a simple class for you, forgive me if its not so standard because im so lazy for now :)
public partial class Form1 : Form
{
CancellationTokenSource src;
CancellationToken t;
public Form1()
{
InitializeComponent();
}
//start incrementing
private async void button1_Click(object sender, EventArgs e)
{
this.Start.Enabled = false;
this.Cancel.Enabled = true;
this.src = new CancellationTokenSource();
this.t = this.src.Token;
try
{
while (true)
{
var tsk = Task.Factory.StartNew<int>(() =>
{
Task.Delay(500);
var txt = int.Parse(this.Display.Text) + 1;
return (txt);
}, this.t);
var result = await tsk;
this.Display.Text = result.ToString();
}
}
catch (Exception ex)
{
return;
}
}
// Stop incrementing
private void button1_Click_1(object sender, EventArgs e)
{
this.src.Cancel();
this.Cancel.Enabled = true;
this.Start.Enabled = true;
}
}
Really not sure why you think this can be done with your restrictions in place. If you want a delay in-between your "events", then you need to use some kind of Timer, or some kind of thread (classic Thread or some kind of Task) that has a delay within it...no way around that.
Here's another approach that'll probably violate your restrictions:
public partial class Form1 : Form
{
private Int64 value = -1;
private bool Paused = true;
private int IntervalInMilliseconds = 100;
private System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
public Form1()
{
InitializeComponent();
this.Shown += Form1_Shown;
}
private async void Form1_Shown(object sender, EventArgs e)
{
await Task.Run(delegate ()
{
while (true)
{
value++;
label1.Invoke((MethodInvoker)delegate ()
{
label1.Text = value.ToString();
});
System.Threading.Thread.Sleep(IntervalInMilliseconds);
mre.WaitOne();
}
});
}
private void button1_Click(object sender, EventArgs e)
{
if (Paused)
{
mre.Set();
}
else
{
mre.Reset();
}
Paused = !Paused;
}
}
USE an EVENT.
If you can not use timers or threads, then how about creating a do while loop that executes an event.
Some PSEUDO code is below - it should give you the idea..
bool IWantEvents = false;
public event EventHandler<myHandler> myNonTimerEvent ;
FormStart()
{
this.myNonTimerEvent += new MyNonTimerEventHandler();
IWantEvents = true;
Do
{
.. do some weird stuff - set IWantEvents False on condition ..
}
while(IWantEvents)
}
MyNonTimerEventHandler()
{
.. Do what I would do if I was using a timer event.
}
My form is going to run some code that might take a while to execute. I would like to display a "please wait" message while the operation is running on the background.
I'd like to have that message in a form, one that I can control its visibility, and also its text, from other forms.
I'd also like it to be set to start in the Program.cs file.
My code, so far:
namespace KAN
{
public partial class prosze_czekac : Form
{
public prosze_czekac()
{
InitializeComponent();
}
private delegate void OffVisible();
public void Wylacz()
{
if (this.InvokeRequired)
this.Invoke(new OffVisible(Wylacz));
else
this.Visible = false;
}
delegate void SetTextCallback(string text);
public void ZmienTekst(string text)
{
if (this.InvokeRequired)
{
//SetTextCallback d = new SetTextCallback(this.ZmienTekst);
Invoke(new SetTextCallback(this.ZmienTekst), text);
//Invoke(d, new object[] { text });
}
else
{
this.Visible = true;
this.Text = text;
this.lblKomunikat.Text = text;
this.Update();
}
}
}
}
I do not know how to run a form, how to create an instance and as editing text. All this in any form, any thread.
Is the above code is correct and how to use it to make it properly?
How am I so ready form "please wait" I would like to turn it on now in the initial class (Program.cs). Use it in any form design.
Sample code, do not know if correct:
namespace KAN
{
static class Program
{
public static prosze_czekac PleaseWait;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread thread = new Thread(new ThreadStart(PleaseWait.Show());
PleaseWait.ZmienTekst("Please wait... Running the program");
// long operation
PleaseWait.Wylacz();
Application.Run(new main());
}
}
}
namespace KAN
{
public partial class main: Form
{
public main()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// examples of long task in another form
for (int i = 0; i < 5; i++)
{
Program.PleaseWait.ZmienTekst((i + 1).ToString());
System.Threading.Thread.Sleep(1000);
}
Program.PleaseWait.Wylacz();
}
}
}
The first time I ask a question, please bear with me.
PS
"Wylacz" is "exit" (void) and is meant to "hide" so that every time you do not initiate the form.
"prosze_czekac" is "please wait".
Use the BackgroundWorker. The following code assumes, you have a button 'button1' in your form, which executes the worker, which starts the long running task on a different thread:
BackgroundWorker _worker;
// button click starts the execution of the lung running task on another thread
private void button1_Click(object sender, EventArgs e)
{
label1.Visible = true; // show the label "please wait"
_worker.RunWorkerAsync();
}
private void Form1_Load(object sender, EventArgs e)
{
// initialize worker
_worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(worker_DoWork);
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
}
// executes when long running task has finished
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// hide the label
label1.Visible = false;
}
// is called by 'RunWorkerAsync' and executes the long running task on a different thread
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// long running task (just an example)
for (int i = 0; i < 1000000000; i++)
{
}
}
How do I control when a thread is permitted to access an object and when it is not.
For example, if I have situation like below, I want to make sure that when I am doing something with objFoo in my ButtonClick event, I should not be able to touch objFoo from my doSomethingWithObjFoo method.
private void button1_Click(object sender, EventArgs e) {
// doing something with objFoo
}
private void timer1_Tick(object sender, EventArgs e) {
Thread T = new Thread(new ThreadStart(doSomethingWithObjFoo));
T.Start();
}
private void doSomethingWithObjFoo(){
// doing something else with objFoo
}
The easiest way is perhaps to use lock:
private object _fooLock = new object();
private void button1_Click(object sender, EventArgs e) {
lock(_fooLock)
{
// doing something with objFoo
}
}
private void timer1_Tick(object sender, EventArgs e) {
Thread T = new Thread(new ThreadStart(doSomethingWithObjFoo));
T.Start();
}
private void doSomethingWithObjFoo(){
lock(_fooLock)
{
// doing something else with objFoo
}
}
There are other options as well, such as using a ReaderWriterLockSlim.
That what we use lock for.
Thread Synchronization is a must read.
public class TestThreading
{
private System.Object lockThis = new System.Object();
public void Process()
{
lock (lockThis)
{
// Access thread-sensitive resources.
}
}
}