this.Invoke(...) - is this bad practice? - c#

I have a function called ExecuteCommand that does things based on a user's input. These things can range from simply doing a Console.Writeline(), checking a check box on my form, or simulating keystrokes to another process, completely independent from my own. The function runs on a separate thread, so changing the UI will requiring some invoking. I have 2 ways of doing it... one of which I'm not sure is a good way but it's very easy.
Code below, the 3rd line is what I have a question with:
private void ExecuteCommand()
{
this.Invoke((MethodInvoker)delegate()
{
if (current_line_index < command_que.Count)
{
current_line = command_que[current_line_index];
if (current_line.StartsWith(">>Auto Enter"))
{
chkAutoEnter.Checked = false;
}
else if (current_line.StartsWith("+WinWait("))
{
string title_to_wait_for = current_line;
title_to_wait_for = title_to_wait_for.Remove(0, "+WinWait(\"".Length);
title_to_wait_for = title_to_wait_for.Remove(title_to_wait_for.Length - 2, 2);
t_WinWait = new Thread(() => WinWait(title_to_wait_for));
t_WinWait.Name = "WinWait";
t_WinWait.Start();
}
}
});
}
The code works perfectly... but I am not sure if it's good practice.
Alternativly, I know I can do something like this to change the UI:
private delegate void CheckCheckBoxHandler(bool checked);
private void CheckCheckBox(bool checked)
{
if (this.chkAutoEnter.InvokeRequired)
{
this.chkAutoEnter.Invoke(new CheckCheckBoxHandler(this.CheckCheckBox), checked);
}
else
{
chkAutoEnter.Checked = checked;
}
}
But as I have multiple controls on my form that will be changed from another thread, I'd have to add a bunch of functions to do that, versus the simple method in the first example.
Is the first way bad in anyway? Are there any risks involved I haven't come across yet? It seems to good to be true...
Thanks!

No it's not bad. It doesn't matter which control that you call Invoke on since they all have the same effect. Invoke calls the delegate on the thread that owns the control - as long as all your controls are owned by the same thread, then there is no difference.

Related

Is there a way to await a flag change in a function?

I've attempted to make a simple step mode for an algorithm I'm running, and here is how it looks like:
public async Task<bool> AStarAlgorithmAsync(PFSquare curr = null)
{
// some algorithm code here
foreach(var square in Sorroundings)
{
if (SteppedMode)
{
await Task.Run(Pause);
}
if (await AStarAlgorithmAsync(square))
{
return true;
}
}
}
In my application, I have a Boolean called SteppedMode that decides if the algorithm should run one iteration per click event.
Pause() looks like this:
private void Pause()
{
while (!ContinueStep) { }
ContinueStep = false;
return;
}
And in another part of my (GUI) application I have an event which sets the boolean ContinueStep to true which in theory should end the while loop and continue the algorithm function. Currently this bit of code locks my GUI thread up and I'm almost certain there is a better way to do this.
I'm trying to get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm. Is there an easier and cleaner way to do this?
(This is a GUI application, not a console application.)
Your property is moonlighting as a method.
It makes no sense to set a property, to then have that property revert back to its original state immediately. As a consumer, I would be majorly confused by that behavior. Think about this code:
var myObj = new MyObject();
myObj.MyBoolean = true;
Console.WriteLine(myObj.MyBoolean); // FALSE!?
It just doesn't make sense.
The only effect you want to trigger by setting this property is to execute some code. That's exactly what methods are supposed to be used for:
public void ContinueStep()
{
Console.WriteLine("I did some work");
}
So instead of this:
myObj.ContinueStep = true;
you should be doing this:
myObject.ContinueStep();
This doesn't lock up your UI thread, while also being a lot more sensical to your consumer. The method suggests that some action will be taken (which may or may not lead to state changes in the object - that's a contextual expectation).
Infinite recursion
As an aside; based on your code, AStarAlgorithmAsync is a recursive function, and seemingly infinitely so. There doesn't seem to be an ending condition.
Every recursive level will interate over the first surrounding and then trigger the next level, which again will interate over the first surrounding and then trigger the next level, which again ...
That can't be right, but it's unclear to me how to fix it as the bigger picture is not explained in your question
A simple implementation
What I'm trying to do is get my algorithm function to run one iteration, wait for a click from the user and only then continue running the algorithm, is there an easier and cleaner way to do this?
A simple example of such a thing:
private int _index = 0;
private List<object> _myList = ...; // assume this list contains some elements
public void ProcessNextObject()
{
if(_index < _myList.Length)
{
Process(_myList[_index]);
_index++;
}
}
private void Process(object o)
{
Console.WriteLine("Processing this object!");
}
You can then hook up your click event to call ProcessNextObject().
Note that in this example, the list is processed once and cannot be processed again. By manipulating the index value, you can change that behavior as you like.

