Threads and Tasks - c#

I'm trying to open new windows form with thread and task, but this code open new windows, but do not show buttons, textfield, etc on it (form is loading)
private void newFomrm_Click(object sender, EventArgs e)
{
NewForm a = new NewForm(param); // my Form
Action showMethod = () => { a.Show(); };
Task t1 = new Task(showMethod);
Thread t = new Thread(new ThreadStart(t1.Start));
t.Start();
}

private void button1_Click(object sender, EventArgs e)
{
NewForm a = new NewForm(); // my Form
Action showMethod = () => {
Invoke(new Action(() => a.Show()));
};
Task t1 = new Task(showMethod);
Thread t = new Thread(new ThreadStart(t1.Start));
t.Start();
}
try this...you have to put the UI operation on the main thread

Related

C# Accessing element from other thread

How can I do to access an element from another thread? In the case, I have a richtextbox in the Main Thread (GUI), and I'm running a method on a secondary thread. I want to access the richeditbox through the secondary thread
private void Log(string input, Label lbl)
{
lbl.Invoke(new Action(()=>
{
lbl.Text = "Status: " + input;
Thread.Sleep(50);
}));
}
void Run()
{
foreach (string line in richTextBox1.Lines)
{
Log(line, label1);
Thread.Sleep(500);
}
}
private void button1_Click(object sender, EventArgs e)
{
ThreadStart th = new ThreadStart(() => Run());
Thread th2 = new Thread(th);
th2.Start();
//th2.Join();
}
The following error is shown:
Invalid thread operation: control 'richTextBox1' accessed from a
thread that is not the one in which it was created.
You're already doing this. Your Log method shows the correct thing to do -- use Invoke to run some code on the UI thread. In this case, you could do something like:
void Run()
{
var getLines = new Func<object>(() => richTextBox1.Lines);
var lines = (string[]) richTextBox1.Invoke(getLines);
foreach (var line in lines)
{
Log(line, label1);
Thread.Sleep(500);
}
}
However, this really isn't necessary. It looks like you really want to read the Lines property once when your button is clicked and just pass it to the background thread.
void Run(string[] lines)
{
foreach (var line in lines)
{
Log(line, label1);
Thread.Sleep(500);
}
}
private void button1_Click(object sender, EventArgs e)
{
var lines = richTextBox1.Lines;
var th = new ThreadStart(() => Run(lines));
var th2 = new Thread(th);
th2.Start();
}
Here's another version...not that you shouldn't be sleeping in the Log() method as that is running in the UI thread!
private void button1_Click(object sender, EventArgs e)
{
ThreadStart th = new ThreadStart(() => Run());
Thread th2 = new Thread(th);
th2.Start();
}
void Run()
{
string[] lines = (string[])richTextBox1.Invoke(new Func<string[]>(() => richTextBox1.Lines));
foreach (string line in lines)
{
Log(line, label1);
Thread.Sleep(500);
}
}
private void Log(string input, Label lbl)
{
lbl.Invoke(new Action(() =>
{
lbl.Text = "Status: " + input;
}));
}

Multi-thread UI, unable to BeginInvoke

I have a requirement to launch a window on a separate thread from the main UI thread. It's not ideal, I know it's not normal.
The new window is launched as follows:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var thread = new Thread(() =>
{
var w = new ScrollingBanner(300,70,0,0);
w.Show();
w.Name = "BannerThread";
w.Closed += (sender2, e2) => w.Dispatcher.InvokeShutdown();
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
This launches the window and performs the action I need it to.
The control on the Window of this (threaded) UI calls an event I'm listening for in the code behind the window, as below:
private void ContentTicker_OnScrollComplete(object sender, EventArgs e)
{
Debug.WriteLine("Scroller Ended");
try
{
if (CheckAccess())
{
sliderText.Text = "Updated Text";
}
else
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => sliderText.Text = "Updated Text"));
}
}
catch (Exception ex)
{
//breakpoint
}
}
ContentTicker_OnScrollComplete is called from a background thread under which the control runs.
I get an exception, a different thread owns the control; but as far as I'm aware I'm calling the Dispatcher to perform the action on the correct thread. (which works if I do all this on the main UI thread)
How can I update "sliderText" on the correct thread?
Thanks
Replace this:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => sliderText.Text = "Updated Text"))
For this:
Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() => sliderText.Text = "Updated Text"))
because Application.Current.Dispatcher will return the "Main" Dispatcher, not the "Secondary" one.

