Winforms and Background Worker - c#

I have a console app at the moment, which monitors a folder for files, and then based on rules and the file name, copies any new file to a location on the network.
I have a requirement to make the application more pretty, so decided to go with a simple WinForms single form application which displays status and 'last updated file' type information.
The console app was written in such a way that all Console display information went through a single method, which I called 'Notify', taking two parameters. A string to display the information I want the user to see, and an ErrorLevel Enum, which, if 'Normal' displayed in green text, if Warning, was yellow, and if error, was red. But the point is, all my code just did was use the 'Notify' method to output any text.
I want to change my console app into a normal class, run it as a background worker from the WinForms project, and have the Notify method in the thread send updates to the winforms app, safely. I think it can be done with events, but I am not sure what would be the best way to handle this. Could you propose a method to get this working?
There's the 'Invoke' way of doing things. Is it good? Something like:
this.BeginInvoke (new MethodInvoker(() => UpdateLabel(s));
It seems it would be basic, but I'd like to still make use of my Notify method, and have that send messages to the UI layer.
I also need the console app to send messages to the thread. For example, 'Stop', where I then run code that gracefully quits the thread... and also, 'Refresh', which does some logic within the thread.
Another option is to run the processing class as a service? And then have a UI that somehow connects to the system service and gets updates? I have never done anything like that, but the process is meant to run all the time...
At the moment, I have my code running, but no updated to the UI:
public partial class MainForm : Form
{
BackgroundWorker _bw = new BackgroundWorker();
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
_bw.DoWork += bw_DoWork;
_bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
var bw = sender as BackgroundWorker;
var monitor = new Monitor();
monitor.RunMe();
}
}
I think I just need to find a way to get my Notify method in my thread to send a message or something (An object that I create, with Message String and ErrorCode properties?) back to my UI, and process it safely on the UI.
And here is the code within the class (thread)...
public class Monitor
{
public void RunMe()
{
Notify("Checking for network connectivity...", Constants.ErrorLevel.Information);
if (FileManagement.FolderExists(Constants.FolderToMonitor) == false)
{
Notify("Unable to monitor folder - Aborting.", Constants.ErrorLevel.Error);
Console.ReadKey();
return;
}
Notify("OK", Constants.ErrorLevel.Information);
....
}
Note: Readkey will be removed..

There may be a better approach, but if your Notify method needs to interact with the GUI, try using the ReportProgress event on the BackgroundWorker. You can pas an object as the state parameter, and probably just ignore the progress value.

Related

Make main thread execute code on button press after form.show

I have a piece of code that does some calculations and then calls the form.show command. Now I have a library (the revit api) that does not allow me to store variables in a project without being in the main thread.
The logical solution for this is to get the spawned thread to call the main thread using say a producer/consumer pattern with code looking a bit like this:
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
Thread.sleep(100);
}
However when I do this the gui does not load fully (black background, no text in buttons ext.).
I have also tried doing this using the evoke method
private void BtnOK_Click(object sender, System.EventArgs e)
{
Commit();
Invoke(Commit);
}
private void Invoke(Action commit)
{
commit.Invoke();
}
However this just tells me that it's not the main thread that's executing the commit function.
Is there another way to do this or am I just making an error.
Just to be clear I have a form.show(owner) command that throws an error if it's not executed by the main thread. I also have a commit() function that must be excused by the main thread or it throws an error. The execution must wait until a button press. But the main thread polling the gui thread for changing causes the program to hang. According to my google search it' s also possible to do something involving an external event to get back into the right context but the example given was using python to invoke c# code, is there a good way to raise an external event to get back into a given thread in c#?
Edit: based on some suggestions I have created the following code:
public class ThreadManager
{
static List<ThreadAble> orders = new List<ThreadAble>();
public static bool running = false;
public static void execute(ThreadAble action)
{
orders.Add(action);
}
static System.Timers.Timer timer;
public static void RegisterAPIThreadAndHold(ExternalCommandData commandData)
{
UIApplication uiapp = commandData.Application;
uiapp.Idling += Application_Idle;
}
private static void Application_Idle(Object o,IdlingEventArgs e)
{
if (orders.Count != 0)
{
ThreadAble f = orders.First();
orders.Remove(f);
f.execute();
}
}
}
public interface ThreadAble {
void execute();
}
However this does not appear to actually run when I use it as
public override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
Form frm = new OverviewForm(ExternalCommandData commandData);
frm.show()
ThreadManager.RegisterAPIThreadAndHold(commandData);
ThreadManager.Execute(new run_ThrowError())
where ThrowError.execute() is
Throw new Exception(" this is actually being executed" );
Your first example could work if you will replace Thread.Sleep by the System.Windows.Forms.Application.DoEvents(). It should give time to paint GUI and do not froze application completly.
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
{
System.Windows.Forms.Application.DoEvents();
// Thread.sleep(100);
}
}
But this is not perfect solution to achieve this.
Better would be calling Dispatcher.Invoke command inside your dialog to perform MainThread operations.
You can use i.e. GalaSoft library - please refer to DispatcherHelper object documentation and samples.
The two ways to do this I'm aware of are with the External Event or the Idling event.
With the idling event, you'll register it, and while it is registered, your code (in the main thread) will get a callback from Revit every time that it's not busy with something else. Often a few times per second.
Once you are in the Idling callback, then you're able to create transactions and interact with the model. So your callback checks the state of the form and decides whether there is something to do.
The External Event works similarly in terms of registration, but you're able to request a trigger of the callback.
Jeremy Tammik must have 20 posts on thebuildingcoder.typepad.com on Modeless dialog / Revit stuff.
For a simple solution to this, please refer to the Revit SDK ModelessDialog ModelessForm_ExternalEvent sample application. It demonstrates exactly what you are asking for.

