I have a a wait form FormWait (long running task notification), that has ShowMessage(string message) function.
Often happens in code:
public RootCall()
{
FormWait.ShowMessage("Begin long task 1...");
ChildCall();
FormWait.CloseForm();
}
public ChildCall()
{
FormWait.ShowMessage("Begin long task 2...");
// some code here
FormWait.CloseForm();
}
FormWait on root shows the message to the user, but before closing it on root level, there is another ShowMessage of child and CloseForm of child.
I have a couple of solutions to resolve this:
Like in code provided the methods are static and operate on one static System.Windows.Forms.Form instance. On every ShowMessage there is a static variable that increments and on every CloseForm it decrements. So by looking on that variable I can understand if I really need to close the form (if I'm or not on root level), or its just a nested CloseForm call. And on every ShowMessage new string just updated on already visible form.
For every new ShowMessage call create new instance of the form, but this is really wired to see. So almost sure I will not pick this solution.
Any ideas, how can I manage WaitForm (form that signals to user about long running tasks) in case of nested calls, by making also the developer life easier.?
The Stack<> class is the natural fit for this:
public partial class WaitForm : Form {
private WaitForm() {
InitializeComponent();
}
private static WaitForm instance;
private static Stack<string> messages = new Stack<string>();
public static void ShowMessage(string message) {
if (instance == null) {
instance = new WaitForm();
instance.FormClosed += delegate { instance = null; };
instance.Show();
}
messages.Push(message);
instance.lblMessage.Text = message;
instance.Update();
}
public static void CloseForm() {
messages.Pop();
if (instance != null) {
if (messages.Count == 0) instance.Close();
else instance.lblMessage.Text = messages.Peek();
}
}
}
Don't forget to put the CloseForm() call in a finally block so this is all exception safe.
Related
Say I have a common class that performs some time-consuming step (eg. saving stuff to USB). I'd like to be able to call that code from multiple forms and receive feedback whenever a step is completed. How does the common class know to whom to send feedback to? The code below describes the situation:
// ### Common class frmCommon ###
// Parent form (when feedback on some slow operation is required)
private static Form m_frmParent = null;
// ...
public static void SetParentForm(Form frmParent)
{
// When some time consuming process takes place (such as saving to USB), setting the
// parent form allows feedback to be given to the user (eg. as a progress bar)
m_frmParent = frmParent;
}
public static void DoSomething()
{
for (int nStep = 0; nStep < 100; nStep++)
{
// Tell the parent form how many product sets (groups of 20) there are to read
if (m_frmParent != null)
{
// How to decide whether to call form 1 or form 2?
((frmForm1)m_frmParent).SendFeedback(nStep);
((frmForm2)m_frmParent).SendFeedback(nStep);
}
// Perform the time-consuming step...
SlowStep(nStep);
}
}
// ### FORM 1 frmForm1 ###
private void SomeEventForm1(int nStep)
{
frmCommon.SetParentForm(this);
frmCommon.DoSomething();
frmCommon.SetParentForm(null);
}
public void SendFeedback(int nStep)
{
// Do something like update a progress bar on form 1
Application.DoEvents();
}
// ### FORM 2 frmForm2 ###
private void SomeEventForm2(int nStep)
{
frmCommon.SetParentForm(this);
frmCommon.DoSomething();
frmCommon.SetParentForm(null);
}
public void SendFeedback(int nStep)
{
// Do something like update a progress bar on form 2
Application.DoEvents();
}
Aiming for .NET 2.0 if that makes a difference.
I'd rather use an event:
public class SlowProcess {
...
// Simplest, not thread safe
public static event EventHandler<int> StepChanged;
public static void DoSomething() {
for (int nStep = 0; nStep < 100; nStep++) {
if (null != StepChanged)
StepChanged(null, nStep);
SlowStep(nStep);
}
}
}
...
public partial class MyEventForm: Form {
...
private void onStepChange(Object sender, int nStep) {
//TODO: update form here after receiving a feedback
}
private void TraceSlowProcess() {
// feedback is required
SlowProcess.StepChanged += onStepChange;
try {
SlowProcess.DoSomething();
}
finally {
// No need of feedback
SlowProcess.StepChanged -= onStepChange;
}
}
}
The calling code will have to provide a delegate to that class. When the class is done with the time consuming process, it will call that delegate to inform the calling code that it finished. Look here for a good tutorial on how to do this.
1 - If SendFeedback is a function you implemented in both forms, and they do the same, consider creating a single static method in a static class to extend the Form:
public static class FormExtender
{
public static void SendFeedback(this Form frm, int nStep)
{
//do what must be done
//you can call this anyhere using, for instance: m_frmParent.SendFeedback(nStep)
//when you call it like that, m_frmParent will be given to this function as the argument frm
}
}
2 - But if the methods are different in both forms, I suggest you create an interface:
interface IFormWithFeedback
{
void SendFeedback(int nStep);
}
Then form1 and form2 should implement this (just add , IFormWithFeedBack where your forms are declared):
public class frmForm1 : Form, IFormWithFeedback
public class frmForm2 : Form, IFormWithFeedback
And your parent form inside that class should be an IFormWithFeedback instead of a form:
private static IFormWithFeedback m_frmParent = null;
Both options (extension method or interface) would allow you to call SendFeedback direclty from m_frmParent without casting it.
I'm trying to work with Threads for a private Project and I have a question which is, as I think very easy to answer.
Is it possible to set a variable in another thread?
Here a little Code example to show you what I'm trying to do:
public class PartyClass
{
public boolean partytime = true;
public void MakeParty()
{
while(partytime)
Console.WriteLine("I'm making a party here");
Console.WriteLine("The party ended. Please leave now");
}
public void StopParty()
{
partytime = false;
}
}
public class MainThread
{
public static int Main(String[] args)
{
PartyClass party = new PartyClass();
Thread partyThread = new Thread(new ThreadStart(party.MakeParty()));
partyThread.Start();
while (!partyThread.IsAlive) ;
System.Threading.Thread.Sleep(5000);
// Now I want to somehow call the StopParty() Method
}
}
I don't know if it's really stupid what I'm trying to do but I think its a nice way to stop the "Partythread" in a clean way.
Is this possible or is there a better solution for this?
Thanks for your Ideas.
(I didn't test the Code - just wrote it out of my head)
You call the stop method just the way you called the start method:
party.StopParty();
In order to ensure that the changes made in another thread aren't just cached, the partytime field should be marked as volatile as well.
You should use synchronization facilities, such as CancellationToken.
Your code will look like:
public class PartyClass
{
private readonly CancellationToken _cancellationToken;
public PartyClass(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
}
public void MakeParty()
{
while (!_cancellationToken.IsCancellationRequested)
Console.WriteLine("I'm making a party here");
Console.WriteLine("The party ended. Please leave now");
}
}
public class MainThread
{
public static int Main(String[] args)
{
var cancellationSource = new CancellationTokenSource();
PartyClass party = new PartyClass(cancellationSource.Token);
Thread partyThread = new Thread(party.MakeParty);
partyThread.Start();
System.Threading.Thread.Sleep(5000);
cancellationSource.Cancel();
partyThread.Join();
}
}
It is thread-safe and suitable not only for this, but also for more advanced scenarios, as well as for working with tasks.
If want more threads to ask for the same variable, take care about thread syncrhonization issues (when two threads try to access the same variable).
Im going to show the safest way (might be more than what you need). The best to way to do it is declaring an static object to set a lock in order to make sure you have one thread changing the party flag at once.
public class PartyClass
{
private object _partyTimeLock = new Object(); // executed at class init
private boolean partyTime= true;
public bool IsPartyGoingOn()
{
bool itIsGoingOn = false;
lock(_partyTimeLock) {
itIsGoingOn = partyTime;
}
return itIsGoingOn;
}
public void StopParty()
{
lock(_partyTimeLock) {
partyTime = false;
}
}
public void MakeParty()
{
while(IsPartyGoingOn()) {
Console.WriteLine("I'm making a party here");
}
Console.WriteLine("The party ended. Please leave now");
}
}
In this example here, no matter who try to call the IsPartyGoingOn(), you will never have an issue (no matter if it the class own thread or another one). The lock keyword will guarantee you are doing the right way.
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();
}
}
Given the class below, to launch a splash screen on an alternate thread:
public partial class SplashForm : Form
{
private static Thread _splashThread;
private static SplashForm _splashForm;
public SplashForm()
{
InitializeComponent();
}
// Show the Splash Screen (Loading...)
public static void ShowSplash()
{
if (_splashThread == null)
{
// Show the form in a new thread.
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// Called by the thread.
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new SplashForm();
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(_splashForm);
}
// Close the splash (Loading...) screen.
public static void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
This is called and closed with the following respective commands
SplashForm.ShowSplash();
SplashForm.CloseSplash();
Fine.
I am not exactly new to the TPL, of course we can show the form on another thread using something as simple as:
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
My issue is closing this SomeForm down when you are ready. There must be a better way than creating a public static method in the SomeForm class like
private static SomeForm _someForm;
public static void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
My question is, what is the best way to do the same thing as shown using the SplashForm class above using the Task Parrallel Library (TPL)? Specifically, the best way to close the form invoked on another thread from the UI.
Your question does not seem to be so much about a difference between Thread and Task because what you want is to get rid of the "dirty" static state. I suggest you encapsulate it into a class:
class SplashController
{
public void Run() {
_someForm = new SomeForm();
someForm.ShowDialog();
}
private SomeForm _someForm;
public void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
}
You can call Run using whatever threading mechanism you like. CloseSomeForm does not use threading so it is independent of this problem.
You can now store a reference to an instance of SplashController wherever you like. In local variables or indeed in a static variable. The latter makes sense because there is exactly one splash screen.
Because the static state is now well encapsulated I don't see any problem with it being statically held.
You probably shouldn't do something like this
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
because it would require a message loop to be present on the exact thread that creates the Form, which is a ThreadPool thread. But I haven't tested this.
You could try this:
public static Task<SplashForm> ShowSplash()
{
var tcs = new TaskCompletionSource<SplashForm>();
// Show the form in a new thread.
_splashThread = new Thread(() =>
{
var splashForm = new SplashForm();
tcs.SetResult(_splashForm);
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(splashForm);
});
_splashThread.IsBackground = true;
_splashThread.Start();
}
this would allow you to remove the static modifier from CloseSplash:
// Close the splash (Loading...) screen.
public void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
May be used like this:
var form = await SplashForm.ShowSplash();
form.CloseSplash();
Can someone please tell me what is wrong with the following code? Ideally it should start a thread first and then wait for the set event. Instead of that it does not start the thread and just get stuck on WaitOne().
I am curious to know what happened to the thread and why?
class Program
{
static void Main(string[] args)
{
Testing t = Testing.Instance;
Console.Read();
}
}
class Testing
{
private static AutoResetEvent evt = new AutoResetEvent(false);
public static Testing Instance = new Testing();
private Testing()
{
Create();
evt.WaitOne();
Console.WriteLine("out");
}
private void Create()
{
Console.WriteLine("Starting thread");
new Thread(Print).Start();
}
private void Print()
{
Console.WriteLine("started");
evt.Set();
}
}
EDIT:
So far, the description provided by #BrokenGlass makes sense. but changing the code to the following code allows another thread can access the instance methods without constructor being completed.(Suggested by #NicoSchertler).
private static Testing _Instance;
public static Testing Instance
{
get
{
if (_Instance == null)
_Instance = new Testing();
return _Instance;
}
}
I suspect the root cause of this behavior is that the spawned thread cannot access the Print method until the constructor has finished executing - but the constructor never finishes executing because it is waiting on the signal that is triggered only from the Print method.
Replacing the evt.WaitOne() with a long Thread.Sleep() call confirms the same behavior - the constructor must finish running before any instance method of the object may execute from another thread.
The problem is that the second thread is created too early. I'm not sure why, but when started before the main program starts, it will not execute.
You should use the singleton pattern in its original version. This will work.
private static Testing _Instance;
public static Testing Instance
{
get
{
if (_Instance == null)
_Instance = new Testing();
return _Instance;
}
}
Additionally, you should not make the evt variable static. The instance variable should be the only static member of a singleton class in most cases.
My guess would be an issue with the relative timing of the static field initialization. Try initializing evt in the constructor of Testing instead:
private static AutoResetEvent evt;
public static Testing Instance = new Testing();
private Testing()
{
evt = new AutoResetEvent(false);
Create();
evt.WaitOne();
Console.WriteLine("out");
}
I should note this is really just a guess- I'd have thought this code would work fine.