delegate void ReportCreatorHandler();
void ReportCreator(Report report, bool isExport, bool isPrint)
{
if (this.InvokeRequired)
{
this.Invoke(new ReportCreatorHandler(delegate() { ReportCreator(report, isExport, isPrint); }));
}
else
{
------;
------;
TForm reportPage = new TForm();
------;
------;
this.tabpages.add(tabpage1);//tabpages is TabPageCollection
}
}
Eventhough i handled "InvokeRequired" in the above code,while adding a tabpage to a TabPageCollection,i am getting an error "Controls created on one thread cannot be parented to a control on a different thread".Also i tried accessing InvokeRequired object on TabPageCollection,property is not there for that collection.Can anyone help in handling the above exception ??
Thanks in advance.
There is one case when the InvokeRequired property return false, although you are on a different thread. -> in case the handle of the connected control is not created yet, or already disposed.
-> https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx
so you either check the ishandledcreated property as well, or you switch to SynchronizationContext for marshalling. Which is recommended.
Related
So I currently have this code below, which has a background worker call showdialog(). However, I thought that the UI cannot be updated on a background thread, so how does the dialog display? Does the dialog actually get opened on the UI thread? what happens?
public partial class ProgressDialog : Window
{
BackgroundWorker _worker;
public BackgroundWorker Worker
{
get { return _worker; }
}
public void RunWorkerThread(object argument, Func<object> workHandler)
{
//store reference to callback handler and launch worker thread
workerCallback = workHandler;
_worker.RunWorkerAsync(argument);
//display modal dialog (blocks caller)
//never returns null, but is a nullable boolean to match the dialogresult property
ShowDialog();
}
I have gotten suggestions that I just run the code and check, but how do i check whether the show dialog window was opened on a new thread or on the background thread itself? Not sure how I would check that.
Anyway this was just a post to try to help my understanding of what is actually happening in my code.
Anyway finally understood more of the comments, so I think I understand everything that is going on. Most of my real problems weren't caused by this dialog anyway, they were caused by updating observable collections from a non-ui thread while controls were bound to them.
Technically you are not changing a property on your Main thread just creating a instance of another object.
But it could help if you elaborate a bit more on your method ShowDialog().
I had also problem with calling ShowDialog() from non-UI thread. And my answer is that it depends on the thread which calls the ShowDialog(). If you set the ApartamentState property for this thread before its start then everything will work as called from the UI thread. I have finally ended up with such a code:
private async void button1_Click(object sender, EventArgs e)
{
var foo = new Foo();
// MessageBox.Show(foo.DirPath) - this works as a charm but
// if, it is called from non UI thread needs special handling as below.
await Task.Run(() => MessageBox.Show(foo.DirPath));
}
public class Foo
{
private string dirPath;
public string DirPath
{
get
{
if (dirPath == null)
{
var t = new Thread(() =>
{
using (var dirDialog = new FolderBrowserDialog())
{
if (dirDialog.ShowDialog() == DialogResult.OK)
dirPath = dirDialog.SelectedPath;
}
}
);
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
return dirPath;
}
set
{
dirPath = value;
}
}
}
I dont know for sure but i thought that the showDialog doesnt create the object only showing it. So when u say ShowDialog it only tells to show. So it will run on the UI thread instead of the backgroundworker
(dont know for sure)
I'm trying to hide a form created on the main thread, from a secondary thread but I obviously get a cross-threading issue when I call the hide method. I'm new to this and don't really have a clue as to how to how to correct this. I've always just created a delegate to invoke my method if it's changing stuff created on the main thread, but I don't know how to do that here for the built-in hide method. Let me know if you need more information.
code:
public partial class MainForm : Form
{
ControlPanelForm m_controlPanel = new ControlPanelForm();
// ....
void MeterThread()
{
while (true)
{
// ....
if (EMOdetected)
{
m_controlPanel.Deinitialize();
m_controlPanel.Hide(); // **** //
}
}
}
}
Basically, my MainForm pulls up a control panel form that does some work. In the background I have a thread running and checking for stuff, one of which is an Emergency Off, at which point I want to shut my control panel down and then hide it.
If I try to invoke it right there,
m_controlPanel.Invoke(new EMOHandler(m_controlPanel.Hide)); // **** //
it doesn't look like it executes anything when i debug it. It seems to just pass over the command. Again, I'm new to this so any and all explanations are welcome.
There's no reason to check InvokeRequired or create an anonymous method. Simply write
mainForm.Invoke(new MethodInvoker(mainForm.Hide));
You haven't given any information code-wise but this is a common pattern for manipulating the UI thread from a non-UI thread.
if (mainForm.InvokeRequired)
{
mainForm.Invoke(new Action(() =>
{
mainForm.Hide();
}));
}
else
mainForm.Hide();
As a simple rule, which you already pointed out:
You should not access one window from another thread.
I would suggest you something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
textBox1.Text += value;
}
void SampleFunction()
{
// Gets executed on a seperate thread and
// doesn't block the UI while sleeping
for(int i = 0; i<5; i++)
{
AppendTextBox("hi. ");
Thead.Sleep(1000);
}
}
}
This is a pretty nice example of how to use MultiThreading, which I got from here.
Though in your case, the method Hide already exists on MainForm and literally waits for you to be invoked, like others already pointed out:
mainForm.Invoke(new MethodInvoker(mainForm.Hide));
I have a form whose focused state is checked in a method using:
if (!this.Focused)
{
//do something
}
However, this also needs to be checked from another worker thread, and I am getting cross thread violations when I call if (!this.Focused) from another thread . How can I access the true or false state of this.Focused boolean from another thread? I am familiar with using delegates to update form controls from other threads, but I am having a real issue with this. What am I missing? Any help is greatly appreciated.
It's exactly the same - you just need to use the return value of Invoke, which is the return value of the delegate:
Func<bool> func = () => this.Focused;
var focused = (bool) Invoke(func);
What about this?
bool focused = false;
this.Invoke((MethodInvoker)delegate
{
focused = controlname.Focused;
});
Try,
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
focused = controlname.Focused;
});
}
else
{
focused = controlname.Focused;
}
check more about InvokeRequired http://www.codeproject.com/Articles/37642/Avoiding-InvokeRequired
I'm looking for a rule to know which properties of a Control prevent accessing from other threads than the thread it was created on. (say UI thread) In other words, why is it possible to access some properties of a Control from any arbitrary thread while some other ones refuse it?
:
:
{
Thread thrd1 = new Thread(DoSomething);
thrd1.Start();
}
:
:
void DoSomething()
{
// There is no problem..
dataGridView1[columnIndex1, rowIndex1].Value = "Access is free!";
dataGridView1[columnIndex1, rowIndex1].Style.BackColor = Color.Red;
// Cross-Thread operation exception would be thrown..
dataGridView1.Rows[rowIndex1].Visible = false;
}
:
:
Thanks
Basically anything whose implementation involves a window handle has affinity with the thread that created that window handle. This restriction is inherited from the underlying Windows API.
Since you can't know the implementation, you have to assume that everything needs Invoke unless the documentation specifically calls out an exception.
While some properties don't require synchronized access, you shouldn't rely on this behavior unless the documentation for the property clearly indicates that the properties may be accessed from any thread. Use Control.InvokeRequired to conditionally perform a cross-thread invocation.
For example:
void DoSomething()
{
if (dataGridView1.InvokeRequired)
dataGridView1.Invoke(new Action(DoSomethingImpl));
else
DoSomethingImpl();
}
void DoSomethingImpl()
{
dataGridView1[columnIndex1, rowIndex1].Value = "Access is free!";
dataGridView1[columnIndex1, rowIndex1].Style.BackColor = Color.Red;
dataGridView1.Rows[rowIndex1].Visible = false;
}
I like to encapsulate this functionality like so:
public static object AutoInvoke(
this ISynchronizedInvoke self,
Delegate del,
params object[] parameters)
{
if (self == null)
throw new ArgumentNullException("self");
if (del == null)
throw new ArgumentNullException("del");
if (self.InvokeRequired)
return self.Invoke(del, parameters);
else
return del.DynamicInvoke(parameters);
}
Then DoSomething() becomes:
void DoSomething()
{
dataGridView1.AutoInvoke(new Action(DoSomethingImpl));
}
if (listBox1.InvokeRequired)
{
listBox = new StringBuilder(this.listBox1.Text);
}
This is the code in c# which when executed produces an invalid cross thread operation error for listBox1 which is a listbox in my form.
Could u guys please tell me why??
I am using the invokeRequired method too and am not changing the contents of the listbox either.
InvokeRequired only tells you that an Invoke is necessary in order to validly access the element. It doesn't make the access legal. You must use the invoke method to push the update to the appropriate thread
Action update = () => listbox = new StringBuilder(this.listBox1.Text);
if (listBox1.InvokeRequired) {
listBox1.Invoke(update);
} else {
update();
}
InvokeRequired simply checks to see if Invoke is required. You found it's required, yet didn't call Invoke!
Your code should run when InvokeRequired is false
delegate void SetListBoxDelegate();
void SetListBox()
{
if(!InvokeRequired)
{
listBox = new StringBuilder(this.listBox1.Text);
}
else
Invoke(new SetListBoxDelegate(SetListBox));
}
Edit:
Check out Making Windows Forms thread safe