Dealing with user interface inside a thread (Compact-Framework)

We want to display a "Loading, please wait..." screen before the application starts executing other functions on a Win CE 5.0 powered device. The application is being developed using .NET Compact Framework 3.5 (C#).
The issue is that, the UI is only updated once the set of current processes are completed, to a stage where other functions will only run with user interaction. C# has a Form.Shown() event, which would allow the application to run other functions, only ONCE the form has been displayed. However, the Compact-Framework does NOT include.
The solution I've approached was using multi-threading, where I would have one thread which would display the "Loading, please wait..." screen, and the other would take care of other function.
The issue with Threading is, when managing UI components, it gives an error if not applied the correct techniques:
public Form1()
{
InitializeComponent();
ThreadStart tstart = new ThreadStart(showLoad);
Thread t1 = new Thread(tstart);
Thread t2 = new Thread(checkConnect);
t1.Start();
t2.Start();
}
private void checkConnect()
{
conn.portConnect(); //Connects to port Successfully
if (conn.isConnected == true) //Variable which checks the connectivity status
{
panelMENUshow();
}
else
{
panelCONFIGshow();
}
}
private void showLoad()
{
if (imgLoading.InvokeRequired)
{
imgLoading.Invoke((Action)(() => imgLoading.Show())); //Image box displaying "Loading, please wait..." shows successfully if I use Control.Invoke
}
//If I don't use Control.Invoke I get the following error:
//"Control.Invoke must be used to interact with controls created on a separate thread."
}
On the showLoad() function, it is not a problem to add Control.Invoke. However, the functions panelMENUshow() and panelCONFIGshow() contains many bits of code that manages UI components, and it would be not practical to use Control.Invoke in every line of code referring to a UI component.
Is there a way of:
stopping the threading but carry on running the code that involves UI management?
practically manage UI components within a thread?
PS: The approach was based on the answer of this post.
Use the refresh function to force the control to be immediately redrawn.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.refresh(v=vs.90).aspx
When writing WinForm apps you want to keep the complexities of threading away from the main UI thread. I've seen too many applications with thread code weaving in and out of them, tying the logic into a knot.
Instead, model anything that requires any kind of threading as a component that, while it may use threads internally, presents a friendly, even-based model to the outside world. These components should not have any direct interaction with your UI.
Given that, your code would look something more like this (just an example, not syntax checked and probably with lots of typos etc, but it will give you an idea):
public class CommunicationObject
{
// you will probably have several EventArgs to define to pass extra info
public event EventHandler<EventArgs> Connected;
// you need this instance to dispatch events to the UI thread
private Control _invoker;
public CommunicationObject(Control invoker)
{
_invoker = invoker;
// start a thread here, or better yet, add an Enabled property or
// Start method to kick it off
}
// from the thread that is doing the real work, call this when you are connected
private void OnConnected()
{
_invoker.Invoke(() =>
{
EventHandler<EventArgs> handler = Connected;
if (handler != null)
{
handler(this, EventArgs.Empty); // eventually you might need your own event args
}
});
}
}
public class Form1 : Form
{
private CommunicationObject _comm;
public Form1()
{
InitializeComponent();
imgLoading.Show(); // show msg until connected
_comm = new CommunicationObject(this);
_comm.Connected += Comm_Connected; // wire up event handler
}
private void Comm_Connected(object src, EventArgs e)
{
if (imgLoading.Visible)
{
imgLoading.Hide(); // hide once connected
}
panelMENUshow();
}
}
Note that the form has no threads in it at all. Push that complexity into stand-alone objects. It might seem like a bit more work, but you will thank yourself later!

Accessing WinForms Controls from a serial port in a class

First, the apology: I'm new to posting questions on this site, so I apologize for formatting or information errors.I have seen many answers to taking data from a serial port dropped on a form and using it to populate text boxes, graphs, etc. on the main form, using "Invoke" because the serial port is running in a different thread.
I am trying to "generalize" some comm stuff we use all the time in to a class (yes, the old VB6 programmer is trying to grow up :-) and I'm having issues. I can do some things if I force a form name in the main program.cs and use the same namespace for the class, but this sorta defeats the purpose. I've also tried adding an event on the "received" even of the serial port in the class to raise an event on the main form. The event tries to get raised but a cross thread exception occurs.
The code at this point is quite large, so I'll try to "outline" it. In simplistic form, assuming I have a for called "Form1" which contains a text box called textbox1 and a class called "SerialThing":
Form1:
SerialThing mySerialThing ;
Form1_Load:
mySerialThing = new SerialThing();
DisplayData()
Textbox1.Text = "You Got Data!";
SerialThing:
Static SerialPort myDevice;
Init()
myDevice = new SerialPort;
myDevice.DataReceived += new SerialDataReceivedEventHandler(devicePort_DataReceived);
devicePort_DataReceived()
this.Invoke(new EventHandler(DisplayData));
The above will work if the serial port is placed on the main form, but not if created inside the class.
Again, sorry if too complex, or too simplistic. I am looking for an "easy" way to do this, but keep the class "generalized" (ideally not have to have the workspace names match, etc).
-Vin
There are many, many ways to do this. I'll present the classic approach using a custom event, delegates, and Invoke(), as I think it's important to understand that process. Once you've got this down, you can jump to some of the newer approaches.
First, in your SerialThing() class, you declare a Custom event to pass out data when it is received:
class SerialThing
{
public delegate void DataReceivedDelegate(string data);
public event DataReceivedDelegate DataReceived;
static SerialPort myDevice;
public SerialThing()
{
myDevice = new SerialPort();
myDevice.DataReceived += new SerialDataReceivedEventHandler(myDevice_DataReceived);
}
void myDevice_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// ... grab the data and place into a string called "data" ...
string data = "";
// raise our custom event:
if (DataReceived != null)
{
DataReceived(data);
}
}
}
Now, over in Form1, you subscribe to that custom event when you create the instance of SerialThing. Additionally, when that event is received, you marshal the call from the secondary thread to the main thread using InvokeRequired, Invoke, and a delegate:
public partial class Form1 : Form
{
SerialThing mySerialThing;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
mySerialThing = new SerialThing();
mySerialThing.DataReceived += new SerialThing.DataReceivedDelegate(mySerialThing_DataReceived);
}
private delegate void DataReceivedDelegate(string data);
void mySerialThing_DataReceived(string data)
{
if (this.InvokeRequired)
{
this.Invoke(new DataReceivedDelegate(mySerialThing_DataReceived), new Object[] { data });
}
else
{
textBox1.Text = data;
}
}
}
EDIT: In response to your comments below...
Think of a delegate as simply a "pointer to a method". When you execute the delegate, the associated method gets run.
The InvokeRequired() portion determines if the code is running in a different thread than the one that created the control. In this case, the control is the Form itself (this). If true is returned, then the event was received in a different thread. We then proceed to this.Invoke() line inside the true portion of the If block. Again this refers to the Form. Thus the Form is requesting to Invoke ("run") the passed delegate on the thread that created it (the main UI thread). We create a instance of the delegate that actually points to the same method we are already in resulting in a recursive call. The second parameter is simply an array of Object used to pass the parameters along with the delegate.
When Invoke() is run we end up re-entering the method because of the recursive call. At this point, however, the InvokeRequired() check will return false as we are now running in the main UI thread. Therefore we drop down into the false portion of the If statement where we update the TextBox. In this pattern it is safe to update an GUI controls in the else block of the If statement.
Note that a recursive call isn't necessary here. This is simply a style choice. We could have instead used a second "helper" function that the delegate pointed to, and Invoked that instead. The recursive approach reduces the number of methods required.
This is perhaps the most verbose approach to solving this type of problem. I like it, though, as it shows the flow of events and data, and movement between the threads.
We could shorten all the Form code to just this, using anonymous delegates:
private void Form1_Load(object sender, EventArgs e)
{
mySerialThing = new SerialThing();
mySerialThing.DataReceived += delegate (string data)
{
this.Invoke((MethodInvoker)(delegate() { textBox1.Text = data; }));
};
}
I don't know about you, but as a former VB6 programmer myself, that just looks weird when you first see that type of thing.
I've also used components that I know have things running in different
threads, yet the "form code" has never had to use the delegate stuff,
so maybe there's something that can be buried into the class?
Yes, it's possible to bake some "magic" into a class so that it raises events already on the main UI thread, thus not requiring any Invoke() calls. One way to do this is thru using a SynchronizationContext.
Another possibility for approaching this type of problem would be to use a BackgroundWorker() control which has events such as ProgressChanged() and RunWorkerCompleted() that are raised in the main UI thread for you (they do the necessary invoking type stuff under the hood for you).