Threading: Waiting for window to open to do action

I have written a Window Manager for my program, which keeps certain windows open for the life of the Program (on background threads) (if the user wants them open).
I just implemented an action for the contacts window. The problem is that, the action works when the window is already open, but if the action is invoked when the window isn't open yet, then the window opens, but the action is not carried out (pressing the button again will carry out the action).
the code:
private static SetupContacts _contactsWindow;
private static Thread _contactthread;
public static void ShowContact(repUserObject uo, ContactFormAction action, int contactID)
{
if (_contactsWindow == null)
CreateContactThread(uo, contactID);
// make sure it is still alive
if (!_contactthread.IsAlive)
CreateContactThread(uo, contactID);
if (_contactsWindow != null)
{
_contactsWindow.BringToFront();
_contactsWindow.Focus();
switch (action)
{
case ContactFormAction.ViewContact:
if (contactID > 0)
_contactsWindow.LoadCustomer(contactID); // load the contact
break;
case ContactFormAction.AddNewContact:
_contactsWindow.AddCustomer();
break;
}
}
}
private static void CreateContactThread(repUserObject uo, int contactID)
{
if (_contactthread == null || !_contactthread.IsAlive)
{
_contactthread = new Thread(delegate()
{
_contactsWindow = new SetupContacts(uo, contactID);
_contactsWindow.CerberusContactScreenClosed += delegate { _contactsWindow = null; };
_contactsWindow.CerberusContactHasBeenSaved += delegate(object sender, ContactBeenSavedEventArgs args)
{
if (CerberusContactHasBeenSaved != null)
CerberusContactHasBeenSaved.Raise(sender, args);
};
Application.EnableVisualStyles();
BonusSkins.Register();
SkinManager.EnableFormSkins();
UserLookAndFeel.Default.SetSkinStyle("iMaginary");
Application.Run(_contactsWindow);
});
_contactthread.SetApartmentState(ApartmentState.STA);
_contactthread.Start();
}
}
What happens when the routine runs for the first time, (by calling ShowTime), that it hits the first if statement and goes to CreateContactThread() routine. That does it job, but when it returns, the _contactsWindow is still null. The next time the routine is called (ie, call by pressing the button the second time), it all works fine as the _contactWindow is not null.
How do i get it to do it all in one go ?
I am in vehement agreement with commenter Blorgbeard, who advises that it's a bad idea to run more than one UI thread. The API itself works best when used in a single thread, and many of the kinds of actions and operations one might want to do in code with respect to the UI objects are most easily handled in a single thread, because doing so inherently ensures things happen in the order one expects (e.g. variables are initialized before being used).
That said, if for some reason you really must run your new window in a different thread, you can synchronize the two threads so that the initial thread cannot proceed until the new thread has gotten far enough for the operations you want to perform on the newly-initialized object to have a reasonable chance of success (including, of course, that object having been created in the first place).
There are lots of techniques for synchronizing threads, but I prefer the new TaskCompletionSource<T> object. It's simple to use, and if and when you update the code to use async/await, it will readily mesh with that.
For example:
public static void ShowContact(repUserObject uo, ContactFormAction action, int contactID)
{
CreateContactThread(uo, contactID);
if (_contactsWindow != null)
{
_contactsWindow.BringToFront();
_contactsWindow.Focus();
switch (action)
{
case ContactFormAction.ViewContact:
if (contactID > 0)
_contactsWindow.LoadCustomer(contactID); // load the contact
break;
case ContactFormAction.AddNewContact:
_contactsWindow.AddCustomer();
break;
}
}
}
private static void CreateContactThread(repUserObject uo, int contactID)
{
if (_contactthread == null || !_contactthread.IsAlive)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
_contactthread = new Thread(delegate()
{
_contactsWindow = new SetupContacts(uo, contactID);
_contactsWindow.CerberusContactScreenClosed += delegate { _contactsWindow = null; };
_contactsWindow.CerberusContactHasBeenSaved += delegate(object sender, ContactBeenSavedEventArgs args)
{
if (CerberusContactHasBeenSaved != null)
CerberusContactHasBeenSaved.Raise(sender, args);
};
_contactsWindow.Loaded += (sender, e) =>
{
tcs.SetResult(true);
};
Application.EnableVisualStyles();
BonusSkins.Register();
SkinManager.EnableFormSkins();
UserLookAndFeel.Default.SetSkinStyle("iMaginary");
Application.Run(_contactsWindow);
});
_contactthread.SetApartmentState(ApartmentState.STA);
_contactthread.Start();
tcs.Task.Wait();
}
}
Notes:
You had what appears to me to be redundant checks in your code. The CreateContactThread() method itself checks for null and !IsAlive, and restarts the thread if either of those are false. So in theory, by the time that method returns, the caller should be guaranteed that everything has been initialized as desired. And you should only have to call the method once. So I changed the code to do just that: call the method exactly once, and do so unconditionally (since the method will just do nothing if there is nothing to do).
The calling thread will wait in the CreateContactThread() method after starting the new thread, until the new window's Loaded event has been raised. Of course, the window object itself has been created earlier than that, and you could in fact release the calling thread at that time. But it seems likely to me that you want the window object fully initialized before you start trying to do things to it. So I've delayed the synchronization to that point.
As Blorgbeard has noted, one of the risks of running UI objects in multiple threads is that it's harder to access those objects without getting InvalidOperationExceptions. Even if it works, you should not really be accessing _contactsWindow outside of the thread where it was created, but the code above does just that (i.e. calls BringToFront(), Focus(), LoadCustomer(), and AddCustomer() from the original thread). I make no assurances that the code above is actually fully correct. Only that it addresses the primary synchronization issue that you are asking about.
Speaking of other possible bugs, you probably have an unresolved race condition, in that the new contacts-form thread might be exiting just as you are checking its IsAlive property. If you check the property just before it exits, but then try to access the thread and/or the window after it has exited, your code is likely to do something bad (like crash with an exception). This is yet another example of something that would be a lot easier to address if all of your UI objects were being handled in a single thread.
I admit that some of the above is speculative. It's impossible for me to say for sure how your code will behave without seeing a good, minimal, complete code example. But I feel the likelihood of all of the above being accurate and applicable is very high. :)

