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
Related
I’m doing some stuffs in thread and I’m try to access the label property, but I can’t to set the property value.
lblDisplay.Visible = true;
I’m getting an error on this.
Error - Cross-thread operation not valid: Control 'lblDisplay' accessed from a thread other than the thread it was created on.
Thanks in advance.
You should use the BeginInvoke method on the form to set the variable on the same thread it's running on, for example:
this.BeginInvoke((Action)delegate{ lblDisplay.Visible = true; });
Most people will tell you to use the Invoke method instead but unless you absolutely NEED everything in the delegate to be run before any other code in the thread is executed you probably wont need it. Invoke will block the thread from processing any further until the delegate has completed, where as BeginInvoke will simply execute it in the thread the form is running in while simultaneously running the thread that began the invoke.
I think you first need to Check whether its need to invoke or not ( In some other case that same code may not need to invoke) so...
if(lblDisplay.InvokeRequired) {
lblDisplay.Invoke((Action)delegate{ lblDisplay.Visible = true; }); // For synchronous
lblDisplay.BeginInvoke((Action)delegate{ lblDisplay.Visible = true; }) // For asynchronous
}
else
{
lblDisplay.Visible=true;
}
You can’t directly access from a thread other than the thread it was created on. You can set that property value by using MethodInvoker.
lblDisplay.Invoke((MethodInvoker)(() => { lblDisplay.Visible = true; }));
This the way you need to access the control in different thread.
A Control can only be accessed within the thread that created it - the UI thread.
Try this,
Invoke(new Action(() =>
{
lblDisplay.Visible = true;
}));
Using this.BeginInvoke method with lambda :
this.BeginInvoke(new Action(() => { lblDisplay.Visible = true; }));
Reference : https://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke(v=vs.110).aspx
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.
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 am using a CheckBox as a Toggle Button (checkBox.Appearance = Appearance.Button) in a child form. If the users presses the button a measurement cycle will start if some criteria are satisfied (e.g. temperature within range). The CheckBox.Checked property remains true until the measurement is completed.
If e.g. the temperature is out of range a warning will appear and the button will be reset. The same happens if the cycle ended properly. In the end this self-explaining function is called:
/// <summary>
/// Resets the button states to false
/// </summary>
public void ResetButton()
{
checkBox_Start.Checked = false;
}
Now, when debugging, I see that the CheckBox_Start.Checked property is false and remains false. BUT the UI does not show the actual value. It seemingly remains checked. I have tried Refresh() and Update() on all levels.
Does anyone have an idea? What could possibly keep the UI from showing the actual value?
I found a similar problem, except with DataGridView. The solution I found was to invalidate the control and then request it to be redrawn, as follows:
public void ResetButton()
{
checkBox_Start.Checked = false;
checkBox_Start.Invalidate();
checkBox_Start.Update();
}
Try that, see if it fixes it.
this.MyCheckBox.Checked=false;
I had same problem, after spending some times on CheckBox properties and methods, finally i realize that where I am initializing this control is not a suitable one. I was using a semi framework which in some cases doesnt goes in this code. I use this simple property:
checkBox_Start.Checked = false;
Make sure you do not have InitializeComponent running twice.
That would cause the initial poster's control behaviour.
This usually comes with trying to update UI within un-main thread. So I recommend to create a generic function doing UI update. And let the main or un-main thread call it if need to update UI.
This is my sample code:
public void OnCheckBox(bool set_on) {
if (set_on == true) {
CBox.Invoke(new Action(() => {
CBox.Checked = true;
CBox.Invalidate();
CBox.Update();
}));
}
else {
CBox.Invoke(new Action(() => {
CBox.Checked = false;
CBox.Invalidate();
CBox.Update();
}));
}
}
And you can call it like:
void run() {
OnCheckBox(true);
Thread.Sleep(500);
OnCheckBox(false);
Thread.Sleep(500);
OnCheckBox(true);
Thread.Sleep(500);
OnCheckBox(false);
}
Thread t = new Thread(new ThreadStart(this.run));
t.Start();
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