I am currently working on a modular application using multiple dynamically loaded plugins. The main window also works as an output for feedback messages from the various plugins. This is a central requirement so the plugins can't have their own output.
The main program consists of 3 classes:
Main class that creates the GUI and handles the plugin calls
Second class that collects the plugins from a specified folder using MEF
Export class that can be accessed from a Plugin to send a message (string[]) to show in the main window
The Export class uses an event for receiving messages, to which the main class subscribes and writes the message into a DataGridView.
This works fine as long as I start a plugin without putting it into a separate thread. But as expected the mainform is frozen while the plugin is working. Now I tried to create separate threads for the plugins so I can have multiple ones running in the background simultaneously. Unfortunately this renders the message receiving a cross-thread operation.
With the use of an event I intended to make it thread-safe because the mainform subscribes to the Event and handles the messages in the "main-thread". Obviously I was wrong… I still don't quite get how the event-triggered method in the main window suddenly switches into the separate task...
Some additional Information:
Invoking is not an option because the output window consists of a lot more controls than just the DataGridView and these controls are constantly being modified whenever a message is received.
The mainform has a public static string[] for transferring the message's content from the export class to the main class
Is there any way to put the message writing method into the main thread but still able to subscribe to the event that is fired from another thread?
Or maybe is there another approach to the task?
[Export]
public class Exportklasse : Interfaces.IMain
{
public static event EventHandler MeldungEintragen;
// Diese Methode aufrufen, um von Modulen aus Textmeldungen einzutragen
public void MeldungEmpfangen(string[] MeldungInput, EventArgs e)
{
EventHandler EintragenEvent = MeldungEintragen;
if (EintragenEvent != null) {
MainForm.MeldungText = MeldungInput;
EintragenEvent(this, e);
}
}
}
Would something like this work?:
private readonly IBackendWorker worker;
private SynchronizationContext uiContext;
public MainForm(IBackendWorker worker)
{
InitializeComponent();
this.worker = worker;
uiContext = SynchronizationContext.Current;
worker.BackendEvent += OnWorkerBackEvent;
...
}
and the eventhandler:
private void OnWorkerBackEvent(int count)
{
// schedule the action on the UI synchronisation context.
uiContext.Post((c) =>
{
// put your code to interact with UI here
outputTextBox.Text = count.ToString();
}, null);
// or: uiContext.Post((c) => outputTextBox.Text = c.ToString(), count);
}
The eventhandler is executed in whatever thread the event is triggered from, but executes the action in the UI-thread.
Related
A form should open only when there is an event if there is no event it should not display on the screen. So Basically i thought of using a timer to do this. An exe will continously be running and after every minute it checks the db to see if there is data and if there is it shows up on the screen and will only be closed manually with user interaction. After a minute it checks again and displays the form if Data is present in the DB.
I used system.threading.Timer in Program.cs file to open a window after every minute.Below is the code
timer = new System.Threading.Timer((s) => {
EL.CustomMessageBox l = new EL.CustomMessageBox();
l.ShowDialog();
}, null, TimeSpan.Zero, 60000);
After certain time I see that this exe is still running in the taskmanager but even though there is data in the DB it stops showing up on the screen. Any help is appreciated.
System.Threading.Timer runs its callback on a threadpool thread. You should never use a threadpool thread for UI work, because:
They don't run a message dispatch loop.
You don't control when the thread gets recycled. UI windows have thread affinity and if their thread exits all the associated windows go poof immediately (you won't even get WM_DESTROY messages).
A normal Application.Run loop on the main thread, with a hidden main window and a UI timer will serve you much better.
I would pass my own custom ApplicationContext to Application.Run() in program.cs.
This will allow you to have NO INTERFACE until your conditions are met. The application will also continue to run (even when you close the Forms) until you explicitly call Application.Exit().
You can keep a reference to your Form at class level. This will help you decide if you need to work with the existing one, or create a new one.
Note that I'm using the System.Windows.Forms.Timer, not the threaded timer.
Something like...
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyContext());
}
}
public class MyContext : ApplicationContext
{
private EL.CustomMessageBox l = null;
private System.Windows.Forms.Timer timer;
public MyContext()
{
timer = new System.Windows.Forms.Timer();
timer.Interval = (int)TimeSpan.FromMinutes(1).TotalMilliseconds;
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
bool result = true; // hit the database and get an answer
if (result)
{
if (l == null || l.IsDisposed)
{
// no form has been created yet, or the previous one was closed
// create a new instance
l = new EL.CustomMessageBox();
l.Show();
}
else
{
// if we get in here, then the previous form is still being displayed
// if your form can be minimized, you might need to restore it
// if (l.WindowState == FormWindowState.Minimized)
// {
// restore the window in here?
// }
}
// update the form "l" with some data?
l.xxx = yyy;
}
}
}
I can't help but think that the other answers, massively technically correct as they are, don't actually solve the problem because they probably don't make sense if you aren't aware of how Windows works. Idle_Mind's is closest to what I'd do, though if the forms designer is familiar I'd go for a solution that basically just uses that - as such I present what I would do to solve the task you're faced with:
Have an app with one form (or make this form an autonomous one within another app, but for now maybe do it as a dedicated app for simplicity) - make a new Windows Forms project
Have a Timer (a Windows Forms timer, out of the toolbox, not a System.Threading timer) with an interval of 60000 and Enabled = true
Have a timer Tick event handler on your form (double click the timer in the tray under the form designer to attach an event handler) that queries the DB and finds if there are any messages
If there are new messages, adds them to a listbox or something, and calls this.Show() to show the form
Have an eventhandler attached to the FormClosing event so when the user clicks X, the form hides instead of closes:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
Maybe have the FormClosing event clear the messages listbox. This way if the form opens and the user is on lunch, the messages will build up and build up, then they can read them and clear them by closing the form. Calling Show on an already-visible form does nothing, so the messages will just accumulate into the listbox if more messages come in and the form is already visible
Good quick rule of thumb; never use System.Threading Timer in a Windows Forms app. Use a timer out of the forms designer toolbox instead. Only use a threading timer if you're writing a service or Console app etc. For stability reasons, Windows controls absolutely must be accessed by the thread that originally created the control. Windows forms timer is aware of this and its Tick event can safely access the controls (a form is a control, showing it requires to access it) in a Forms app
You should call Invoke to execute your delegate on the thread that owns the control's underlying window handle.
Something like this should work:
timer = new System.Threading.Timer((s) => {
EL.CustomMessageBox l = new EL.CustomMessageBox();
l.Invoke((Action) () =>
{
l.ShowDialog();
});
}, null, TimeSpan.Zero, 60000);
Or even better, use this extension method:
public static void InvokeIfRequired(this Control c, MethodInvoker action)
{
if (c.InvokeRequired)
{
c.Invoke(action);
}
else
{
action();
}
}
And call it like this:
l.InvokeIfRequired(() => { l.ShowDialog(); });
Further information can be found at: https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls?view=netframeworkdesktop-4.8
I'm just trying to learn this thing and in future, wanted to use it in one of my projects.
I have a small Form with a simple Text box, stored in a .Net dll (C#). And here is my class in this dll which contains methods to interact with this Form:
using System;
using System.Collections.Generic;
using System.Text;
namespace ClassLibrary1
{
public class Class1
{
static Form1 dlg = new Form1();
public static void ShowForm()
{
dlg.ShowIcon = true;
dlg.Show();
}
public static void SetText(string MyText)
{
dlg.Text = "Form Text ";
dlg.SetText(MyText);
}
}
}
Successfully loaded this form by referencing this dll into another C# application while calling its method i.e.:
private void button1_Click(object sender, EventArgs e)
{
ClassLibrary1.Class1.ShowForm();
}
And I was able to interact with the form perfectly.
Now loading same in Powershell using:
[Reflection.Assembly]::LoadFile("D:\Playing\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.dll")
[ClassLibrary1.Class1]::ShowForm()
Now this is loaded successfully at its default position, but I can't interact with this form i.e. I can't type in its Text Box, neither I can move or even close this form by clicking on its Close (x) button on right corner. Whenever I put my mouse on it, it becomes a HourGlass i.e. waiting for some process .
To verify if form is not hanged, I called SetText at Powershell prompt:
[ClassLibrary1.Class1]::SetText("String from Powershell")
and it worked fine. TextBox received this text properly, but still I can't interact with the form with my mouse.
I feel, I have to manually set its Window Handler i.e. System.Windows.Forms.IWin32Window.
But I don't know which Handler and how to achieve this?
Please guide .... Would really appreciate for any alternative tricks.
You can't show a form from PowerShell using Form.Show() method because it needs a message pump (and it's not provided by PowerShell host process).
Here what you can do to solve this issue:
Use Form.ShowDialog() or Application.Run(), your form will have its own message pump.
It'll be modal then you need to run it in another thread. I suggest to use a background thread and BeginInvoke() in your SetText() method.
Here code to do that (I won't change your code too much so I'll keep it as a singleton instance even if this prevents to display form multiple times). Code is just an example (I wouldn't suggest to use Thread Pool for this task) to illustrate the procedure.
public static void ShowForm()
{
if (dlg != null)
dlg.BeginInvoke(new MethodInvoker(delegate { dlg.Dispose(); }));
ThreadPool.QueueUserWorkItem(delegate(object state)
{
Application.Run(_dlg = new Form1());
});
}
public static void SetText(string text)
{
_dlg.BeginInvoke(new MethodInvoker(delegate { dlg.SetText(text); }));
}
In this way Form1 will be modal in another thread (with its own message pump) and your calling PowerShell thread won't be stopped. Communication between them is still possible via message dispatching (Invoke()/BeginInvoke()).
Please note that SetText() is now asynchronous, to make it synchronous just replace BeginInvoke() with Invoke().
I'm using C# and Winforms in Visual Studio 2010
I have a program with which I am trying to read output through a serial port and print it to the screen. It originally started as a Console program but has now evolved to where we would like to have the output be in a field on a form. I have the code that parses out the output I'm looking for off the serial port written and working, I just need to change the Console.WriteLine to label.text = "";, basically. I have merged the function that listens to the serial port into the GUI code so everything is in the same file.
I'm getting hung up on how to get the function to write to the label, though. It is STATIC so I cant just say 'label.text ='. I tried creating a new form object inside the function to use, and that allowed me to access the control on the form, but doesnt update the form I see at runtime (I'm guessing because I've created a new instance of the form rather than accessed the existing instance?)
I need to have the serial listener run at the same time as the GUI as well, so the GUI label will update with the results it gets from running the function in close to real-time, so Ive tried to set it up to be threaded, with the GUI being one thread that is started by main() and the serial listener being another thread which is started when i click the button to start it. However, I run into the same issue with not being able to access the label in the serial listener thread because it has to be static to be initialized using system.threading.
I'm thinking maybe I need to use a background worker for the serial listener but I have absolutely zero experience with those. Would a background worker be able to update the label on the GUI in real time?
I cant post specific code but heres the general idea:
Main() starts GUIthread
GUI has button to start serial listener
OnClick button starts ListenerThread
ListenerThread outputs to console, want to output to a form label instead
Cant access GUI.Label because Listener is static out of necessity to be threaded
Creating new GUI instance inside Listener allows me to call the controls for that instance, but they dont update the GUI at runtime
have ensured label is public.
The BackgroundWorker class was essentially made just for this.
Just have the DoWork method do your actual work, and ensure that ReportProgess is called while working as needed. You can pass any data as a string (or whatever else, if you want) and then use that value in the ProgressChanged event handler, which the form can handle to update it's UI.
Note that the BackgroundWorker will automatically ensure that the ProgressChanged and RunWorkerCompleted events run in the UI thread, so you don't need to bother with that.
Here's a sample worker:
public class MyWorker//TODO give better name
{
public void DoWork(BackgroundWorker worker)//TODO give better name
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);//to mimic real work
worker.ReportProgress(0, i.ToString());
}
}
}
And here's an example of configuring the background worker. Here I use lambdas both because it's convenient to be able to close over variables (i.e. use variables across each of these anonymous methods) but if you wanted to you could refactor each of the event handlers out into methods.
private void button1_Click(object sender, EventArgs e)
{
var bgw = new BackgroundWorker();
MyWorker worker = new MyWorker();
bgw.WorkerReportsProgress = true;
bgw.DoWork += (s, args) => { worker.DoWork(bgw); };
bgw.ProgressChanged += (s, data) =>
{
label1.Text = data.UserState.ToString();
};
bgw.RunWorkerCompleted += (s, args) =>
{
label1.Text = "All Done!";
};
bgw.RunWorkerAsync();//actually start the worker
}
Note here that none of the controls in the form are public, none of them are static, and I'm not passing any references to my form outside of the class. It's considered best form each Form to be responsible for updating it's own Controls. You shouldn't be allowing anyone else to directly access them. Rather than allowing some other worker class to directly access the label or modify it's text, what's happening is that the worker is simply telling the form, "Hey, I've got some data, you can go update yourself accordingly based on these values." It is then the form that is responsible for updating itself. events are what you use to allow these workers, or other types of child elements (such as other forms you create, for example) to inform the "parent" form that it needs to update itself.
To write to any windows control, you must be on the UI thread. If you have a serial listener running on a different thread, then you need to switch threads before changing the windows control. The BeginInvoke can be handy, http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx.
What I would do, is add a Action to the serial listener that is called whenever the listener wants to display something. And then this Action would call BeginInvoke.
Something like:
static class SerialListner
{
public Action<string> SomethingToDisplay;
void GotSomethingToDisplay(string s)
{
SomethingToDisplay(s);
}
And then somewhere in your windows form
SerialListern.SomethingToDisplay = (s) =>
label.BeginInvoke((Action) () => label.Text = s);
I think you can use a background worker, and they are really easy to use.
In order to use a BackgroundWorker, you'll have to implement at least two events:
backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
there you read your input. It's triggered calling backgroundWorker1.RunWorkerAsync(...)
backgroundWorker1_ProgressChanged(....)
there you update your label. Maybe you'll have to create a delegate to update it.
you can also implement:
backgroundWorker1_RunWorkerCompleted(....)
to let you know when it stop...
Going on what you said about a static listener method and that it used to be a console application, I think a relatively minor modification might be the following:
class Program
{
static void Main(string[] args)
{
// Create a main window GUI
Form1 form1 = new Form1();
// Create a thread to listen concurrently to the GUI thread
Thread listenerThread = new Thread(new ParameterizedThreadStart(Listener));
listenerThread.IsBackground = true;
listenerThread.Start(form1);
// Run the form
System.Windows.Forms.Application.Run(form1);
}
static void Listener(object formObject)
{
Form1 form = (Form1)formObject;
// Do whatever we need to do
while (true)
{
Thread.Sleep(1000);
form.AddLineToTextBox("Hello");
}
}
}
In this case, Form1 is obviously the form class, and Listener is the listening method. The key here is that I'm passing the form object as an argument to the Listen method (via Thread.Start), so that the listener can access the non-static members of the GUI. Note that I've defined Form1.AddLineToTextBox as:
public void AddLineToTextBox(string line)
{
if (textBox1.InvokeRequired)
textBox1.Invoke(new Action(() => { textBox1.Text += line + Environment.NewLine; }));
else
textBox1.Text += line + Environment.NewLine;
}
Note especially that since now the Listener method is running in a separate thread, you need to use the Invoke method on the GUI control to make a change. I've used a lambda expression here, but if you're targeting an earlier version of .net you could use a full method just as easily. Note that my textBox1 is a TextBox with Multiline set to true and ReadOnly set to false (to be similar to a label).
An alternative architecture which may require more work but would probably be more elegant would be to do the opposite dependence relationship: you create the form with a reference to a Listener object. The listener will then raise events which the GUI would be subscribed to in order to update its display.
I am in the process of writing an application that communicates with several devices through GPIB commands, running a test on some equipment. I've set up a class, TestProcedure, which will start a new thread, and run the testing. Throughout testing, I've set up several custom events to send information back to the GUI. Here is an example of a simple event:
public event InformationEventHandler<string> TestInfoEvent;
/// <summary>
/// Event raised when information should be passed back to the main testing form.
/// </summary>
/// <param name="s">Information to send back to form.</param>
private void OnInfo(string s)
{
if (TestInfoEvent != null)
TestInfoEvent(this, s);
}
Which would be handled through the GUI, updating a text box like this:
TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string>
(InfoOccurred);
....
private void InfoOccurred(Object sender, string s)
{
this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text;
if (this.textBox1.Text.Length > 10000)
this.textBox1.Text = this.textBox1.Text.Remove(1000);
}
This event handling seems to be working fine. I haven't received any cross threading issues, and overall it's been working as expected. However, on another form I just added a similar event handler, which throws a cross-thread exception. The event fires, sending a simple class with a bit of information that I display in an InputTextBox (A custom ComponentOne control). The particular control does not have a .Invoke method, so I'm looking for alternative solutions to access it asynchronously.
So my question is, are event handlers safe to access controls on a form? If not, how do event handlers fire, and could somebody help educate me, or provide some linked information, as to how an event handler communicates with form controls? Do I need to lock the event?
Controls on the UI thread may only be accessed from the UI thread - any access from other threads is bound to cause issues. You need to use InvokeRequired and BeginInvoke() to marshal an event to the right thread if it's not already there.
Example
You'll want to create a delegate callback and Invoke() execute that after testing the InvokeRequired property. The following code will handle the addition in a thread safe manner.
TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string>
(InfoOccurred);
private void InfoOccurred(Object sender, string s)
{
LogMessage(s);
}
delegate void LogMessageCallback(string text);
void LogMessage(String message)
{
if (this.textBox1.InvokeRequired)
this.Invoke(new LogMessageCallback(LogMessage), message);
else
{
this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text;
if (this.textBox1.Text.Length > 10000)
this.textBox1.Text = this.textBox1.Text.Remove(1000);
}
}
In my project, whenever a long process in being executed, a small form is displayed with a small animated gif file. I used this.Show() to open the form and this.Close() to close the form.
Following is the code that I use.
public partial class PlzWaitMessage : Form
{
public PlzWaitMessage()
{
InitializeComponent();
}
public void ShowSpalshSceen()
{
this.Show();
Application.DoEvents();
}
public void CloseSpalshScreen()
{
this.Close();
}
}
When the form opens, the image file do not immediately start animating. And when it does animate, the process is usually complete or very near completion which renders the animation useless. Is there a way I can have the gif animate as soon as I load the form?
Why not using threads? It's always good idea to learn something new.
You could simply put your "long process" in background thread, and use events to report to presentation layer, for example:
// in your "long process" class
public event Action<double> ReportCompletition;
// this method will start long process in separate background thread
public void Start()
{
Thread thread = new Thread(this.LongProcess);
thread.IsBackground = true;
thread.Start();
}
private void LongProcess()
{
// do something
// report 10% completition by raising event
this.ReportCompletition(0.1);
// do something more
this.ReportCompletition(0.5);
// ... and so on
}
This way, all you have to do is implement simple method in your Form/UI, which will consume this information.
public partial class MainApplicationWindow : Form
{
private LongProcessClass _longProcess;
public MainApplicationWindow
{
this.InitializeComponent();
this._longProcess = new LongProcessClass();
// bind UI updating method to long process class event
this._longProcess.ReportCompletition += this.DisplayCompletitionInfo;
}
private void DisplayCompletitionInfo(double completition)
{
// check if control you want to display info in needs to be invoked
// - request is coming from different thread
if (control.InvokeRequired)
{
Action<double> updateMethod = this.DisplayCompletitionInfo;
control.Invoke(updateMethod, new object[] { completition });
}
// here you put code to do actual UI updating,
// eg. displaying status message
else
{
int progress = (int) completition * 10;
control.Text = "Please wait. Long process progress: "
+ progress.ToString() + "%";
}
}
Of course, you can report anything you like from within long process. Be it completition rate, ready to display string messages, anything. You can also use events to report that long process has finished, broke, or any long process data you wish.
For more detailed information on this topic you might want to check MSDN tutorials on Threading and Events.
You should do the "long process" in a separate thread. I advice using the BackgroundWorker.
This will make your code more difficult though. One of the main reasons is, that you cannot communicate with the UI from the background thread.
Also note the warning from the linked page:
Caution
When using multithreading of
any sort, you potentially expose
yourself to very serious and complex
bugs. Consult the Managed Threading
Best Practices before implementing any
solution that uses multithreading.
If this is too difficult, you could call Application.DoEvents very frequent from your "long process" code.
Whatever you choose, this will make it possible for the user to interact with your form. For instance closing it. You should be aware of this.
Use the gif in a PictureBox, and have it open using Form pWait = new Form(); pWait.Show();