C# Trouble Using Safe Thead or Background Worker

Fairly frustrating since this seems to be well documented and the fact that I accomplished this before, but can't duplicate the same success. Sorry, I'll try to relate it all clearly.
Visual Studio, C# Form, One Main Form has text fields, among other widgets.
At one point we have the concept that we are "running" and therefore gathering data.
For the moment, I started a one second timer so that I can update simulated data into some fields. Eventually that one second timer will take the more rapid data and update it only once per second to the screen, that's the request for the application right now we update at the rate we receive which is a little over 70 Hz, they don't want it that way. In addition some other statistics will be computed and those should be the field updates. Therefore being simple I'm trying to just generate random data and update those fields at the 1 Hz rate. And then expand from that point.
Definition and management of the timer: (this is all within the same class MainScreen)
System.Timers.Timer oneSecondTimer;
public UInt32 run_time = 0;
public int motion = 5;
private void InitializeTimers()
{
this.oneSecondTimer = new System.Timers.Timer(1000);
this.oneSecondTimer.Elapsed += new System.Timers.ElapsedEventHandler(oneSecondTimer_elapsed);
}
public void start_one_second_timer()
{
run_time = 0;
oneSecondTimer.Enabled = true;
}
public void stop_one_second_timer()
{
oneSecondTimer.Enabled = false;
run_time = 0;
}
Random mot = new Random();
private void oneSecondTimer_elapsed(object source, System.Timers.ElapsedEventArgs e)
{
run_time++;
motion = mot.Next(1, 10);
this.oneSecondThread = new Thread(new ThreadStart(this.UpdateTextFields));
this.oneSecondThread.Start();
}
private void UpdateTextFields()
{
this.motionDisplay.Text = this.motion.ToString();
}
motionDisplay is just a textbox in my main form. I get the Invalid Operation Exception pointing me towards the help on how to make Thread-Safe calls. I also tried backgroundworker and end up with the same result. The details are that motionDisplay is accessed from a thread other than the thread it was created on.
So looking for some suggestions as to where my mistakes are.
Best Regards. I continue to iterate on this and will update if I find a solution.
Use a System.Forms.Timer rather than a System.Timers.Timer. It will fire it's elapsed event in the UI thread.
Don't create a new thread to update the UI; just do the update in the elapsed event handler.
Try this
private void UpdateTextFields()
{
this.BeginInvoke(new EventHandler((s,e)=>{
this.motionDisplay.Text = this.motion.ToString();
}));
}
This will properly marshall a call back to the main thread.
The thing with WinForm development is that all the controls are not thread safe. Even getting a property such as .Text from another thread can cause these type of errors to happen. To make it even more frustrating is that sometimes it will work at runtime and you won't get an exception, other times you will.
This is how I do it:
private delegate void UpdateMotionDisplayCallback(string text);
private void UpdateMotionDisplay(string text) {
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.motionDisplay.InvokeRequired) {
UpdateMotionDisplayCallback d = new UpdateMotionDisplayCallback(UpdateMotionDisplay);
this.Invoke(d, new object[] { text });
} else {
this.motionDisplay.Text = text;
}
}
When you want to update the text in motionDisplay just call:
UpdateMotionDisplay(this.motion.ToString())

