Delegates and setting labels - c#

So, my overall goal with this code is to set the text property of labels, from a different thread (in a safe manner).
namespace csDinger3
{
public delegate void setlblStarted_txt(string text);
public partial class ClientUI : Form
{
public void setlblStarted_txt(string text)
{
var setlblStarted a = new setlblStarted(setlblStarted_txt);
if (this.lblStarted.InvokeRequired)
{
this.Invoke(a, new object[] { text });
}
else
{
this.lblStarted.Text = text;
}
}
}
}
Calling code:
namespace csDinger3
{
public class Program
{
// Some code that's not relevant
public static void updateText(Int32 number)
{
setlblStarted x = new setlblStarted(ClientUI.setlblStarted_txt);
x(number.ToString());
}
}
}
From what I can understand (and please correct me if I'm wrong), I need to create a new instance of setlblStarted_txt, point that new instance at method setlblStarted_txt, but the issue is currently ClientUI.setlblStarted_txt isn't static, and wants an object reference.
I've tried using ClientUI c = new ClientUI();, but that doesn't work (because it's creating a new instance of the form?)
What am I doing wrong, and if possible, can you help me understand why?

In .Net 4.0, you can use actions:
if (InvokeRequired)
{
Invoke(new Action<string>(updateText), "some text");
}
else
{
updateText("some text");
}
Also, void updateText(string text) does not need to be static.

As I understand, you are trying to use MethodInvoker delegate to update your text. I suggest you to change this approach to simplify your code:
namespace csDinger3
{
public class Program
{
static ClientUI aForm;
static void Main()
{
aForm = new ClientUI();
aForm.Show();
}
// Some code that's not relevant
public static void updateText(Int32 number)
{
aForm.setlblStarted_txt(number.ToString());
}
public partial class ClientUI : Form
{
public void setlblStarted_txt(string text)
{
if (lblStarted.InvokeRequired)
{
Invoke(new EventHandler(delegate
{
lblStarted.Text = text
}));
}
else
{
lblStarted.Text = text;
}
}
You can achieve the same behaviour with using the ThreadPool or SynchronizationContext or Dispatcher (in WPF). Please see this tutorial for better understanding:
Beginners Guide to Threading in .NET: Part 5 of n
Understanding SynchronizationContext (Part I)
It's All About the SynchronizationContext

Related

Generic method for updating the cross-wire call error

I have a form where you need to make multiple updates using the method of thread-safe calls to Windows Forms controls
Excerpts of code that do this are repeated several times. Trying to refactor I created a sort of general method for updating.
I have a class to register the methods that will be used within the general metdo:
public class ListOfUpdateMethods
{
public delegate void Metodo();
private List<Metodo> MetodosPreAtualizacao;
private List<Metodo> MetodosAtualizacao;
public ListOfUpdateMethods()
{
this.MetodosPreAtualizacao = new List<Metodo>();
this.MetodosAtualizacao = new List<Metodo>();
}
public void AddMetodosPreAtualizacao(Metodo m)
{
this.MetodosPreAtualizacao.Add(m);
}
public void AddMetodosAtualizacao(Metodo m)
{
this.MetodosPreAtualizacao.Add(m);
}
public void ExecutaMetodosPreAtualizacao()
{
foreach (var m in this.MetodosPreAtualizacao)
m();
}
public void ExecutaMetodosAtualizacao()
{
foreach (var m in this.MetodosAtualizacao)
m();
}
}
The method:
//General method for updating all Controls as needed
private void UpdadeControl(ListOfUpdateMethods list, Control control)
{
//Execute required methods before updating the control
list.ExecutaMetodosPreAtualizacao();
if (control.InvokeRequired)
{
var up = new Updates(UpdadeControl);
Invoke(up, new object[] { list, control });
}
else
{
//Execute methods needed to update control
list.ExecutaMetodosAtualizacao();
}
}
When I want to update some control, what I do is:
ListOfUpdateMethods VariavelDeInstancia = new ListOfUpdateMethods();
UpdateStopXRayTimer.AddMetodosAtualizacao(MetodoComInstrucoesQUeAtualizaOControle);
UpdadeControl(VariavelDeInstancia, ControleASerAtualizado);
However, when the MetodoComInstrucoesQUeAtualizaOControle is called, an access exception occurs for theadins crossed. That is, the method does not run on the main treading.
An example of what you would have within this method would be:
void MetodoComInstrucoesQUeAtualizaOControle()
{
ControleASerAtualizado = "Text";
}
Does anyone have any tips on how to solve the described problem?
Because your code have bugs. Hint at below.
public class ListOfUpdateMethods
{
public delegate void Metodo();
private List<Metodo> MetodosPreAtualizacao;
private List<Metodo> MetodosAtualizacao;
public ListOfUpdateMethods()
{
this.MetodosPreAtualizacao = new List<Metodo>();
this.MetodosAtualizacao = new List<Metodo>();
}
public void AddMetodosPreAtualizacao(Metodo m)
{
this.MetodosPreAtualizacao.Add(m);
}
public void AddMetodosAtualizacao(Metodo m)
{
// change this code.
// this.MetodosPreAtualizacao.Add(m);
this.MetodosAtualizacao.Add(m);
}
public void ExecutaMetodosPreAtualizacao()
{
foreach (var m in this.MetodosPreAtualizacao)
m();
}
public void ExecutaMetodosAtualizacao()
{
foreach (var m in this.MetodosAtualizacao)
m();
}
}
I suggest you can change your UpdadeControl method to below.
private void UpdadeControl(ListOfUpdateMethods list, Control control)
{
// this method can't added the UI operate.
// If you added the UI operate delegate. It will throw the exception.
list.ExecutaMetodosPreAtualizacao();
if (control.InvokeRequired)
{
// you can use Action delegate. Action delegate is so good.
var action = new Action<ListOfUpdateMethods, Control>(UpdadeControl);
control.Invoke(action, new object[] { list, control });
}
else
{
//Execute methods needed to update control
list.ExecutaMetodosAtualizacao();
}
}

Best way to add items to a log window from a Class

I'm trying to add a "log" message from my class to a ListBox on my form. Within the form I would just be able to use lblog.add("message"), but as I'm trying to clean up my code, what is the best way to pass the "message" to the front end?
I found a suggestion that has the code below, but wondering if there is a simpler way?
Form:
// This is all required so that we can call the function from another class
public void publicLogMessage(string message)
{
if (InvokeRequired)
{
Invoke(new OutputDelegate(logMessage), message);
}
}
public delegate void OutputDelegate(string message);
public void logMessage(string message)
{
lblog.Items.Add(DateTime.Now + " " + message);
}
Class:
//This is required so that we can call the "PublicLogMessage" function on the main form
public frmMain formToOutput;
public speechRecognition(frmMain f)
{
formToOutput = f;
}
Usage:
formToOutput.logMessage
You now have a pretty tight coupling between your algorithm and your ouput method. Your algorithm knows all about your output method (for example that it's a form with a specific signature).
I would suggest decoupling it:
private readonly Action<string> log;
public speechRecognition(Action<string> log)
{
this.log = log;
}
public void DoWork()
{
this.log("work started");
// ...
this.log("work in progress");
// ...
this.log("work ended");
}
This class knows nothing about the logging method. It only knows it gets a string. The class controlling both the output method (form) and algorithm (class above) can then link them together:
var form = new YourFormWithLoggingWindow();
var algorithm = new speechRecognition(form.publicLogMessage);
Now the algorithm will log to the form. You could have called it using
var algorithm = new speechRecognition(Console.WriteLine);
and it would log to the console in a Console Application. The algorithm does not care and does not need your form to compile. It's independent. Your form does not know the algorithm either. It's independent, too.
You could even have unit testing that checks the logging:
var log = new List<string>();
var algorithm = new speechRecognition(log.Add);
algorithm.DoWork();
Assert.AreEqual(log.Count, 3);
Use if/else when using InvokeRequired, I don't think there are other optimizations at the moment.
public void publicLogMessage(string message)
{
if (InvokeRequired)
Invoke(new OutputDelegate(logMessage), message);
else
logMessage(message);
}
public delegate void OutputDelegate(string message);
private void logMessage(string message)
{
lblog.Items.Add(DateTime.Now + " " + message);
}
private void listboxlrm(byte[] text)
{
if (this.listBox2.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(listboxlrm);
this.Invoke(d, new object[] { text });
}
else
{
byte[] convert = new byte[text[4]];
Array.Copy(text, 6, convert, 0, text[4]);
string yourtext = System.Text.Encoding.UTF8.GetString(convert);
this.listBox2.Items.Insert(0, string.Format(yourtext));
}
}
I am using that method.... If you use insert always add the top.

C# callbacks into windows forms

I'm trying to create a class (in the context of a Windows Application) that can update progress (or send some user message) back to the main form UI via delegates. The problem I have is that the compiler won't allow any of the constructs I attempt because of missing object references. This has been discussed here but no answers had to do with writing to an object on a Form.
in c++ I would do this:
void LogToUI(string s)
{
if(Form1)
Form1->update(s);
}
void TForm1::update(string s)
{
listbox->Items->Add(s);
}
// so that any function, anywhere, can update the listbox (thread safety aside)
in C#:
namespace test
{
public delegate void callback(String msg);
public partial class Form1 : Form
{
public void writeToListbox(String s)
{
listbox.Items.Add(s);
}
public static void writeToConsole(String s)
{
System.Console.WriteLine(s);
}
public void createclass
{
callback ui_update = writeToConsole; // this is ok
callback ui_update = writeToListbox; // not allowed
someclass SC = new someclass(ui_update);
}
}
class someclass
{
callback cb;
void someclass(callback T)
{
this.cb = T;
}
void logthis(string s)
{
cb("it's me!");
}
}
}
I understand the problem with having to assign a static method to the delegate, and the Form1 method is non-static. I would like to use the delegate method because it seems the cleanest; I just can't find a way to write this in such a way as to make it work, short of passing a pointer back to the Form, which seems messy.
I believe I just came across the answer. You have to expose a static reference to a UI object, in this case a ListBox. Then you can assign the callback delegate to a function that makes sure the listbox reference is not null. You just need to make sure you assign the static reference when the form is created:
namespace test
{
public delegate void callback(String msg);
public partial class Form1 : Form
{
public static ListBox callbackListBox; // add this
public void writeToListbox(String s)
{
if(null == callbackListBox)return; // add this check
// also make this threadsafe:
if (callbackListBox.InvokeRequired)
{
callbackListBox.Invoke(new MethodInvoker(() => { writeToListbox(s); }));
}else{
callbackListBox.Items.Add(s);
callbackListBox.TopIndex = callbackListBox.Items.Count - (callbackListBox.Height / callbackListBox.ItemHeight);
}
}
public static void writeToConsole(String s)
{
System.Console.WriteLine(s);
}
public void createclass
{
callback ui_update = writeToListbox; // now OK
someclass SC = new someclass(ui_update);
}
// and add this to the form's constructor:
public Form1()
{
InitializeComponent();
callbackListBox = listbox1;
}
}
class someclass
{
callback cb;
void someclass(callback T)
{
this.cb = T;
}
void logthis(string s)
{
cb("it's me!");
}
}
}
I still have to try this, but at least the compiler is not complaining.

Update a winform from an Interface callback from another class and thread

I have an winform and an interface callback that continuously sends updates. I want to be able to update a label1.Text from the callback interface. However since the intrface runs on an seperate thread I do not think i can do it directly so I was trying to use a delegate and invoke.
However I am running into an error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created - at
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
Here is the full code.
public partial class Form1 : Form
{
MyCallBack callback;
public delegate void UpdateDelegate(string myString);
public UpdateDelegate myDelegate;
public Form1()
{
InitializeComponent();
myDelegate = new UpdateDelegate(UpdateDelegateMethod);
callback = new MyCallBack(this);
CallBackInterfaceClass.SetCallBack(callback);
callback.OnUpdate();
}
public void UpdateDelegateMethod (String str)
{
label1.Text = str;
}
}
class MyTestCallBack : Callback
{
public Form1 form1;
public SomeObject so;
public MyTestCallBack(Form1 form)
{
this.form1 = form;
}
public void OnUpdate(SomeObject someobj)
{
so = someobj;
OnUpdate();
}
public void OnUpdate()
{
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
}
}
Two questions.
Can anyone explain what I am doing wrong?
Is this actually best method to do this?
Here is the answer based on the reply by bokibeg (see below) with a couple of modifications to make it work:
public partial class Form1 : Form {
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action(() =>
{
callback_MyTestCallBackEvent(sender, e);
}));
return;
}
label1.Text = e.SomeObject.GetDisplayString();
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(this, e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
} }
Here's what I'd do:
public partial class Form1 : Form
{
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
// TODO: Invoke code here with e.g. label1.Text = e.SomeObj.ToString()...
}
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
public MyTestCallBack()
{
//
}
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
}
}
It separates the GUI logic from whatever that thread is doing. It fires an event and it's Form's duty to do whatever it pleases with it.
I'm not sure if this compiles, I wrote it in text pad. Tell me if you have questions.
You probably just learned about delegates and got carried away with it, this is similar as it uses an event but the event is properly placed in the "back end" logic - form may or may not use it. Also notice how form's code is a lot better, it doesn't have so much boilerplate code just to implement some background service class.
Note however that MyTestCallBackEvent event may keep firing even after you close the form so make sure you unsubscribe from it when the form closes or disposes (or whenever you feel like form doesn't need it anymore).
Oh and I almost forgot: the original error you were getting is because you called Invoke when one wasn't required and Form definitely wasn't ready for it. Read this question to see how to safely invoke controls.

C# multithreaded throbber form

Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.
The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.
Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:
the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
public void CheckData()
{
try
{
ProgressBar pb = new ProgressBar();
pb.ShowProgressBar();
//do data checking here
pb.CloseForm()
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static Thread ms_oThread = null;
public bool shouldStop = false;
static ProgressBar ms_ProgBar = null;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
public void ShowForm()
{
ms_ProgBar = new ProgressBar();
Application.Run(ms_ProgBar);
}
public void CloseForm()
{
ms_ProgBar.Close();
}
public void ShowProgressBar()
{
// Make sure it is only launched once.
if (ms_ProgBar != null)
return;
ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
{
System.Threading.Thread.Sleep(1000);
}
}
}
You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.
You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.
Also, ShowProgressBar should be static.
Here's a rewrite attempt:
public class Main
{
public void CheckData()
{
try
{
ProgressBar.ShowProgressBar();
//do data checking here
ProgressBar.CloseForm();
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static ProgressBar _progressBarInstance;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
static void ShowForm()
{
_progressBarInstance = new ProgressBar();
Application.Run(ms_ProgressBar);
}
public static void CloseForm()
{
_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
_progressBarInstance= null;
}
public static void ShowProgressBar()
{
// Make sure it is only launched once.
if (_progressBarInstance != null)
return;
var ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
}
}

Categories