BackgroundWorker ShowDialog causes app to stop

I have been trying to implement a BackgroundWorker into my application, and so far, it has not gone well. On a new thread, I want to open up a new Form that will have a progressbar and a label to report progress, however, this is not working well. When I call ShowDialog, the application does not respond any more. Is this because my code is running from my Form1, and I am showing WorkingForm? Also, can this be implemented cleaner?
private void button14_Click(object sender, EventArgs e)
{
List<object> param = new List<object>();
object[] objectparams = new object[1];
objectparams[0] = null;
Opera opera = new Opera();
System.Reflection.MethodInfo clearOpera = opera.GetType().GetMethod("ClearOpera");
param.Add(clearOpera);
param.Add(opera);
param.Add(objectparams);
backgroundWorker1.RunWorkerAsync(param);
}
private void button2_Click_1(object sender, EventArgs e)
{
Browser.cancelPending = true;
}
private delegate void getnewform();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
mainForm main = new mainForm();
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
var variab = (bool)form.Invoke(new getnewform(main.AskForConfirmation));
List<object> param = e.Argument as List<object>;
List<object> result = new List<object>();
var method = param[0] as MethodInfo;
object[] parameters = param[2] as object[];
if (parameters[0] == null)
{
result.Add(method.Invoke(param[1], null));
result.Add(false);
}
else
{
result.Add(method.Invoke(param[1], parameters));
if (parameters.Contains(true))
result.Add(true);
}
int progress = (100 * Browser.progressValue) / Browser.progressMax;
backgroundWorker1.ReportProgress(progress);
// If the BackgroundWorker.CancellationPending property is true, cancel
if (backgroundWorker1.CancellationPending)
{
Console.WriteLine("Cancelled");
Browser.cancelPending = true;
}
e.Result = result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
form.progressBar1.Value = e.ProgressPercentage;
form.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
Application.DoEvents();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
List<object> param = e.Result as List<object>;
if (e.Cancelled == false && param.Contains(true))
{
Display.DisplayURLs(param[0] as SortableBindingList<URL>);
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
MessageBox.Show("Done");
}
else if (e.Cancelled == false && param.Contains(false))
{
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
MessageBox.Show("Done");
}
}
public class mainForm
{
public void AskForConfirmation()
{
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
var workingForm = new TestURLGUI4.WorkingForm();
workingForm.ShowDialog(form);
workingForm.DialogResult = DialogResult.None;
}
}
Edit:
Ok, I have updated my code according to the suggestions, and now, this produces a stackoverflowexception in System.Windows.Forms.dll:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
mainForm main = new mainForm();
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
List<object> param = e.Argument as List<object>;
List<object> result = new List<object>();
var method = param[0] as MethodInfo;
object[] parameters = param[2] as object[];
if (parameters[0] == null)
{
result.Add(method.Invoke(param[1], null));
result.Add(false);
}
else
{
result.Add(method.Invoke(param[1], parameters));
if (parameters.Contains(true))
result.Add(true);
}
int progress = (100 * Browser.progressValue) / Browser.progressMax;
backgroundWorker1.ReportProgress(progress);
// If the BackgroundWorker.CancellationPending property is true, cancel
if (backgroundWorker1.CancellationPending)
{
Console.WriteLine("Cancelled");
Browser.cancelPending = true;
}
e.Result = result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestURLGUI4.Form1 form1 = (TestURLGUI4.Form1)Application.OpenForms[0];
if (Application.OpenForms.Count >= 2)
{
TestURLGUI4.WorkingForm form2 = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
form2.progressBar1.Value = e.ProgressPercentage;
form2.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
Application.DoEvents();
}
else if(Application.OpenForms.Count == 1)
{
var workingForm = new TestURLGUI4.WorkingForm();
workingForm.ShowDialog(form1);
}
}
The purpose of a BackgroundWorker is to invoke code on another thread (not the UI thread). By calling Invoke in the DoWork method, you're completely circumventing the purpose of BackgroundWorker. Do all your UI work before you start the worker. If you need to interact with the user while the worker is working, do it in the ProgressChanged handler--it runs on the UI thread and you don't need to use Invoke in ProgressChanged.
By invoking UI work in DoWork, you run the risk of a deadlock, which will hang your program
you cant run UI on other threads. Has to be on the main thread.
Instantiate the UI before you start the new thread. In the new thread use cross thread invoke methods on the controls you want to work with. Look here for example http://msdn.microsoft.com/en-us/library/ms171728.aspx