Show text on a label for a specific time

I want to show a text in a label for a particular time so I did a search on google and I found these two solutions :
The first solution is :
public void InfoLabel(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(InfoLabel), new object[] { value });
return;
}
barStaticItem3.Caption = value;
if (!String.IsNullOrEmpty(value))
{
System.Timers.Timer timer =
new System.Timers.Timer(3000) { Enabled = true };
timer.Elapsed += (sender, args) =>
{
this.InfoLabel(string.Empty);
timer.Dispose();
};
}
}
The second solution :
private void ShowTextForParticularTime(String caption)
{
Timer t = new Timer { Interval = 5000, Enabled = true};
t.Tick += (sender, args) => OnTimerEvent(sender, args, caption);
}
private void OnTimerEvent(object sender, EventArgs e, String caption)
{
barStaticItem3.Caption = caption;
}
Could you please tell me the deffrence between the two solutions, and why we use this symbole "=>" , also I understood nothing from this line :
if (InvokeRequired)
{
this.Invoke(new Action<string>(InfoLabel), new object[] { value });
return;
}
Okay, there is a good amount to explain here.
There is no major differences between the two options you have shown. The reason they look different is because the first id declaring a delegate method (lambda expression) inside the public method, while the second is just creating an event handler. They do almost the exact same thing. Infact you can see that in the delegate method you have the tradition event handler prameters (object sender, EventArgs e). Personally I prefer the second solution because it looks cleaner to me.
Invoke Required is used to handle threading. In C# errors will be thrown if a thread that didn't create a visual object tries to alter the visual object. To get around this we make a call to the thread that created the visual object by calling "Invoke". The "InvokeRequired" property just tells us if the current thread did not create the visual object. You should always use this when you are threading or making delegate methods (because you can't control the thread that runs them.)
I hope this brief explanation helps. Comment if it is unclear
In WinForms and WPF, the UI can only be updated from the thread that created the control in question. These two approach show two ways to update your UI from a different thread.
The first approach manually checks if the code is running on a different thread and, if it is, marshals the call to the UI thread.
The second approach uses an event, leaving the details of marshaling to .NET
The symbol => represents a lamda expression. You can think of it much like a function pointer (though sometimes it is really something called an expression tree behind the scenes). Essentially, it creates a variable that points to code that can be called by referencing that variable.
Either approach should work fine. Personally I prefer the second approach because it allows the framework to handle more of the plumbing work.

C# winform backgroundworker