Cross-thread: Invoke before the dialog shown

Probably I don't understand correctly this topic. Here is an issue...
C# Windows Application (.NET 2.0). The MainForm has a button "Query". When the user pushes it, the following should happen:
private void btnQuery_Click(object sender, EventArgs e)
{
querier = new Querier();
OutputForm outputForm = new OutputForm();
querier.ProcessAll(outputForm.OutputReceived);
outputForm.ShowDialog();
}
Querier is the worker. It creates a background thread and runs it to do stuff. The OutputForm is a simple form with txtOutput multiline text-box that should display the output of the working thread.
To allow the working thread send its output, querier.ProcessAll() method receives a callback handler. This is its implementation:
public void OutputReceived(string message)
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate() { this.OutputReceived(message); });
else if (!string.IsNullOrEmpty(message))
txtOutput.AppendText(message + Environment.NewLine);
}
So basically the working thread runs and sends output using the OutputReceived() method, which uses Invoke(), because the working thread can't access the txtOutput field directly.
Note that outputForm.ShowDialog() is called AFTER querier.ProcessAll(). That's because ShowDialog() is blocking.
But here is the problem. If the working thread sends any output BEFORE the dialog is actually shown, I get the exception about cross-thread operation! When I debug it, I see that for some reason this.InvokeRequired() in the OutputReceived() method returns "false"! That's why the working thread tries to access txtOutput directly and crashes.
The problem is clearly about the race condition between the thread and ShowDialog(). If I add Thread.Sleep() in the beginning of the working thread, the dialog shows up and then everything works fine.
Can you explain such behavior?
The best thing to do here would be to ensure that the long running task doesn't actually start until the other form is first displayed. This isn't all that hard thanks to the Shown event in Form.
private void btnQuery_Click(object sender, EventArgs e)
{
querier = new Querier();
OutputForm outputForm = new OutputForm();
outputForm.Shown += delegate { querier.ProcessAll(outputForm.OutputReceived); };
outputForm.ShowDialog();
}
Sorry, I found the answer!
There is one special case when InvokeRequired() will return "false". It's when the control's handle hasn't been created yet. In this case it is forbidden to call Invoke() - so the InvokeRequired() tries to protect you, sort of.
Now I call CreateHandle() method in the CTOR of OutputForm. In this way the handle is created even before the dialog is shown, so InvokeRequired() works as expected.

