So I have multi threaded my application. I ran into this error "Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on."
My thread was calling a windows forms control. So to get around this I used
Control.Invoke(new MethodInvoker(delegate { ControlsAction; }));
I am trying to figure out a way that I could make this generic method so I can reuse code and make the app alot cleaner.
So for instance, on of my invokes I do the following with a rich text box.
rtbOutput.Invoke(new MethodInvoker(delegate {
rtbOutput.AppendText(fields[0].TrimStart().TrimEnd().ToString() + " Profile not
removed. Check Logs.\n"); }));
Another is with a combo box where I am simply setting the text.
cmbEmailProfile.Invoke(new MethodInvoker(delegate { EmailProfileNameToSetForUsers =
cmbEmailProfile.Text; }));
Another example is again with a rich text box where I am simply clearing it.
rtbOutput.Invoke(new MethodInvoker(delegate { rtbOutput.Clear(); }));
How would I create a generic function that could do this for me where I just need to pass in the control with the action i want it to do?
This is what we have come up so far.
private void methodInvoker(Control sender, Action act)
{
sender.Invoke(new MethodInvoker(act));
}
so the problem is something like appendtext, it doesn't seem to like.
Something like this should do the trick:
public static class FormsExt
{
public static void InvokeOnMainThread(this System.Windows.Forms.Control control, Action act)
{
control.Invoke(new MethodInvoker(act), null);
}
}
And then using it is as simple as:
var lbl = new System.Windows.Forms.Label();
lbl.InvokeOnMainThread(() =>
{
// Code to run on main thread here
});
With your original label:
rtbOutput.InvokeOnMainThread(() =>
{
// Code to run on main thread here
rtbOutput.AppendText(fields[0].TrimStart().TrimEnd().ToString() + " Profile not removed. Check Logs.\n"); }));
});
Related
I searched and got that Dispatcher CheckAccess can be used in place of InvokeRequired in wpf.
This is the code want to convert in wpf
private void ChangeTextBox(string txt)
{
if (msg_log.InvokeRequired)
{
Invoke(new UpdateText(ChangeTextBox), new object[] { txt });
}
else
{
msg_log.Text += txt + "\r\n";
}
}
I tried out this ---->
private void ChangeTextBox(string txt)
{
if (msg_log.Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new UpdateText(ChangeTextBox), new object[] { txt });
}
else
{
msg_log.Text += txt + "\r\n";
}
}
But while running i am getting Error [InvalidOperationException] "The calling thread cannot access this object because a different thread owns it."
What i am doing wrong ? Please Help ?
Your problem is not because of the CheckAccess method... it is fine to use that to check whether the call to Invoke is required or not. When you call the Dispatcher, it is important to ensure that you are calling the correct instance of the Dispatcher class. From the Dispatcher Class page on MSDN:
In WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with. For example, a background thread cannot update the contents of a Button that is associated with the Dispatcher on the UI thread. In order for the background thread to access the Content property of the Button, the background thread must delegate the work to the Dispatcher associated with the UI thread. This is accomplished by using either Invoke or BeginInvoke. Invoke is synchronous and BeginInvoke is asynchronous.
So in your case, if you can access the correct Dispatcher instance using the following:
msg_log.Dispatcher.CheckAccess()
Then as #SriramSakthivel mentioned in the comments, you should access the same instance when calling Invoke:
msg_log.Dispatcher.Invoke(new UpdateText(ChangeTextBox), new object[] { txt });
OP problem solved but just for record a useful helper for dispatcher check is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
which can then be called as:
DispatchIfNecessary(() => { myUIcontrol.Update(...); });
I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.
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));
im new to C# language and i would appreciate any help/feedback on the following issue.
Basically, im trying to develop a program such that it has two classes class1(main thread) and class2. class1 has a button and class2 starts a thread and do some calculations. How can i update any property of the button in class1(main) from class2 within the thread in class2?
here is an example i would like to do
// CLASS 1 is a win form
class1:form
{
public btn_click()
{
// call function startthread from class2
btn.Enabled = false;
startthread()
}
}
// CLASS 2 is not a winform
class2
{
public startthread()
{
Thread worker = new Thread(doCalculation)
}
public doCalculation()
{
// do some calculation then
// blahhh
// blahhh
// HERE I NEED SOMEHOW ENABLED MY BUTTON
}
}
This depends a bit on the UI framework you're using. As an example, say you wanted to update the text written on the button.
If you're using WPF, you could use:
class1Instance.TheButton.Dispatcher
.BeginInvoke( (Action) () => class1Instance.TheButton.Content = "Foo" );
With Windows Forms, you'd use:
class1Instance.TheButton
.BeginInvoke( (Action) () => class1Instance.TheButton.Text = "Foo" );
You have to bring the modifying code back onto the the main thread, regardless whether its WPF or Winforms, cross-thread access to the UI is either explicitly disallowed (usually) or strongly discouraged and buggy.
There are a lot of ways to do this, in Winforms I typically use Control.Invoke like this:
public void UpdateMyTextBox(string NewText)
{
if(InvokeRequired)
Invoke(new Action<string>(UpdateMyTextBox), NewText);
else
myTextBox.Text = NewText;
}
In this example, the InvokeRequired property will check to see if it's being called on the UI thread, and if not, we will call Invoke which will place the request onto the UI thread.
In WPF, you watch to use the Dispatcher, and you do so in a very similar way:
public void UpdateMyWpfTextBox(string NewText)
{
if(!CheckAccess())
Dispatcher.Invoke(new Action<string>(UpdateMyWpfTextBox), NewText);
else
myTextBox.Text = NewText;
}
Not to sure what your trying to do here, but this is best an answer I can come up with, considering the questions. I'm going to assume this is a winforms application
Class2 is going to need a reference to the button in Class 1. So you'll need to pass that to it.
Once Class2 has reference to the button you can attempt to modify the property as
btn.Text = "New Text";
I'm going to assume again that you've tried this route, got an exception and so you posted your question. The reason you got the exception was that your btn and your class2 are running in separate threads. To get around this, you can call invoke on the button. I often handle this like this.
private void SetButtonText(string newText) {
if (btn.InvokeRequired) {
Invoke((MethodInvoker)(() => SetButtonText(newText)));
}
else {
btn.Text = newText;
}
}
It is a cross threaded operation in windows application done in c#, How can i change it ?
You can write a method which you can call from any thread:
private void SetLabel(string newText)
{
Invoke(new Action(() => SomeLabel.Text = NewText));
}
Then you can just call SetLabel("Update the label, please") from any thread.
However, your question title states “from another Form” rather than “from another thread”, so it is unclear what you actually mean. You don’t need multithreading if you just want to have multiple forms. You should use threads only for tasks, e.g. downloading a file, copying a file, calculating a value, etc., but not for Forms.
You need to use a delegate and invoke...
private delegate void SetLabelSub(string NewText);
private void SetLabel(string NewText)
{
if (this.InvokeRequired()) {
SetLabelSub Del = new SetLabelSub(SetLabel);
this.Invoke(Del, new object[] { NewText });
} else {
SomeLabel.Text = NewText;
}
}
Then you can just call SetLabel("New Text Here") from any thread
How about writing a more general method to change the Text property of any control in your form like:
private void SetText(Control control, string text)
{
if (control.InvokeRequired)
this.Invoke(new Action<Control>((c) => c.Text = text),control);
else
control.Text = newText;
}
This will work for labels, buttons, etc, from either the UI thread or any other thread.
If you're dealing with threads you need to use the form.Invoke() method, assuming you're passing the form instance into the other form.roughly
Form form1 = new Form()
Form form2 = new Form();
form2.CallingForm = form1; // make this property or what ever
inside form2 add some code like
form1.Invoke(someDelagate, value);
I don't do winforms that often but if you google form.Invoke you'll get some good examples of how to do cross thread operations.