I am currently working on a home project for myself.
The program is written in C# using winforms.
The problem I'm currently experiencing is as followed:
I have a listview in my mainform called lvwGames
When I run the program without debugging, it runs fine.
However when I start with a debug, I get an error. This has something to do with the background worker thread.
Allow me to post some code to assist me.
private void MainViewLoad(object sender, EventArgs e)
{
RefreshGamesListView();
}
Nothing special here.
The reason I am calling RefreshGamesListView() is because I have to refresh on several occasions.
The method being called looks like this.
public void RefreshGamesListView()
{
pbRefreshGamesList.Value = 0;
bgwRefreshList.RunWorkerAsync();
}
So when the method is called, the background worker is called and runs the dowork method.
This one is quite big.
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
lvwGames.Items.Add(li);
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
When i run the code in debug, when the code is on lvwGames.Items.Add(li);
It gives me the following error:
Cross-thread operation not valid: Control 'lvwGames' accessed from a thread other than the thread it was created on.
I have absolutely no clue why.
I think it is code specific. But it can also mean I don't get the background worker completely, and specifically when to use it properly.
The reason I'm using it is because I'm loading a large large list from the database, I want to keep responsiveness in the UI when the list is loaded, and inform the users how far it is, using a progress bar.
If any code is missing, or you actually understand why this is happening PLEASE explain me why in this case its causing the error. You don't need to fix it for me. I just want to know WHY it's caused.
Thanks for taking the time to read this post. I hope to be able to continue using the debugger soon. :)
You need to call Conrol.Invoke when accessing visual controls from background threads.
if (_lvwGames.IsHandleCreated) {
Action addGameToList = () => {
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
....
_lvwGames.Items.Add(li);
};
if (_lvwGames.InvokeRequired) {
_lvwGames.Invoke(addGameToList);
} else {
addGameToList();
}
}
From Manipulating Controls from Threads
...For example, you might call a method that disables a button or
updates a display on a form in response to action taken by a thread.
The .NET Framework provides methods that are safe to call from any
thread for invoking methods that interact with controls owned by other
threads. The Control.Invoke method allows for the synchronous
execution of methods on controls...
This is because you're attempting to access a UI control (lvwGames) from a background thread. The way to make it work requires you to marshal the information back to the main UI thread and update the control from there:
private void BgwRefreshListDoWork(object sender, DoWorkEventArgs e)
{
List<Game> games = _mainController.RetrieveAllGames();
int count = 1;
foreach (Game game in games)
{
string id = game.id.ToString();
var li = new ListViewItem(id, 0);
li.SubItems.Add(game.title);
li.SubItems.Add(game.Genre.name);
li.SubItems.Add(game.Publisher.name);
li.SubItems.Add(game.Platform.name);
li.SubItems.Add(game.CompletionType.name);
li.SubItems.Add(game.gameNotice);
// This is the new line you need:
lvwGames.Invoke(new MethodInvoker(delegate { lvwGames.Items.Add(item) }));
double dIndex = (double)(count);
double dTotal = (double)games.Count;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
count++;
bgwRefreshList.ReportProgress(iProgressPercentage);
}
}
Normally you would check the InvokeRequired property first as mentioned in other answers, but there is really no need if you are always calling it from the background thread. Your DoWork method will always require an invoke call, so you might as well just go ahead and write it like that.
This happening cause, just like compiler cliams, you are going to update UI control content from another thread. You can not do that, as UI control can be updated only within main thread.
Please have look on this SO answer with example code provided:
Invoke from another thread
The background worker is not working properly if you run in debug mode in studio. If you have calls that use the windows handle to retrieve messages, then they will fail. If you for instance have a progressChanged event handler and this changes a text in a textbox that might fail.
I had this scenario: A Form that has a background worker. If I just start the worker without getting a dialog box up first then it works ok. If I show a dialog and then start the background worker then it fails. When I run the program normally it does not fail. It is somehow the debug environment that destroys the link between the events and the foreground window. I have changed my code to use invoke, and now all works both in when running in release and when I debug.
Here is a link explaining what can be done to make a program thread safe.
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
I did not do the same as the sample to microsoft. I made delegates, assigned to the functions I needed to run. and called invoke on them.
sample pseudo code:
class MyClassWithDelegates
{
public delegate void ProgressDelegate( int progress );
public ProgressDelegate myProgress;
public void MyProgress(int progress)
{
myTextbox.Text = ..... ; // this is code that must be run in the GUI thread.
}
public MyClassWithDelegates()
{
myProgress = new ProgressDelegate(MyProgress);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Invoke( myProgress, e.ProgressPercentage );
}
}
All code that potentially have to be run in the GUI thread of the application must be Invoked to be safe.

Categories