Displaying WPF-"NotifyIcon" on a separate thread

I am currently working on an office add-in and I need to show a notification dialog that displays progress, I'm using Philipp Sumi's wpf-notifyicon.
I need to display the notifyicon from a separate thread as I have a lot of code that already executes on the main thread, this causes the wpf-notifyicon to block and wait because the messages in the windows message queue are not being processed.
I know that I should rather execute this time consuming code on a separate thread, and display the notifyicon from the main thread and update it accordingly, but that is unfortunately not an alternative because this whole solution is single-threaded.
Example:
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
Thread showThread = new Thread(delegate()
{
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
});
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.TextB.Text = "Doing something...";
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.TextB.Text = "Done doing something...";
}
Update
I have been able to progress a little further with this updated code:
I'm creating the TaskbarIcon object on a new thread , and using Application.Run to process the application message loop on that thread...
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
Thread showThread = new Thread(delegate()
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
System.Windows.Forms.Application.Run();
});
showThread.SetApartmentState(ApartmentState.STA);
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Doing something...";
}));
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Done doing something...";
}));
}
I have solved my problem, I had to initialize the notifyIcon on a separate STA thread and use Application.Run in order to start pumping windows messages on that thread.
var myThread = new Thread(delegate()
{
notifyIcon = new NotifyIcon();
Application.Run();
});
myThread.SetApartmentState(ApartmentState.STA);
myThread.Start();
Then I just had to Invoke the UI of my notification dialog.

TopMost form in a thread?

I am using the following code to open a form in a new thread:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
form.ShowDialog();
}
But the newly created form isn't TopMost even though I set it to true.
How can I make a form in a thread TopMost ?
Usually you don't need another thread, you open the form as usual in modal or non modal mode, if the form needs to do a heavy process then you do the process inside a thread.
Specific to your question one option is to run the form from an Application.Run as described here.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
using (Form1 _form = new Form1())
{
_form.TopMost = true;
Application.Run(_form);
}
}
}
That will launch a new thread with its own message pump and will keep it as a TopMost form.
Just ran into this problem myself. It seems that if the form has an Owner, then TopMost works as expected. If the owning form was created on another thread, though, it's a little tricky to set. Here's what I used:
var form = new Form();
form.Shown += (sender, e) => {
Control.CheckForIllegalCrossThreadCalls = false;
form.Owner = /* Owning form here */;
form.CenterToParent(); // Not necessary
Control.CheckForIllegalCrossThreadCalls = true;
form.TopMost = true; // Works now!
};
Application.Run(form);
Instead of calling ShowDialog directly, try using this.Invoke to gain ownership of the form.
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
this.Invoke((Action)delegate() { form.ShowDialog(); });
}

Categories