Change label text and start threads in the same void - c#

I have a big problem and hope you guys can help me...
I need to start a Thread called ListenOnSocket, no problem so far..
Thread ListenOnSocket = new Thread(newThreadStart(Form1.ListenOnSocket));
ListenOnSocket.Start();
But when I want to change the label from within ListenOnSocket, I get an object reference is required for the non-static field, method or property.
So normally you would passe the label on by doing this
public static void ListenOnSocket(Label lblstatus)
{
//i want to change the label from here.
lblstatus.text = "Hello";
}
but then I get
No overload for ListenOnSocket matches delegate System.Threading.Threadstart
in my threadstart.
Can anyone please help, I am really stuck, sorry if there is not much to go on I am quite new to C#.

You can use Lambda Expression to pass parameter.
Thread ListenOnSocket = new Thread( () => { Form1.ListenOnSocket(yourParameter); } );
ListenOnSocket.Start();
But you will get the CrossThreadException when the ListenOnSocket method execute. So you need to use BeginInvoke to set label text.
So search the CrossThreadException and why you will get it.
Note: I do not write the sample code for this, because searching is more beneficial.

You need to marshal this back to the UI thread:
public static void ListenOnSocket(Label lblstatus)
{
this.BeginInvoke(new Action(() =>
{
//i want to change the label from here.
lblstatus.text = "Hello";
});
}

It looks like you might actually want a ParameterizedThreadStart here.
You would pass the Label in as its parameter.
Control changes also need to be performed on the UI thread.
public void DoSomething()
{
// Actually a ParameterizedThreadStart, but you don't need to state this explicitly
//var listenOnSocket = new Thread(new ParameterizedThreadStart(ListenOnSocket));
var listenOnSocket = new Thread(ListenOnSocket);
// Pass the label as the ParameterizedThreadStart parameter
// TestLabel is a label within the form
listenOnSocket.Start(TestLabel);
}
private void ListenOnSocket(object statusLabelObject) // Parameter must be of type Object
{
var statusLabel = statusLabelObject as Label;
if (statusLabel == null)
throw new ArgumentException(#"Parameter must be of type Label", "statusLabelObject");
// Changes to controls must be performed on the UI thread.
BeginInvoke((Action)(() => statusLabel.Text = #"text"));
}

They key gotcha here is it's not valid to update a UI element (like a label) from a background thread.
If you have a long running task then you probably don't want to run that on the UI thread as it will block.
Assuming that you're creating a thread because you have something that runs for too long to run on the UI thread, it might be worth looking into way of marshalling calls from background threads onto the UI thread.
For more information on how to do this see How to update the GUI from another thread in C#? if you're looking to update the status from a long running task, you might want to look into background worker: MSDN: How to Use Background Worker which is a helper class designed to help with long running background tasks.

Related

Accessing Object from different thread - Tasks

i am new to tasks and don't really know if its good to use it the way i want.
I have a Class which inherits from TextBlock and colorize the text (Syntaxhighlighting).
It does take awhile so i want to do it in Background
Here is my code so far:
async void SyntaxTextBlock_TargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
if ((clientSettings.SyntaxCheck || clientSettings.SyntaxHighlighting) && !string.IsNullOrEmpty(Text))
{
string value = Text;
Syntax syntax = await Task.Factory.StartNew(() => DoSyntax(value));
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
if (syntax.IsTextDecoration)
TextDecorations.Add(Syntax.TextDecoration);
ToolTip = string.Join(Environment.NewLine, syntax.Messages);
Text = null;
foreach (Run run in syntax.Runs)
Inlines.Add(run);
}));
}
}
My problem is i cant access the syntax.Runs List from the object Syntax which is returned by DoSyntax(value).
Error: the calling thread cannot access this object because a different thread owns it
I tried it with the scheduler TaskScheduler.FromCurrentSynchronizationContext and it worked, but the GUI freezed.
Is there a way to do this on multiple different threads while the gui does not freeze?
The problem is that DoSyntax is creating thread-affine objects (specifically, Syntax) while on a background thread.
You'll need to change DoSyntax so that it will only create "normal" objects (like a list of ranges with colors/semantics) on the background thread, and then have your UI thread process that and create the actual Syntax types, if necessary.

Unable to update label prior to function call

I need to update a simple label control prior to calling a function in another class but the label only updates after the function call completes. The code is below.
lblCalling.Text = "Calling...";
bool res = pf_auth.pf_authenticate(pfAuthParams, out otp, out callStatus, out errorId);
NO, I don't think so. Rather, in your code the label gets updated as it supposed to but most probably the following method takes long time and you are running in STA (single threaded) mode and so your main UI thread is block; not allowing you to see the update probably.
Run the method pf_auth.pf_authenticate() in separate thread probably
Try this:
public await void DoWork()
{
await Task.Run(() =>
{
lblCalling.Text = "Calling...";
});
bool res = pf_auth.pf_authenticate(pfAuthParams, out otp, out callStatus, out errorId);
}