Is this a good practice to handle the displaying of the progress of data processing in a Class library?

I developed a C# class library, some of their methods shows information of its processing progress because they read and write millions of records, and the user asked for knowing how the process is going and the time they should wait.
Using dependency injection to avoid the "if console app write progress on console else if WPF app display progress bar", (1) I have got the displaying on the console the time for every one million records processed if the method is invoked from a console application and (2) I have got the displaying a progress bar on a GUI if the method is invoked from a WPF application.
The question here is, is it a good practice what I am doing or, is there better/correct alternative to this matter?
My best regards.
Please don't do this. If you are building a class library, you should make zero assumptions about the UI is interacting with the user.
Your solution sounds like it might work if you have a console window or a WPF application, but what if it's being called from a website or inside a service? I've seen many a service get brought down beause some rogue class library was trying to display a dialog but there was nobody around to click OK.
The better solution is to simply raise an event whenever you want to report some progress, and let the consuming UI application worry how it wants to display that progress to the user.
See how the BackgroundWorker class works for a good model of this: http://msdn.microsoft.com/en-us/library/8xs8549b.aspx
I wouldn't expect a class library to display the progress itself. I'd expect it provide hooks - probably in the form of events - so that whatever using the class library can display that information in the most appropriate form.
Quite how much control you want to give over that (e.g. report to me on every item or every N items) is a matter you'll have to work out for yourself - but it should be fairly easy for a handler to work that sort of thing out for itself.
Here's an example of raising events, this code will go in your class that is doing the work on the background thread. The MessageEventsArgs derives from EventArgs (MessageEventArgs : EventArgs) so custom information can be passed to the caller. This isn't required, one could use EventArgs e as well.
public delegate void SchemaProcessorMessageEventHandler(object sender, MessageEventArgs e);
public event SchemaProcessorMessageEventHandler SchemaProcessorMessage;
protected virtual void OnSchemaProcessorMessage(MessageEventArgs e)
{
if (SchemaProcessorMessage != null)
{
SchemaProcessorMessage(this, e);
}
}
Now in your caller (UI) set up the event listener. Remove the event listener -= when finished.
_SchemaProcessor = new ServerSchemaUtilityFramework.SchemaProcessor();
_SchemaProcessor.SchemaProcessorMessage += new ServerSchemaUtilityFramework.SchemaProcessor.SchemaProcessorMessageEventHandler(sp_SchemaProcessorMessage);
void sp_SchemaProcessorMessage(object sender, ServerSchemaUtilityFramework.MessageEventArgs e)
{
//Update the UI, if on background will need to (!this.Dispatcher.CheckAccess())
}

Categories

Resources