I have a C# library, inside which there is a timer that keeps checking a boolean variable ProcessFinished. ProcessFinished is initialized as false.
What I want is that the main application needs to watch the variable Status from the library, and a message box should display once this ProcessFinished becomes true.
The problem I had is the message box never display if I simple execute the main application, but it displays if I step in the main application.
Here is the timer_tick code in main application:
public Window1()
{
_fl = new FijiLauncherControl();
this._statusTimer = new System.Windows.Forms.Timer(); // read log 4 times per sec
this._statusTimer.Interval = 125;
this._statusTimer.Tick += new EventHandler(_statusTimer_Tick);
InitializeComponent();
}
void _statusTimer_Tick(object sender, EventArgs e)
{
try
{
if (_fl.ProcessFinished)
{
System.Windows.MessageBox.Show("Process is finished");
_statusTimer.Stop();
}
}
catch (Exception ex)
{
}
}
private void FijiLaucherButton_Click(object sender, RoutedEventArgs e)
{
_statusTimer.Start();
_fl.LaunchFiji();
}
where the _fl is the object of the class from the other library.
Inside the library, the timer code is like this:
public FijiLauncherControl()
{
_ijmFile = "";
_fijiExeFile = "";
_logFile = "";
_outputDir = "";
_isLogOn = false;
_processOn = false;
_processFinished = false;
_headless = true;
_doneStr = "Procedure is finished.";
_logFileCheckTimer = new System.Timers.Timer(500); // read log 4 times per sec
_logFileCheckTimer.Enabled = true;
_logFileCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(_logFileCheckTimer_Elapsed);
}
void _logFileCheckTimer_Elapsed(object sender, EventArgs e)
{
if (_processOn && IsLogOn)
{
try
{
_processFinished = CheckStatuts();
}
catch (Exception ex)
{
}
}
}
I am wondering what is going on here? Is there anyway I can see the message box shows up without stepping in? What is the right way to watch ProcessFinished from the main application?
Would it not be better to fire an event from the thread and catch it. Then show the message box?
Like this maybe?
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click( object sender, EventArgs e )
{
var logChecker = new LogChecker();
logChecker.FinishedExvent += () => MessageBox.Show( "Finished" );
logChecker.Start();
}
}
internal class LogChecker
{
public void Start()
{
var thread = new Thread( CheckLog );
thread.Start();
}
private void CheckLog()
{
var progress = 0;
while ( progress < 3000 )
{
Thread.Sleep( 250 );
progress += 250;
}
FinishedExvent();
}
public event TestEventHandler FinishedExvent;
}
internal delegate void TestEventHandler();
}
Try
volatile bool _processFinished;
Related
I have class method and event
public class DefaultVariables
{
private DataTable SetDataFromSQL()
{
var Eventstatus = new BasicEventsHandlersArgs();
Eventstatus.Status.Task = BasicEventStatus.Busy;
Eventstatus.Status.Task_Status = "Please wait while we performing";
Eventstatus.Status.Task_CurrentProgress = "Verifying Steps...";
Eventstatus.Status.Task_TotalProgress = "Measuring Source Properties , Destinations Properties and someother(s)";
Raise_DefaultVariablesProgressUpdate(Eventstatus);
// Long running SQLs
}
public event EventHandler<BasicEventsHandlersArgs> Event_DefaultVariablesBasciProgress;
protected virtual void Raise_DefaultVariablesProgressUpdate(BasicEventsHandlersArgs e)
{
Event_DefaultVariablesBasciProgress?.Invoke(this, e);
}
}
and I call it in a Windows Forms app like this:
DefaultVariables variables = new DefaultVariables();
public EmployeeTag(string UserID)
{
InitializeComponent();
variables.Event_DefaultVariablesBasciProgress += Variables_Event_DefaultVariablesBasciProgress;
}
private void Variables_Event_DefaultVariablesBasciProgress(object sender, BasicEventsHandlersArgs e)
{
Application.DoEvents();
LabDepartmentCount.Text = e.Status.Task_Status.ToString();
labDesignationCount.Text = e.Status.Task_CurrentProgress.ToString(); ;
labEmCount.Text = e.Status.Task_TotalProgress.ToString();
// while (e.Status.Task != BasicEventStatus.Completed) { importToolStripMenuItem1.Enabled = false; }
if (e.Status.Task == BasicEventStatus.Busy)
{
importToolStripMenuItem1.Enabled = false;
}
if (e.Status.Task == BasicEventStatus.Completed)
{
importToolStripMenuItem1.Enabled = true;
}
}
private void importToolStripMenuItem1_Click(object sender, EventArgs e)
{
SetDataFromSQL();
}
The code is working and raising event as I expected, no error.
I just want to add button on GUI to pause, stop and continue between execution and want to control SetDataFromSQL, pause if there is long running code inside and continue from there.
Just like backgroundworker.cancel() method.
So how can I pause the execution from GUI button?
How can I pass pause flag to wait until I click continue?
Please help me out
I'm a fairly new developer and this one has me stumped.
My WinForms application is a slideshow for websites that rotates through a list of URLs, fading-in/out on each transition by using a second form as a "curtain". It's meant to run for an indefinite period of time but consistently hangs on the transition after running for a couple of days.
Form1:
HttpWebResponse response = null;
List<Slide.Doc> sList = null;
bool repeatSlideshow = true;
bool pageLoaded = false;
double curtainAnimStep = 0.05;
int errorCount = 0;
public Form1()
{
InitializeComponent();
CursorShown = false;
this.Visible = true;
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
webBrowser1.ScrollBarsEnabled = false;
webBrowser1.ScriptErrorsSuppressed = true;
Slideshow(environment, channel);
}
public void Slideshow(string environment, string channel)
{
while (repeatSlideshow)
{
try
{
sList = Slide.convertJSONToSlide(Slide.getParams(environment, channel));
}
catch (Exception)
{
Form2 curtain = new Form2(curtainAnimStep);
curtain.Show();
waitForFade(curtain, 1);
displayError();
raiseCurtain(curtain, curtainAnimStep);
waitForFade(curtain, 0);
curtain.Dispose();
waitAround(30);
continue;
}
foreach (Slide.Doc s in sList)
{
bool slideWasDisplayed = false;
Form2 curtain = new Form2(curtainAnimStep);
curtain.Show();
waitForFade(curtain, 1);
slideWasDisplayed = displaySlide(s.URL_TEXT);
if (slideWasDisplayed == false)
{
webBrowser1.DocumentText = "<html><body style='background-color: #1C1C1C;'></body></html>";
redrawPage();
}
raiseCurtain(curtain, curtainAnimStep);
waitForFade(curtain, 0);
curtain.Dispose();
if (slideWasDisplayed == true)
{
waitAround(s.DISPLAY_SEC);
}
}
if (errorCount == sList.Count)
{
Form2 curtain = new Form2(curtainAnimStep);
curtain.Show();
waitForFade(curtain, 1);
displayError();
raiseCurtain(curtain, curtainAnimStep);
waitForFade(curtain, 0);
curtain.Dispose();
waitAround(30);
}
errorCount = 0;
Utilities.Web.WebBrowserHelper.WebBrowserHelper.ClearCache();
}
}
public bool displaySlide(string slideUrl)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(slideUrl);
request.Timeout = 1000;
try
{
response = (HttpWebResponse)request.GetResponse();
webBrowser1.Navigate(slideUrl);
redrawPage();
response.Dispose();
return true;
}
catch (WebException)
{
errorCount++;
return false;
}
}
public void redrawPage()
{
while (pageLoaded == false)
{
Application.DoEvents();
}
webBrowser1.Invalidate();
Application.DoEvents();
pageLoaded = false;
}
public void raiseCurtain(Form curtain, double curtainAnimStep)
{
while (curtain.Opacity > 0)
{
curtain.Opacity -= curtainAnimStep;
Application.DoEvents();
System.Threading.Thread.Sleep(10); // How long between shifts in opacity (NOT interval between slides)
}
}
public void waitAround(int duration)
{
DateTime dt2 = DateTime.Now;
while (dt2.AddSeconds(duration) > DateTime.Now)
{
Application.DoEvents();
}
}
public void waitForFade(Form curtain, int finalOpacity)
{
while (curtain.Opacity != finalOpacity)
{
DateTime dt = DateTime.Now;
dt = dt.AddSeconds(1);
while (dt > DateTime.Now)
{
Application.DoEvents();
}
}
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
pageLoaded = true;
}
Form2:
public Form2(double animStep)
{
InitializeComponent();
this.AnimStep = animStep;
}
public double AnimStep { get; set; }
private async void Form2_Load(object sender, EventArgs e)
{
while (Opacity < 1.0)
{
await Task.Delay(10);
Opacity += AnimStep;
}
Opacity = 1;
}
I've been working on this for a long time, but I have to admit that I genuinely don't even know what I should be looking for at this point.
Could the use of Application.DoEvents be responsible? Leaving them out breaks the application, but I can't figure out an alternative appproach.
Looking at your code (and as indicated by Noseratio) one of the things I advice is to get rid of the need for the DoEvents calls. Just remember that in Windows there is a dedicated UI thread that is used to update the controls on the form. As you are doing a lot of stuff (in loops, calling a bunch of methods) on that same UI thread the Windows controls depends on your cooperation to share some time with them, hence the calls to DoEvents.
I'm going to use a BackgroundWorker and a Timer and WaitHandle to schedule commands that will update the UI from a background thread. With that we do as little as needed on the UI thread.
Form Load
Form1 will only have a webbrowsercontrol and a backgroundworker. A queue will hold the commands that needs to be executed. From the Load event we start the Backgroundworker.
Form2 frm2 = new Form2();
Queue<ICommandExecutor> commands = new Queue<ICommandExecutor>();
private void Form1_Load(object sender, EventArgs e)
{
frm2.Show();
frm2.BringToFront();
commands.Enqueue(new LoadSlideShow(this, frm2, commands));
backgroundWorker1.RunWorkerAsync();
}
BackgroundWorker
The Backgroundworker DoWork event is the engine that runs on it's own background thread. It runs as long as there are commands found in the queue. After fetching a command it's Execute method is fired. If the command supports disposing the Dispose method is called and with that a command is processed and we start over again.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(commands.Count>0)
{
ICommandExecutor cmd = commands.Dequeue();
try
{
cmd.Execute();
// dispose if we can
IDisposable sync = cmd as IDisposable;
if (sync != null)
{
sync.Dispose();
}
}
catch(Exception exp)
{
// add commands here
Trace.WriteLine("error" + exp.Message);
}
}
}
Commands
There is a standard interface available to implement a command pattern. ICommandExecutor has a single method, Execute. We can create different classes that implement this interface. Each class holds its own state and references and it can be as simple as a timer of as complex as loading a new batch of urls to show.
public class ShowSlide:ICommandExecutor
{
string url;
Form1 form;
AutoResetEvent done = new AutoResetEvent(false);
public ShowSlide(Form1 form, string url)
{
this.url = url;
this.form = form;
}
public void Execute()
{
// if we are not on the UI thread...
if (form.InvokeRequired)
{
// ... switch to it...
form.Invoke(new MethodInvoker(Execute));
}
else
{
// .. we are on the UI thread now
// reused from your code
form.displaySlide(url);
}
}
}
Here is a timer. Notice how a Timer class is used and the timerDone waithandle to make the backgroundthread continue work only if the timer has finished when Dispose is called.
public class WaitForSeconds: ICommandExecutor, IDisposable
{
int ms;
System.Threading.Timer timer;
ManualResetEvent timerDone = new ManualResetEvent(false);
public WaitForSeconds(int secs)
{
this.ms = secs * 1000;
}
public void Execute()
{
// use a timer
timer = new System.Threading.Timer(
(state) => timerDone.Set() // signal we are done
);
timerDone.Reset();
timer.Change(this.ms, Timeout.Infinite);
}
public void Dispose()
{
timerDone.WaitOne();
timerDone.Dispose();
timer.Dispose();
}
}
To setup the commands in the correct order we use the following command class implememntation that takes the Command queue, Form1 and Form2 as parameters on its constructor. The Execute command loads all url's to be fed to the webbrowser control. For each url it adds the commands that needs to be executed to the queue. At the end the this instance is added to the queue as well which means the class will be used again if all commands have been processed. The queue will there for never be empty.
public class LoadSlideShow: ICommandExecutor
{
readonly Queue<ICommandExecutor> commands;
readonly Form1 form;
readonly Form2 form2;
public LoadSlideShow(Form1 form, Form2 form2, Queue<ICommandExecutor> cmds)
{
this.form = form;
commands = cmds;
this.form2 = form2;
}
public void Execute()
{
var list = Slide.convertJSONToSlide(null);
foreach (var slide in list)
{
commands.Enqueue(new ShowSlide(form, slide.URL_TEXT));
commands.Enqueue(new WaitForSeconds(1));
//commands.Enqueue(new LowerCurtain(form2));
commands.Enqueue(new WaitForSeconds(slide.DISPLAY_SEC));
//commands.Enqueue(new RaiseCurtain(form2));
}
commands.Enqueue(this);
}
}
This is basically all there is that is needed to get a basic slideshow going.
For the so called curtain we are going to do something similar with Form2 but I'll use the BackgroundWorker_progress event as well.
Form2 the Curtain
Form2 will act as the curtain by changing it's Opacity in a loop. It has it's own backgroundworker:
ManualResetEvent stateChange = new ManualResetEvent(false);
public ManualResetEvent stateChangeDone = new ManualResetEvent(false);
private void Form2_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(stateChange.WaitOne())
{
stateChange.Reset();
var progressDone = new AutoResetEvent(false);
int progress = 0;
using(var timer = new System.Threading.Timer(_=>
{
backgroundWorker1.ReportProgress(progress);
progress += 2;
if (progress>=100)
{
progressDone.Set();
}
}, null, 0, 25))
{
progressDone.WaitOne();
}
stateChangeDone.Set();
}
}
The background worker calls ResportProgress with an int indicating its prpgress. That causes the ProgressChanged event to be raised. Based on what state the Curtain needs to be in, we calculate the correct value for the Opacity.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
switch(state)
{
case Curtain.Up:
this.Opacity = e.ProgressPercentage / 100.0;
break;
case Curtain.Down:
this.Opacity = (100 - e.ProgressPercentage) / 100.0;
break;
}
}
To get this all started we create two public methods called Up and Down:
enum Curtain
{
Up,
Down
}
Curtain state;
public void Up()
{
state = Curtain.Up;
stateChange.Set();
stateChangeDone.Reset();
}
public void Down()
{
state = Curtain.Down;
stateChange.Set();
stateChangeDone.Reset();
}
With that we are only left with the implementation of the Command classes that will be added to the Command queue and handled by the background worker of Form1:
public class RaiseCurtain:ICommandExecutor, IDisposable
{
readonly Form2 form2;
public RaiseCurtain( Form2 form2)
{
this.form2 = form2;
}
public void Execute()
{
if (form2.InvokeRequired)
{
form2.Invoke(new MethodInvoker(Execute));
}
else
{
form2.BringToFront();
form2.Up();
}
}
public void Dispose()
{
form2.stateChangeDone.WaitOne();
}
}
public class LowerCurtain : ICommandExecutor,IDisposable
{
readonly Form2 form2;
public LowerCurtain(Form2 form2)
{
this.form2 = form2;
}
public void Execute()
{
if (form2.InvokeRequired)
{
form2.Invoke(new MethodInvoker(Execute));
}
else
{
form2.Down();
}
}
public void Dispose()
{
form2.stateChangeDone.WaitOne();
}
}
That is it. We have eliminated the use of DoEvents.
There is one caveat: this doesn't guarantee that the application will stop again after a couple of hours/days. The reason for this is a possible memory-leak in the webbrowser control and in my testing I did see the same effect, a slowly but steadily increasing private memory consumption while the managed memory bytes stayed virtually the same.
As none of the posts provided a definitive answer one option could be to restart your app as indicates in one of the answers here. On the plus side, you can implement this now as a Command class...
I'm trying to run the timer tick event which initially runs on my windows form,also when loaded on another class using a thread. I tried calling the timer event on another thread it didn't help. Am I supposed to use the same timer or create a new timer for that thread. This is my current implementation:
namespace XT_3_Sample_Application
{
public partial class Form1 : Form
{
Queue<string> receivedDataList = new Queue<string>();
System.Timers.Timer tmrTcpPolling = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
TM702_G2_Connection_Initialization();
}
static void threadcal()
{
Class1 c = new Class1();
c.timer_start();
c.test("192.168.1.188",9999);
}
public string Connection_Connect(string TCP_IP_Address, int TCP_Port_Number)
{
if (tcpClient.Connected)
{
Socket_Client = tcpClient.Client;
TcpStreamReader_Client = new StreamReader(tcpClient.GetStream(), Encoding.ASCII);
tmrTcpPolling.Start();
Status = "Connected\r\n";
}
else
{
Status = "Cannot Connect\r\n";
}
}
public string Connection_Disconnect()
{
tmrTcpPolling.Stop();
// do something
return "Disconnected\r\n";
}
void TM702_G2_Connection_Initialization()
{
tmrTcpPolling.Interval = 1;
tmrTcpPolling.Elapsed += new ElapsedEventHandler(tmrTcpPolling_Elapsed);
}
#region Timer Event
void tmrTcpPolling_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
if (tcpClient.Available > 0)
{
receivedDataList.Enqueue(TcpStreamReader_Client.ReadLine());
}
}
catch (Exception)
{
//throw;
}
}
private void tmrDisplay_Tick(object sender, EventArgs e)
{
Tick();
}
public void Tick()
{
Console.Write("tick" + Environment.NewLine);
if (receivedDataList.Count > 0)
{
string RAW_Str = receivedDataList.Dequeue();
//tbxConsoleOutput.AppendText(RAW_Str + Environment.NewLine);
tbxConsoleOutput.AppendText(Parser_Selection(RAW_Str) + Environment.NewLine);
}
}
#endregion
private void btnConnect_Click(object sender, EventArgs e)
{
tbxConsoleOutput.AppendText(Connection_Connect(tbxTCPIP.Text, Convert.ToInt32(tbxPort.Text, 10)));
Thread t = new Thread(threadcal);
t.Start();
}
}
}
But the timer tick event starts the moment the application is launched but not on button click - private void btnConnect_Click(object sender, EventArgs e).
I'm trying to call a separate thread for the class Class1's test method. I'm trying to use a similar timer event to receive output from the server, for this thread.
namespace XT_3_Sample_Application
{
class Class1
{
TcpClient tcpClient;
Socket Socket_Client;
StreamReader TcpStreamReader_Client; // Read in ASCII
Queue<string> receivedDataList = new Queue<string>();
System.Timers.Timer tmrTcpPolling = new System.Timers.Timer();
void TM702_G2_Connection_Initialization()
{
tmrTcpPolling.Interval = 1;
tmrTcpPolling.Elapsed += new ElapsedEventHandler(tmrTcpPolling_Elapsed);
}
public void test(string TCP_IP_Address, int TCP_Port_Number)
{
TM702_G2_Connection_Initialization();
try
{
string Status = "";
Ping pingSender = new Ping();
PingOptions options = new PingOptions();
PingReply reply = pingSender.Send(TCP_IP_Address, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
tcpClient = new TcpClient();
tcpClient.Connect(TCP_IP_Address, TCP_Port_Number);
if (tcpClient.Connected)
{
Socket_Client = tcpClient.Client;
TcpStreamReader_Client = new StreamReader(tcpClient.GetStream(), Encoding.ASCII);
tmrTcpPolling.Start();
Status = "Connected\r\n";
}
else
{
Status = "Cannot Connect\r\n";
}
}
else
{
Status = "Ping Fail\r\n";
}
MessageBox.Show(TCP_IP_Address + " :" + Status);
}
catch (System.Exception ex)
{
MessageBox.Show(TCP_IP_Address + " :" + ex.Message);
}
setFilterType();
setButtonRadioLvl();
heloCmd();
}
public void timer_Start()
{
Form1 f = new Form1();
f.Tick();
}
}
}
When tried the above method the timer is not fired on the new thread. Any suggestions on this?
Without any blocking code or loop your thread will not live long. the following calls your test method every one second and doesn't use timer
static void threadcal()
{
while (true)
{
Thread.Sleep(1000);
Class1 c = new Class1();
c.test("192.168.1.188", 9999);
}
}
I want to know when all my async threads have completed so I know when to close my loading form. My code never closes the loading form. I don't know why. I'm unsure how to correctly pass my ManualResetEvent object to the async thread too.
I'm also open to a simpler means to achieve my goal of knowing when to close the loading form.
UPDATE
After reading the advice here I've updated my class. Unfortunetly, it still does not work. I feel closer though. It's just that the callback never fires.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;
namespace BrianTests
{
public class TaskInfo
{
public RegisteredWaitHandle Handle;
public string OtherInfo = "default";
public Form loading;
}
public partial class AsyncControlCreateTest : Form
{
//List<ManualResetEvent> MREs = new List<ManualResetEvent>();
Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
CountdownWaitHandle cdwh;
public AsyncControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Load(object sender, EventArgs e)
{
loading.Show(this);//I want to close when all the async threads have completed
CreateControls();
}
private void CreateControls()
{
int startPoint= 0;
int threadCount = 2;
cdwh = new CountdownWaitHandle(threadCount);
for (int i = 0; i < threadCount; i++)
{
ManualResetEvent mre = new ManualResetEvent(initialState: true);
UserControl control = new UserControl() { Text = i.ToString() };
control.Load += new EventHandler(control_Load);
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
//MREs.Add(mre);
//mre.Set();//just set here for testing
}
Task.Factory.StartNew(new Action(() =>
{
TaskInfo info = new TaskInfo();
info.loading = loading;
try
{
info.Handle = ThreadPool.RegisterWaitForSingleObject(cdwh, WaitProc, info, 4000, executeOnlyOnce: false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}));
}
public static void WaitProc(object state, bool timedOut)
{//this callback never occurs...
TaskInfo ti = (TaskInfo)state;
string cause = "TIMED OUT";
if (!timedOut)
{
cause = "SIGNALED";
// If the callback method executes because the WaitHandle is
// signaled, stop future execution of the callback method
// by unregistering the WaitHandle.
if (ti.Handle != null)
ti.Handle.Unregister(null);
}
Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
ti.OtherInfo,
Thread.CurrentThread.GetHashCode().ToString(),
cause
);
ti.loading.Close();
}
void control_Load(object sender, EventArgs e)
{
RichTextBox newRichTextBox = new RichTextBox();
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(2000);
newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
cdwh.Signal();
}));
}
}
public class CountdownWaitHandle : WaitHandle
{
private int m_Count = 0;
private ManualResetEvent m_Event = new ManualResetEvent(false);
public CountdownWaitHandle(int initialCount)
{
m_Count = initialCount;
}
public void AddCount()
{
Interlocked.Increment(ref m_Count);
}
public void Signal()
{
if (Interlocked.Decrement(ref m_Count) == 0)
{
m_Event.Set();
}
}
public override bool WaitOne()
{
return m_Event.WaitOne();
}
}
}
The problem is that WaitHandle.WaitAll is throwing an exception, which you can see:
try
{
WaitHandle.WaitAll(MREs.ToArray());
}
catch (Exception e) {
MessageBox.Show(e.Message);
throw;
}
The error message is that "WaitAll for multiple handles on a STA thread is not supported."
If you do something like
foreach(var m in MREs)
m.WaitOne();
It will work.
I'm not quite sure why the exception did not crash the application, as I would have hoped. See perhaps How can I get WinForms to stop silently ignoring unhandled exceptions? for this.
Locking the MRE's and moving the WaitAll off the STA thread does the trick.
public partial class AsyncControlCreateTest : Form
{
object locker = new object();
static List<ManualResetEvent> MREs = new List<ManualResetEvent>();
Form loading = new Form() { Text = "Loading...", Width = 100, Height = 100 };
public AsyncControlCreateTest()
{
InitializeComponent();
}
private void AsyncControlCreateTest_Load(object sender, EventArgs e)
{
loading.Show(this);//I want to close when all the async threads have completed
CreateControls();
}
private void CreateControls()
{
int startPoint= 0;
for (int i = 0; i < 100; i++)
{
ManualResetEvent mre = new ManualResetEvent(initialState: false);
UserControl control = new UserControl() { Text = i.ToString() };
control.Load += new EventHandler(control_Load);
Controls.Add(control);
control.Top = startPoint;
startPoint += control.Height;
MREs.Add(mre);
}
Task.Factory.StartNew(new Action(() =>
{
try
{
WaitHandle.WaitAll(MREs.ToArray());
}
catch (Exception ex)
{
MessageBox.Show("error " + ex.Message);
}
finally
{
MessageBox.Show("MRE count = " + MREs.Count);//0 count provides confidence things are working...
loading.Invoke(new Action( () => loading.Close()));
}
}));
}
void control_Load(object sender, EventArgs e)
{
RichTextBox newRichTextBox = new RichTextBox();
UserControl control = sender as UserControl;
control.Controls.Add(newRichTextBox);
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(500);
newRichTextBox.Invoke(new Action(() => newRichTextBox.Text = "loaded"));
lock (locker)
{
var ev = MREs.First();
MREs.Remove(ev);
ev.Set();
}
}));
}
}
This is my first post here, but I've using this site regularly to help me with my own app's, and I should say that this site has been a great help to me, so thanks to everyone.
Now my question:
I'm developing my first software app that exchanges data between a sql server and the app itself. It's beeing developed in C#. Saving or retreiving data from the sql server database is no problem.
What I want is a way to inform the user of the delay between the local machine (where the app is installed) and the server. I can make some animations or simply display some text messages. What I need help with is how to create the code that activates/fires/runs when that server communication time is running.
If you can't understand the idea, picture a video game. When it's loading (in some games) you can see the loading screen before the game starts. I need some code that displays that "loading window" when the the app is downloading or uploading data from/to the server.
I would appreciate any code example or web site recommendation.
PS: Sorry for the extensive text, but I want to make sure everyone understand so I don't have to repeat it again :P
How do I implement a progress bar in C#?
How to create a smooth progress bar in Visual C#
ProgressBar Class
I have developed a simple PleaseWait class 2 years ago, but I didn't update this class, It works very well, have look hope this will give you an idea to implement your logic.
public partial class frmWait : Form
{
public frmWait()
{
InitializeComponent();
}
bool _isMoving = false;
int _moveStart_x = 0;
int _moveStart_y = 0;
private void tmrProgress_Tick(object sender, EventArgs e)
{
if (barProgress.Value == barProgress.Maximum)
barProgress.Value = barProgress.Minimum;
else
barProgress.Value += 1;
}
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
PleaseWait.Abort();
}
protected override CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams p = base.CreateParams;
p.ClassStyle += 0x20000;
p.ExStyle += 0x8000000;
return p;
}
}
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 132;
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
if (m.Result.ToInt32() == 1)
m.Result = new IntPtr(2);
break;
}
}
private void panelEx1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_isMoving = true;
_moveStart_x = e.X;
_moveStart_y = e.Y;
}
}
private void panelEx1_MouseUp(object sender, MouseEventArgs e)
{
_isMoving = false;
}
private void pnlContainer_MouseMove(object sender, MouseEventArgs e)
{
if (_isMoving)
this.Location = new Point(Location.X + e.X - _moveStart_x, Location.Y + e.Y - _moveStart_y);
}
}
public class PleaseWait
{
#region Static Operations
private static Boolean _isAborted = false;
private static Boolean _isVisible = false;
private static frmWait _waitForm;
private static String _waitingState = "";
private static Boolean _autoClose = false;
private static Boolean _cancelable = false;
private static System.Threading.Thread _waiterThred;
public delegate void CancelButtonPressed();
public static event CancelButtonPressed OnCancel;
public static Boolean AutoClose
{
get { return PleaseWait._autoClose; }
set { PleaseWait._autoClose = value; }
}
public static string WaitingState
{
get { return PleaseWait._waitingState; }
set { PleaseWait._waitingState = value; }
}
public static bool IsVisible
{
get { return _isVisible; }
internal set { _isVisible = value; }
}
public static void ShowPleaseWait()
{
ShowPleaseWait("", _autoClose, false);
}
public static void ShowPleaseWait(string waitingState)
{
ShowPleaseWait(waitingState, _autoClose, false);
}
public static void ShowPleaseWait(bool autoClose)
{
ShowPleaseWait("", autoClose, false);
}
public static void ShowPleaseWait(string waitingState, bool autoClose, bool cancelable)
{
if (_waiterThred != null)
{
if (_isVisible)
{
// the please wait it woking, just continue and apply the changes
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
return;
}
else
{
_waiterThred.Abort();
_waiterThred = null;
}
}
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
_isAborted = false;
_isVisible = false;
if (_autoClose)
Application.Idle += new EventHandler(Application_Idle);
_waiterThred = new System.Threading.Thread(DisplayWaitingForm);
_waiterThred.IsBackground = true;
_waiterThred.Name = "Please Wait....";
_waiterThred.Start();
Application.DoEvents();
}
public static void Abort()
{
_isAborted = true;
}
private static void Application_Idle(object sender, EventArgs e)
{
if (_autoClose)
_isAborted = true;
}
private static void DisplayWaitingForm()
{
if (_waitForm != null)
{
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
_isVisible = false;
}
try
{
if (_isAborted)
return;
_waitForm = new frmWait();
if (_cancelable)
{
_waitForm.btnCancel.Enabled = true;
_waitForm.btnCancel.Click += new EventHandler(btnCancel_Click);
}
try
{
_isVisible = true;
_waitForm.Show();
_waitForm.Focus();
while (!_isAborted)
{
System.Threading.Thread.Sleep(15);
_waitForm.lblMessage.Text = _waitingState;
Application.DoEvents();
_waitForm.lblMessage.Text = _waitingState;
}
_isVisible = false;
}
finally
{
FreeWaitingForm();
}
}
finally
{
_isVisible = false;
}
}
static void btnCancel_Click(object sender, EventArgs e)
{
if (_waitForm.InvokeRequired)
{
_waitForm.BeginInvoke(new EventHandler(btnCancel_Click), new object[] { e });
}
else
{
if (OnCancel != null)
OnCancel.Invoke();
}
}
private static void FreeWaitingForm()
{
_waitingState = "";
_isVisible = false;
if (_waitForm == null)
{
return;
}
_waitForm.Hide();
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
}
#endregion
}
use like following code :
PleaseWait.ShowPleaseWait("Please wait", true, false);
// If second param is true then it will close the form automatically.
// If third param is true the it will expose a cancel button, so you can cancel your Asynchronous operations.
I didn't insert design code, you can understand by looking at code.
hope this help.
First let me thank you for your replies.
Toby your answer got me thinking about thread monitoring my sql connections but it was a bit tricky and confusing since the app is still in develop and will use a lot more connections.
S.Amani answer it wasn't quite what I want, but thanks to that I found a easier way. I created a form (could be anything else), placed a label saying: Saving To Data Base, took out the top bar, defined location and defined it's parent to be disabled when shown and enabled when closed. The following code is what I put inside my DataBaseInteractionClass
private Wait myCustomWaitDialog = new Wait(); // My Waiting form
private void SaveToDatabase(myObjectToSave obj) // Method called to save data do DB
{
// Create the connections and queries
(...)
// This is what I did
// Show Waiting Form
myCustomWaitDialog.Show();
// Instanciate the command that will carry the query and to DB
SqlCommand command = new SqlCommand(Queries.GetData(code), conn);
// This is important
//Create event that will fire when the command completes
command.StatementCompleted += new StatementCompletedEventHandler(command_StatementCompleted);
// Execute the transaction
SqlDataReader reader = command.ExecuteReader();
// Rest of the code (validations, close connections, try/catch, etc
(...)
}
void command_StatementCompleted(object sender, StatementCompletedEventArgs e)
{
// This is the method that closes my Waiting Dialog
myCustomWaitDialog.CloseDialog();
myCustomWaitDialog.Dispose();
}
It's not quite what I want yet, but is the best solution that I found so far. For now it will do :)
Anyway, thanks for the replies and I hope this helps someone else.