How to create a control properly from other thread (.net compact framework), with no reference to other control?

I have code that runs in a different thread than the UI's one, and it has to create a control (windows forms). However, I don't have a reference to any control from the UI (that way, I could use myControl.Invoke( methodThatAddsControlToUI ) ). Is there a way to do it in the .net compact framework?
I would be interested in a solution that doesn't use references to other controls, if possible (tracking all created forms, for example, would not be a good workaround, as my code will be in a library). In the full framework version, there is the Application.OpenForms property, but this doesn't exit in the CF.
EDIT:
The main purpose of this is calling a method on the UI thread:
class Worker
{
public MyMethod()
{
// I need to call a method on the UI (this code doesn't run in the UI thread),
// but I don't have any field in this object holding an UI control
// value. So, I can't write myControlField.Invoke(...),
// but I still need to call a method on the UI thread
}
}
Any suggestions?
From a library there's really no way to guarantee your thread context, so your safest bet is to have the consume provide the invoker and leave it to them to ensure it was created in the proper context. Something like this pattern:
class Foo
{
private Control m_invoker;
public Foo()
: this(null)
{
}
public Foo(Control invoker)
{
if (invoker == null)
{
// assume we are created on the UI thread,
// if not bad things will ensue
m_invoker = new Control();
}
else
{
m_invoker = invoker;
}
}
public void Bar()
{
m_invoker.Invoke(new Action(delegate
{
// do my UI-context stuff here
}));
}
}
I'm sorry if this isn't a real answer, but I think it may help:
The reason why WinForms has this approach -- using a Control or Form reference to access a Invoke method that enables you to run code on the UI Thread -- is that the only reason you should have to run a code in the UI Thread is if you are going to write/change the state of UI components.
Of course, if you are going to do that, you must have a reference to a UI component. So you'd have access to its Invoke method. I cannot think of any other reason you'd have to access the UI thread from a component other than to modify a visual element.
It must be invoke ... But invoke have to wait still main thread i mean you not get error this way but this is not exacly working parallel if you want to go more than one process at same time just create more then one thread
Thread thread = new Thread(new delegate_method(method));
thread.start ();
Thread thread2 = new Thread(new delegate_method(method2));
thread.start ();
handle two process same time
void method ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}
void method2 ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}
void finish_thread()
{
if(invoke.Required)
{
//Here you have to call delegate method here with UI
BeginInvoke(new delegate_method(finish_thread));
}
else
{
//Now you can control UI thread from here and also you finished background work
//Do something working with UI thread
textBox.Text = "";
}
}
//Declare this in class
public delegate void delege();
//Write this lines when you want to background thread start
Thread thread = new Thread(new ThreadStart(() => {
//Do what you what with backgorund threading , can not use any interface comand here
BeginInvoke(new delege(() => {
//Do here any main thread thread job,this can do interface and control jobs without any error
}));
}));
thread.Start();

Dispatcher.Invoke from a new thread is locking my UI

i'm using wpf, there's a button on my ui.
when the user clicks it, i have a for loop that runs a new method, on a new thread using autoresetevent.
in that method on that new thread, i'm using a label, let's call it lblStatus. i want to update that label on this thread that's not on the ui. using wpf, i have to use Dispatcher.Invoke.
here's a sample of my code:
Thread thread= new Thread(StartLooking);
thread.Start();
_waitHandle.WaitOne();
private void StartLooking(object value)
{
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
{
lblStatus.Content = "Scanning>...";
}
else
{
lblStatus.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => lblStatus.Content = "Scanning>>>>>"));
}
_waitHandle.Set();
}
the program just stops here. it doesn't change the content of the label, it returns to my ui, but blocks it.
i've tried
lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");
as well, but that isn't working also. any ideas?
The problem is that you're making it impossible for this to execute, since you're using Invoke.
Dispatcher.Invoke will not return until the UI thread processes. However, you've blocked the UI thread by calling _waitHandle.WaitOne();, and don't set the wait handle until AFTER this processes. The two effectively cause a dead lock.
If you switch this to use BeginInvoke instead, the UI will queue the element, the wait handle will set, THEN the label will update. It will work and not block, however.
Since the two previous posts already cover the problem in your code, just a suggestion: instead of
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
try using
if (!lblStatus.CheckAccess())
It's cleaner and has the exact intent you want. Just read about it here.
You probably want to use BeginInvoke instead. Invoke will block the thread that called it until the UI thread has run the Action, and since you're setting the priority to Background, this could take some time.
Best solution I have found for .net 4.5+ is using SynchronizationContext Post
Example (Task.Run's can be as many as you want in parallel accessing UI):
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var context = SynchronizationContext.Current;
Task.Run(() =>
{
var i = 0;
while (true)
{
context.Post((tmp) =>
{
uiText.Text = $"{i}";
}), this);
Thread.Sleep(1000);
i++;
}
});
}

