I'm trying to workout how to call chart1.Series[0].Points.AddXY(1, 4); from another thread.
I have been trying to adapt the examples which all show setting a text property using a delegate but I can't get them to work with the chart control.
could someone please help me?
delegate void SetTextCallback(string text);//assume text is the value and not the Text property
private void chartRefresh()
{
while (true)
{
//code to refresh chart
for (int i = 0; i < 30; i++)
{
if (this.chart1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.chart1.Series[0].Points.AddXY(i, i + 2);
}
chart1.Series[0].Points.AddXY(i, i + 2);
Thread.Sleep(500);
}
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.chart1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.chart1.Series[0].Points.AddXY(1, 4);
}
}
while (true)
{
for (int i = 0; i < 20000; i++)
{
Temp ++;
chart1 .Invoke((MethodInvoker)delegate
{
// Running on the UI thread
Series.Points.AddXY(i, random.Next(0,100));
});
}
You may find interesting the modern approach, using the Progress class. You can instantiate such an object in the UI thread, supplying the lambda function that will update the chart:
private IProgress<int> _chartProgress
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_chartProgress = new Progress<int>(i =>
{
this.chart1.Series[0].Points.AddXY(i, i + 2);
});
}
Then pass this object somehow to the background worker, and invoke its Report method any time you want the chart to be updated.
private void ChartRefresh(IProgress<int> progress)
{
while (true)
{
//code to refresh chart
for (int i = 0; i < 30; i++)
{
progress.Report(i);
Thread.Sleep(500);
}
}
}
The lambda will always run in the UI thread, because the Progress object was created in that thread.
The advantage of this technique is that it allows decoupling the UI-related stuff from the background-related work. It was invented primarily for facilitating reporting progress from asynchronous operations, but it can be useful in multithreading too.
Related
I have looked around the internet and found a nice solution which I am incorporating into my code below however it doesn't quite do exactly what I want, it works when just calling an update but I want to run a method in another class then let that method call the method that will report back to the UI and just pass some information so this mock up is just changing the button content before the operation is ran.
Using a Dispatcher I can get a UI control to update however I don't just wish to do that I want to perform some functions then have the UI Update.
So there maybe some theory I am not getting, I know the Invoke is a synchronous operation and breaking through the code it does run but the UI doesn't update.
MainWindow
Has a single button with content "CLICK ME"
Code Behind
public partial class MainWindow : Window
{
public static Button windowButton;
public MainWindow()
{
InitializeComponent();
windowButton = btnStart;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
// Run a Process in another class on a different Thread
ProcessClass processClass = new ProcessClass();
Task processTask = new Task(() =>
{
processClass.DoSomething();
});
processTask.Start();
}
}
}
ProcessClass
class ProcessClass:MainWindow
{
public static void UpdateUI()
{
App.Current.Dispatcher.Invoke(delegate
{
windowButton.Content = "CHANGED CONTENT";
});
}
public void DoSomething()
{
UpdateUI();
int counter = 0;
for(int i = 1; i < 100; i++)
{
counter += i;
Thread.Sleep(100);
}
MessageBox.Show($"Task Completed, answer is {counter}");
}
}
Assuming that ProcessClass is your own code that you can update, change the signiture of DoDomething() to
public async Task DoSomething(IProgress<string> progress)
{
progress.Report("Begin DoSomething()");
var counter = 0;
for(var i = 1; i < 100; i++)
{
counter += i;
await Task.Delay(100).ConfigureAwait(false);
progress.Report($"DoSomething() - i = {i}");
}
progress.Report($"DoSomething() Completed, answer is {counter}");
}
Now your button click handler can be written
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
// usually you would update some other control such as a TextBlock
// for the feedback, rather than the button content
var progress = new Progress<string>(s => btnStart.Content = s);
ProcessClass processClass = new ProcessClass();
await processClass.DoSomething(progress).ConfigureAwait(false);
}
Hi,guys, I don't understand how to handle the UI thread, so I try to create many UI elements in a child thread, but,
How to modify the following code so that the window don't block:
public void CreateCtr(string name)
{
Button btn = new Button();
btn.Content = name;
btn.Width = 10;
btn.Height = 10;
content.Children.Add(btn);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int count = 100000;
Task.Factory.StartNew(()=> {
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(()=> {
CreateCtr(i.ToString());
}));
}
});
}
You need to give the UI thread some space to breathe. With the current code, the UI thread is busy processing all the create button tasks and cannot get to processing user input. Put a minimal pause between creating each individual button. A few milliseconds are enough:
private void Button_Click(object sender, RoutedEventArgs e)
{
int count = 100000;
Task.Factory.StartNew(() => {
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(() => {
CreateCtr(i.ToString());
}));
Thread.Sleep(5);
}
});
}
The longer the pause, the more responsive the form becomes.
You will note that the form becomes less responsive again after some time, because after a certain amount of components on a form, adding another component takes very long. This is where my original comment becomes relevant: There is no usecase for having that many components on a form, so the framework is not designed to handle it well.
What might work is not putting all buttons on the same parent container but creating a tree of components where each component has no more than say 100 children. But again: I don't see any relevant usecase.
Might work to disable the dispatcher when adding the controls.
int count = 100000;
Task.Factory.StartNew(() => {
using (var d = Dispatcher.DisableProcessing())
{
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(()=> {
CreateCtr(i.ToString());
}));
}
}
});
i copied the following example Microsoft Thread Example
Which gives the code below
but i get an error on the line "this.progressBar1.Value = newval;" stating that "Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on."
what could be the issue?
thanks
damo
C# Code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("This is the main thread");
}
}
First of all I strongly suggested to use some higher level techniques like Tasks rather than using Thread class directly. Task classes not only easier to use, they're more effective, easier to compose and easier to avoid such issues that you faced recently.
The main issue with your code that you trying to update UI objects from non-UI threads. UI technologies (like Windows Forms or WPF) requires that only thread that creates an UI object will access to their properties.
To fix this you should marshal control from non-UI thread to UI thread. And there is a plenty of options to do this (but all of them only a syntactic sugar around concept called SynchronizationContext):
Use synchronization context directly:
.
// storing SynchronizationContext in the private field of your form
private SynchronizationContext _syncContext = SyncrhonizationContext.Current;
private void MethodFromTheSeparateThread()
{
// Marshaling control to UI thread
_syncContext.Post(d =>
{
// Put all your code that access UI elements here
}, null);
}
Use InvokeRequired/Invoke as Gregor mentioned already
Use TaskScheduler.FromSynchronizationContext
.
private void ImplementLongRunningOperation()
{
int id;
string name;
Task.Factory.StartNew(() =>
{
// our long-runing part. Getting id and name
id = 42;
name = "Jonh Doe";
}).ContinueWith(t =>
{
// Handling results from the previous task.
// This callback would be called in UI thread!
label1.Text = id.ToString();
label2.Text = name;
}, TaskScheduler.FromSynchronizationContext);
}
As I mentioned, last approach (using Tasks) is a preferable way if you're working on .NET 4.0+. This not only saves you from some low-level classes but also lead to more clear design because you can clearly separate separate steps like getting the data and processing them.
You have to invoke new delegate:
delegate void ThreadTaskDelegate();
private void ThreadTask()
{
if (this.InvokeRequired)
{
ThreadTaskDelegate del = new ThreadTaskDelegate(ThreadTask);
this.Invoke(del, null);
}
else
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
}
Happy coding! :)
The example is a poor one. You must access controls in the thread in which they are created. This is almost always the main UI thread. (It is possible to have separate UI threads for different forms each with their own message pumps. But don't worry about that right now.)
The background thread must use Control.Invoke(Delegate) to change to the main UI thread before accessing Controls. Then, when the UI work is done, get out of the UI thread as soon as possible.
For example:
private void ThreadTask()
{
// This code runs in the background thread.
while (true)
{
if (this.InvokeRequired)
{
// In order to access the UI controls, we must Invoke back to the UI thread
this.Invoke(new ThreadStart(SetRandomProgress));
}
else
{
// We are already in the UI thread, so we don't have to Invoke
SetRandomProgress();
}
// Wait briefly. This wait happens in the background thread.
// During this time, the UI is still responsive, because it is not blocked.
// You can verify this by tweaking the duration to something longer (say, 5000 ms).
Thread.Sleep(100);
}
}
private void SetRandomProgress()
{
Random rnd = new Random();
int stp = this.progressBar1.Step * rnd.Next(-1, 2);
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
You could rewrite your code like this, your progressBar will be updated in the UI thread, Invoke a method that does access to the progressBar through delegate. Check the code:
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
Random rnd = new Random();
while (true)
{
int randValue = rnd.Next(-1, 2);
progressBar1.Invoke(new updater(UpdateProgressBar), new object[] {randValue});
Thread.Sleep(100);
}
}
private delegate void updater(int value);
private void UpdateProgressBar(int randValue)
{
int stp = this.progressBar1.Step * randValue;
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
I am trying my hardest to learn Cross/Multi Threading, but I am very confused on the concept.
I made a sample application which is suppose to display i on a label.text via a thread.
it's not working because I am trying to access a thread other than it was created on, I've researched a lot and I am still confused on Invoking, Delegation, etc...
Here is my code:
private void s1_Click(object sender, EventArgs e)
{
Thread Thread1 = new Thread(new ThreadStart(Start1));
Thread1.Start();
}
public void Start1()
{
for (int i = 0; i < 1000; i++)
{
displaytext("Working.........", i);
Thread.Sleep(100);
}
}
public void displaytext(string thetext, int number)
{
t1.Text = thetext + " " + number;
}
What is a good way to get this working ?
Any help is greatly appreciated.
I am learning this for the love of programming.
I am trying to access a thread other than it was created on
The actual error is accessing a Windows Forms control on a thread other than the one creating it.
The fix: use Invoke.
public void Start1()
{
for (int i = 0; i < 1000; i++)
{
t1.Invoke(() => displaytext("Working.........", i));
Thread.Sleep(100);
}
}
You gotta Invoke the function through delegate to get it to work.
private void s1_Click(object sender, EventArgs e)
{
Thread Thread1 = new Thread(new ThreadStart(Start1));
Thread1.Start();
}
public void Start1()
{
for (int i = 0; i < 1000; i++)
{
if(t1.InvokeRequired)
{
t1.Invoke(new MethodInvoker( () => displaytext("Working.........", i)));
}
else
{
displaytext("Working........", i);
}
Thread.sleep(100);
}
}
public void displaytext(string thetext, int number)
{
t1.Text = thetext + " " + number;
}
I'm having an application with a progress bar and a buttom.
When the button clicked the progress bar value will get increased, here is the source code,
private void Run()
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
private void button1_Click(object sender, EventArgs e)
{
Run();
}
so when i run the application, the progress bar value is getting increased, but when i try to move the window its not responding.
I can not run it in a normay thread way - it will throw Cross-Thread error.
so i changed the code like,
private void Run()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(this.Run));
}
else
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(new ThreadStart( Run));
myThread.Start();
}
Now i can able to move the winodow, but when i move the progress bar is stopped, and when i release the mouse button its resuming. So still the execution is in UI Thread.
How to handle it in a better way.Please help me to do this .
Invoke() works by running the given delegate from the UI thread. So if you use Invoke() to run your entire method, then your entire method runs from the UI thread.
Instead, you should be doing your actual work in the other thread, and just performing UI updates in the UI thread, by just Invoke()ing the little bits of code that perform the updates.
One easy way to do this is to use the BackgroundWorker class built into the standard library.
This has been answered here - in your case the code should look something like:
this.BeginInvoke((Action)(() => progressBar1.Value++));