Using Threads and .Invoke() and controls still remain inactive - C#

I am trying to populate a text box with some data, namely the names of several instruments a line at a time.
I have a class that will generate and return a list of instruments, I then iterate through the list and append a new line to the text box after each iteration.
Starting the Thread:
private void buttonListInstruments_Click(object sender, EventArgs e)
{
if (ins == null)
{
ins = new Thread(GetListOfInstruments);
ins.Start();
}
else if (ins != null)
{
textBoxLog.AppendText("Instruments still updating..");
}
}
Delegate to update textbox:
public delegate void UpdateLogWithInstrumentsCallback(List<Instrument> instruments);
private void UpdateInstruments(List<Instrument> instruments)
{
textBoxLog.AppendText("Listing available Instruments...\n");
foreach (var value in instruments)
{
textBoxLog.AppendText(value.ToString() + "\n");
}
textBoxLog.AppendText("End of list. \n");
ins = null;
}
Invoking the control:
private void GetListOfInstruments()
{
textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
new object[] { midiInstance.GetInstruments() });
}
Note: GetInstruments() returns a List of type Instrument.
I am implementing therads to try to keep the GUI functional whilst the text box updates.
For some reason the other UI controls on the WinForm such as a seperate combo box remain inactive when pressed until the text box has finished updating.
Am I using threads correctly?
Thanks.
You haven't accomplished anything, the UpdateInstruments() method still runs on the UI thread, just like it did before. Not so sure why you see such a long delay, that must be a large number of instruments. You can possibly make it is less slow by first appending all of them into a StringBuilder, then append its ToString() value to the TextBox. That cuts out the fairly expensive Windows call.
I would recommend using a SynchronizationContext in general:
From the UI thread, e.g. initialization:
// make sure a SC is created automatically
Forms.WindowsFormsSynchronizationContext.AutoInstall = true;
// a control needs to exist prior to getting the SC for WinForms
// (any control will do)
var syncControl = new Forms.Control();
syncControl.CreateControl();
SyncrhonizationContext winformsContext = System.Threading.SynchronizationContext.Current;
Later on, from any thread wishing to post to the above SC:
// later on -- no need to worry about Invoke/BeginInvoke! Whoo!
// Post will run async and will guarantee a post to the UI message queue
// that is, this returns immediately
// it is OKAY to call this from the UI thread or a non-UI thread
winformsContext.Post(((state) => ..., someState);
As others have pointed out, either make the UI update action quicker (this is the better method!!!) or separate it into multiple actions posted to the UI queue (if you post into the queue then other message in the queue won't be blocked). Here is an example of "chunking" the operations into little bit of time until it's all done -- it assumes UpdateStuff is called after the data is collected and not necessarily suitable when the collection itself takes noticeable time. This doesn't take "stopping" into account and is sort of messy as it uses a closure instead of passing the state. Anyway, enjoy.
void UpdateStuff (List<string> _stuff) {
var stuff = new Queue<string>(_stuff); // make copy
SendOrPostCallback fn = null; // silly so we can access in closure
fn = (_state) => {
// this is in UI thread
Stopwatch s = new Stopwatch();
s.Start();
while (s.ElapsedMilliseconds < 20 && stuff.Count > 0) {
var item = stuff.Dequeue();
// do stuff with item
}
if (stuff.Count > 0) {
// have more stuff. we may have run out of our "time-slice"
winformsContext.Post(fn, null);
}
};
winformsContext.Post(fn, null);
}
Happy coding.
Change this line:
textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
new object[] { midiInstance.GetInstruments() });
with this:
textBoxLog.BeginInvoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
new object[] { midiInstance.GetInstruments() });
You are feeding all instruments into the textbox at once rather then one-by-one in terms of threading. The call to Invoke shall be placed in the for-loop and not to surround it.
nope, you start a thread, and then use invoke, which basically means you are going back to the UI thread to do the work... so your thread does nothing!
You might find that it's more efficient to build a string first and append to the textbox in one chunk, instead of line-by-line. The string concatenation operation could then be done on the helper thread as well.

Categories

Resources