Timer usage in Thread Pool - c#

I am developing a windows application for my company that runs on the server. It is a multi threaded application, and i am using Thread Pool for that.
My Application Email module consists of 3 major methods. 1st method gets new campaigns from database, second method decides to whom the campaign is going to be sent via email and third method sends it.
When I start the application, 1st method goes into Thread Pool, if there is a new campaign, 2nd method is invoked with the campaign info. But while these all are happening, first method has to check database in every three seconds if there is a new campaign or not.
I am not sure if I have to use System.Windows.Forms.Timer class for that or System.Threading.Timer??
And I am not sure how to implement it? Am I going to use Invoke Function to invoke thread outside the main UI? Could you please post an example code and suggest best practices??
Thanks a lot
Here is my code :
private void btnStart_MouseClick(object sender, MouseEventArgs e)
{
smartThreadPool = new SmartThreadPool();
workItemGroup = smartThreadPool.CreateWorkItemsGroup(1);
workItemGroup.QueueWorkItem(CheckNewCampaigns);
//smartThreadPool.QueueWorkItem(new WorkItemCallback(this.CheckNewCampaigns));
}
private object CheckNewCampaigns(object state) // 1st method
{
StringBuilder builder = new StringBuilder();
IEnumerable<Campaigns> CampaignsList = DatabaseManager.GetCampaignsList(DatabaseManager.GetNewCampaigns());
foreach (Campaigns Campaign in CampaignsList)
{
builder.AppendFormat("New Campaign Arrived($) --> {0}\r\n", DateTime.Now.ToLongTimeString());
builder.AppendFormat("CampaignID --> {0}\r\n", Campaign.CampaignID);
builder.AppendFormat("CustomerID --> {0}\r\n", Campaign.CustomerID);
builder.AppendFormat("ClientID --> {0}\r\n", Campaign.ClientID);
builder.AppendFormat("Title --> {0}\r\n", Campaign.Title);
builder.AppendFormat("Subject --> {0}\r\n", Campaign.Subject);
builder.AppendFormat("Status --> {0}\r\n", Campaign.Status);
}
Console.WriteLine(builder.ToString());
workItemGroup.QueueWorkItem(new WorkItemCallback(this.PrepareCampaignEmail), 2);
return true;
}
private object PrepareCampaignEmail(object CampaignID) // Second Method
{
int campaignID = (int)CampaignID;
IEnumerable<Campaigns> CampaignDetailsList = DatabaseManager.GetCampaignsList(DatabaseManager.GetCampaignDetails(campaignID)); // bir tane campaign gelmekte
IEnumerable<Subscribers> SubscribersList = DatabaseManager.GetCampaignSubscribersList(DatabaseManager.GetCampaignSubscribers(campaignID));
ArrayList test = new ArrayList();
DataTable dtCustomValuesForCampaign = DatabaseManager.GetCustomValuesForCampaign(campaignID);
foreach (Subscribers subscriber in SubscribersList)
{
workItemGroup.QueueWorkItem(new WorkItemCallback(this.SendEmail), subscriber.Email);
}
return true;
}

In your situation, since it's a Windows Forms application and you'll potentially want to update the UI in the timer event handler, I'd suggest using Windows.Forms.Timer.
Using Windows.Forms.Timer is pretty easy. In the design view of your form, select the Timer from the Toolbox and drop it on your form. Then, click on it to set the properties. You want to set Interval to 3000 (that's 3000 milliseconds), and Enabled to False.
On the Events tab, double-click the Tick event and the IDE will create a handler for you. You want the event handler to look something like this:
private void timer1_Tick(object sender, EventArgs e)
{
var stateObj = // create your state object here
CheckNewCampaigns(stateObj);
}
You'll need to start the timer (set Enabled to True). You can do that in your Create event handler, or you can enable it when the user hits the Start button. You can also stop it at any time by setting Enabled to False.

System.Thread.Timer and Windows.Forms.Timer act differently.
The System.Thread.Timer will run on a system thread. It has an internal thread pool, so it won't be run on one of the threads you explicitly created. Every interval, one of the threads in the Timer's pool will run the callback you initialized the object with.
The Windows.Forms.Timer will raise the Tick event on the current thread and will do so every interval, until you disable it.
I can't tell you which is more appropriate for your situation; it depends on whether you want to run the timer on the UI thread, as well as other factors.

Try this:
public void EnableTimer()
{
if (this.InvokeRequired)
this.Invoke(new Action(EnableTimer));
else
this.timer1.Enabled = true;
}

Related

WPF, how to implement async/await?

I'm learning how to webscrape in WPF. I check the site every 20sec, update my ObservableCollection (myClients) according to search results and display it in Listview (myList). I have 2 Buttons, one to start search and one to stop it.
I didn't know how to implement button autoclick every X sec (which would solve all my problems, am i right?) so i had to use Task.Delay(20000). Program works, it doesn't freeze right at the start like if i had used Thread.Sleep(), but if i press the Stop button and then Start, everything freezes.
I will upload only portion of the code that seems to be the problem. Note that the whole program at the moment is mostly reverse-engineered from several different programs as i am still a beginner.
private async void Button_Click(object sender, RoutedEventArgs e) //Start button
{
string car;
string price;
string link;
wantToAbort = false;
while (!wantToAbort)
{
// ----Simulate GET request----
//-----End GET----
myList.ItemsSource = myClients;
string searchCar = txtBlock.Text + " " + txtBlock2.Text;
var articleNodes = htmlDoc.DocumentNode.SelectNodes($"//*[#id='main_content']/div[1]/div[2]/ul[1]//*[text()[contains(., '{searchCar}')]]");
if (articleNodes != null && articleNodes.Any())
{
foreach (var articleNode in articleNodes)
{
car = WebUtility.HtmlDecode(articleNode.InnerText);
price = WebUtility.HtmlDecode(articleNode.ParentNode.ParentNode.SelectSingleNode("span").InnerText);
link = WebUtility.HtmlDecode(articleNode.ParentNode.ParentNode.Attributes["href"].Value);
var tempUser = new User(car, price, link);
if (!myClients.Any(x=>x.Link == tempUser.Link))
{
myClients.Insert(0, tempUser); //Inserts new item if Links are different
txtBlock3.Text = "Searching...";
}
}
await Task.Delay(20000); //This seems to be an issue
}
}
}
private void Button_Click_1(object sender, RoutedEventArgs e) //Stop button
{
wantToAbort = true;
txtBlock3.Text = "Ready to search again!";
}
Running a while loop on the UI thread may freeze the application as the UI thread cannot both process UI events and execute a loop or doing anything else simultaneously.
If you want to do something every x seconds you could use a timer as suggested by EJoshuaS. There is a DispatcherTimer class in WPF that fires a Tick event on the UI thread at an interval specified by the Interval property: https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer%28v=vs.110%29.aspx
You don't want to perform the GET request to the web server on the UI thread though so you should probably use a System.Timer.Timer: https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx. This is a different type of timer that runs on a background thread.
Since you can only access UI controls such as TextBlocks and ListBoxes on the thread on which they were originally created - that is the UI thread - you will have to use the dispatcher to marshall any code that access these controls back to the UI thread in your Elapsed event handler:
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
//call the web server here....
//dispatch any access to any UI control
txtBlock3.Dispatcher.Invoke(new Action(() = > { txtBlock3.Text = "Searching..."; }));
}
The golden rule to maintain a responsive application is to execute any long-running code on a background thread but you must only access UI controls back on the UI thread. Please refer to MSDN for more information about the threading model in WPF: https://msdn.microsoft.com/en-us/library/ms741870(v=vs.110).aspx
DispatcherTimer may be a better solution in this case, like in the below example:
public partial class MainWindow : Window
{
private DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 220);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
// Do something on your UI
Trace.TraceInformation("Timer expired");
}
}
Basically, this will raise an event at a given interval. Note that Windows Forms also has a timer, as does System.Threading, but you want to make sure you use DispatcherTimer rather than those. In particular, the one from System.Threading tends not to mix well with UIs because it runs its actions on the thread pool and WPF in particular is very fussy about how you update your UI from background threads.
The documentation I link to, as well as this answer, also give details on this.

C# Invoke button control on separate thread

I have seen a lot of questions about how to edit controls on c# form from a different thread but none make much sense to me. I understand that you can not change any UI from another thread than it's main. To make this work you have to use invoke and from there safely edit the control?
I have a button that starts writing in a file and the moment you press the button the button itself gets disabled so you can not start multiple threads that do exactly the same. When the writing is done I want the button to be available again but I can not get it working on this other thread.
I have this as the Generate_Click event from the form.
private void Generate_Click(object sender, EventArgs e)
{
Generate.Enabled = false;
int x = 512;
int y = 512;
MBrot mbrot = new MBrot(x, y);
PB_Update lb = new PB_Update(0, y, Generator_PB, Generate, mbrot, this);
lb.Start();
}
And this is in PB_Update.cs the ThreadWork() function, when the while loop is done the writing to the file is done and so is the thread so its ended and given a messagebox with "finished" now as last the button needs to be enabled again.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.Enabled = true;
}
For WinForms you can execute directly on the thread which the control was created on through the Control.BeginInvoke method, you can use Control.Invoke as well but, Control.BeginInvoke is preferred for UI operations.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.BeginInvoke((Action)delegate()
{
Generate_.Enabled = true;
});
}
Somehow, get a reference to the form that hosts the generate_ button (let's call it myform). Then, at the bottom of your ThreadWork:
myform.Invoke(new Action(() => {
myform.SetGenerateEnabled();
}));
And then inside your form create that method that enables the button appropriately. (I used a method rather than just updating the button directly so that you don't publicly expose the button.)
This executes the commands inside the { ... } on myform's thread, which is a UI thread, because it is UI. At least, that's what I understand. This is how I do all of my UI updating from other threads.
Here's a simple example of a way to kick off an async task that disables a button for 5 seconds and then enables it again. Meanwhile, the rest of the UI is functional.
Note that this async method exists in the same class as your Generate_Click event, and runs on the UI thread. This means that it can enable and disable the button. But the long running task executes on a separate thread, so it doesn't lock the UI.
Hopefully this sample provides you a base to modify for your own code:
private void Generate_Click(object sender, EventArgs e)
{
DisableButton(sender as Button, 5);
}
private async void DisableButton(Button sender, int secondsToDisable)
{
sender.Enabled = false;
// In your code, you would kick off your long-running process here as a task
await Task.Run(()=>Thread.Sleep(TimeSpan.FromSeconds(secondsToDisable)));
sender.Enabled = true;
}

How to implement a popup window with a counter

I would like to implement a simple popup window in Windows Forms, which will show a simple timer to the user while some slow-running process is executing. The premise is simple; show to the user that something is indeed going on and the application is not frozen. Note that this slow-running process is not a loop, nor is it something that I can tap into.
What I want is a simple popup window, showing some message along the lines "Elapsed time: x seconds", where x is incremented every second.
The basic concept is the following:
public void test()
{
//Some code which does stuff
//Popup window with counter
//Perform long running process
//Close popup window with counter
//Some other code which does other stuff
}
I tried to do it using various ways, including background workers, threads, and of course timers. But I did not manage to make it work as I wanted. And I would prefer not to post any of my code so as not to "lead" the responses to a specific way of doing this.
So what would be the best way to do this work?
Thanks.
UPDATE:
In reply to some comments, since I cannot paste any code in the replies section, I'm editing my original question to accomodate this. One of the implementations that I tried is to spawn the popup window in a separate thread. Although I got no runtime errors, the popup window did not refresh correctly. It indeed poped-up, but no text would show within it, and the counter would not refresh. Here's the code:
private void test()
{
frmProgressTimer ofrmProgressTimer = new frmProgressTimer(); //Instance of popup Form
System.Threading.Tasks.Task loadTask = new System.Threading.Tasks.Task(() => ProgressTimer(ofrmProgressTimer));
loadTask.Start();
//Perform long running process
System.Threading.Tasks.Task cwt = loadTask.ContinueWith(task => EndProgressTimer(ofrmProgressTimer));
}
private void ProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Show();
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.startTimer();
}));
}
private void EndProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
}
And here's my popup form code:
public partial class frmProgressTimer : Form
{
private int counter = 0;
private Timer timer1;
public frmProgressTimer()
{
InitializeComponent();
timer1 = new Timer();
timer1.Interval = 1000;
timer1.Tick += new EventHandler(timer1_Tick);
}
public void startTimer()
{
timer1.Start();
}
public void stopTimer()
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
counter += 1;
labelText.Text = counter.ToString();
}
}
This is actually quite easy to do. Create your dialog, define your long running operation to take place in a non-UI thread when it is shown, add a continuation to that operation which closes the dialog when the task finishes, and then show the dialog.
MyDialog dialog = new MyDialog();
dialog.Shown += async (sender, args) =>
{
await Task.Run(() => DoLongRunningWork());
dialog.Close();
};
dialog.ShowDialog();
The code to have the ticking over time should be entirely contained within the dialog, and based on the question it seems you already have that well under control with a simple Timer.
Make a new form, which will pop up, and show a timer. That way it won't be interrupted with all the work on your main form, and the timer will work continuously.
Remember when showing a new from to use newForm.ShowDialog() not newForm.Show(). Your can google the differences
I would simply start your work on a separate thread. Launch a modal form with your timer output. To display the timer use an actual timer instance set to update every second. When the timer event fire update your dialog.
Finally once you're thread completes close the dialog so your main form is active again.
First of all you need to make it not closeable by the user (as if modal dialogs weren't annoying enough) but closeable by your code. You could accomplish this by subscribing to the FormClosing event of the form. Let's say your popup form's name is Form2:
private bool mayClose = false;
public void PerformClose()
{
this.mayClose = true;
this.Close();
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (!this.mayClose)
e.Cancel = true;
}
Create a Timer, provide a Tick event handler, enable it and set its Interval to 500 milliseconds:
Create a label to host your desired text. Let's call it label1.
Within and surrounding your Tick event handler do something like this:
private DateTime appearedAt = DateTime.UtcNow;
private void timer1_Tick(object sender, EventArgs e)
{
int seconds = (int)(DateTime.UtcNow - this.appearedAt).TotalSeconds;
this.label1.Text = string.Format(#"Ellapsed seconds: {0}", seconds);
}
Make sure your long running process is happening on a background thread, not on the GUI thread.
Say your long running process can be thought of as the execution of a method called MyProcess.
If that is the case, then you need to call that method from a secondary thread.
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
Show the form right before the long running process starts. This can be done in any of the 2 places (marked in the previous section of code) called PLACE1 or PLACE2. If you do it in PLACE2 you will have to marshal a call back to the GUI thread in order to be able to interact with the WinForms framework safely. Why am I bringing this up ? It's because maybe the long running process is not started from within the GUI thread at all and you absolutely need to do this.
Close the form right after the long running process finishes. This can be done only in PLACE3 and you absolutely need to marshal a call.
To wrap the earlier 2 bullets and the answer, you could do this:
private void DoIt()
{
Form2 form2 = new Form2();
Action showIt = () => form2.Show();
Action closeIt = () => form2.PerformClose();
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
form2.BeginInvoke(showIt);
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
form2.BeginInvoke(closeIt);
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
}
Finally I've managed to resolve this in the most simplistic manner. And it works like a charm. Here's how to do it:
//Create an instance of the popup window
frmProgressTimer ofrmProgressTimer = new frmProgressTimer();
Thread thread = new Thread(() =>
{
ofrmProgressTimer.startTimer();
ofrmProgressTimer.ShowDialog();
});
thread.Start();
//Perform long running process
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
You can see the code for the popup window in the original post/question, with the only difference that the tick function changes the label text as:
labelText.Text = string.Format("Elapsed Time: {0} seconds.", counter.ToString());
Thank you to everybody for trying to help me out.

c# threading with manual reset events

I have an application that imports data read from text files from a directory into a database. I have a UI that allows the user to click an import button and begin importing data and when the user clicks on that button again I wanted to stop importing the data in those files. I began using threads to allow this, so that I would not freeze up the UI while data was being imported. But Im having a few issues. I started using thread.Abort() to kill the thread after the user stops wanting to import but when the user clicks import again, some duplicate data is added to the database because it begins reading at the top of the text file which I dont want. I have been told to use ManualResetEvents and Thread.Join() to trigger for the import to finish, but im confused how that is supposed to work. Right now my code looks like this:
public ManualResetEvent event1 = new ManualResetEvent(false);
public Thread workerThread;
public Form1
{
InitializeComponent();
}
private void importButton_Click(object sender, EventArgs e)
{
if(importButton.Text == "Begin Import")
{
importButton.Text = "Stop Import";
//runs a service that begins reading and importing data and writing to
//a "console" box.
Service service = new Service(consoleBox);
//run the new thread to begin importing data
workerThread = new Thread(service.importData);
workerThread.Start();
}
else
{
importButton.Text = "Begin Import";
event1.Set();
while(!event1.WaitOne(TimeSpan.FromSeconds(4)))
{ //imports data for 30 more text files
service.importData(30);
workerThread.Join();
}
}
}
Basically what im trying to do is to keep the tread looping and checking to see if there is any files to be read, if there is then import The Data otherwise sleep for 4 seconds. Should I be using a threading Timer for this? I am a bit unsure of what to do.
Do not, in any way, block the UI thread by calling Thread.Join or ManualResetEvent.WaitOne. This will do exactly what you were trying to prevent; freeze the UI. Instead you need to create the MRE with its state initially set to true. Then in the importData method you need to periodically call WaitOne to see if importing should proceed (when the event is signaled) or pause (when the event is unsignaled).
Here is a rough sketch of how you would call WaitOne inside the importData method. Obviously, you would need to make adjustments to fit it into your specific implementation.
private void importData()
{
foreach (string filePath in GetFilesInSomeDirectory())
{
event1.WaitOne(); // Block when the MRE is unsignaled.
}
}
Then from your importButton.Click event handler you can call event1.Reset to pause the import operation or event1.Set to resume it.
Also, you should try to avoid calling Thread.Abort at all costs. It usually leads to more problems unless extra-special-nearly-impossible care is taken to avoid corrupting the state of the AppDomain.
Use timer for running the import process instead of thread, and define a variable to check if user request to stopinstead of thread.Abort() which by the way should be avoided.
In this code use System.Timers.Timer. and flag AutoReset property to false, so only import data if user not request to stop.
private System.Timers.Timer _importTimer = new System.Timers.Timer();
private volatile bool _requestStopImport = false;
public Form1()
{
InitializeComponent();
_importTimer.Interval = 4000;//4 seconds
_importTimer.AutoReset = false;//not automatically raise elapse event each time interval elapsed, only if we don't want to stop.
_importTimer.Elapsed += OnImportTimerElapced;
}
private void importButton_Click(object sender, EventArgs e)
{
if (importButton.Text == "Begin Import")
{
importButton.Text = "Stop Import";
StartImport();
}
else
{
importButton.Text = "Begin Import";
StopImport();
}
}
private void OnImportTimerElapced(object sender, System.Timers.TimerEventArgs e)
{
//runs a service that begins reading and importing data and writing to
//a "console" box.
Service service = new Service(consoleBox);//or maybe this would be a class level variable
service.importData();
if (!_requestStopImport)
{
_importTimer.Start();
}
}
private void StartImport()
{
_requestStopImport = false;
_importTimer.Start();
}
private void StopImport()
{
_requestStopImport = true;
_importTimer.Stop();
}
As you notice you don't have to use ManualResetEvent here. however if you want to be notified when code is completed or so you can use AutoResetEvent or raise an event for more detailed example check this.

Pause execution of a method without locking GUI. C#

I'm working on a card game in C# for a project on my Intro to OOP paper and have got the game working now but am adding "flair" to the GUI.
Currently cards are dealt and appear on the UI instantaneously. I want to have to program pause for a moment after dealing a card before it deals the next.
When a game is started the following code runs to populate the PictureBoxes that represent them (will be a loop eventually):
cardImage1.Image = playDeck.deal().show();
cardImage2.Image = playDeck.deal().show();
cardImage3.Image = playDeck.deal().show();
cardImage4.Image = playDeck.deal().show();
cardImage5.Image = playDeck.deal().show();
...
I have tries using System.Threading.Thread.Sleep(100); between each deal().show() and also inside each of those methods but all it achieves is locking up my GUI until all of the sleeps have processed then display all of the cards at once.
I have also tried using a combination of a timer and while loop but it resulted in the same effect.
What would be the best way of achieving the desired result?
The problem is that any code that you run on the UI will block the UI and freeze the program. When your code is running (even if it's running Thread.Sleep), messages (such as Paint or Click) sent to the UI will not be processed (until control returns to the message loop when you exit your event handler), causing it to freeze.
The best way to do this is to run on a background thread, and then Invoke to the UI thread between sleeps, like this:
//From the UI thread,
ThreadPool.QueueUserWorkItem(delegate {
//This code runs on a backround thread.
//It will not block the UI.
//However, you can't manipulate the UI from here.
//Instead, call Invoke.
Invoke(new Action(delegate { cardImage1.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage2.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage3.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
//etc...
});
//The UI thread will continue while the delegate runs in the background.
Alternatively, you could make a timer and show each image in the next timer tick. If you use a timer, all you should do at the beginning is start the timer; don't wait for it or you'll introduce the same problem.
Normally I'd simply recommend a function like this to perform a pause while allowing the UI to be interactive.
private void InteractivePause(TimeSpan length)
{
DateTime start = DateTime.Now;
TimeSpan restTime = new TimeSpan(200000); // 20 milliseconds
while(true)
{
System.Windows.Forms.Application.DoEvents();
TimeSpan remainingTime = start.Add(length).Subtract(DateTime.Now);
if (remainingTime > restTime)
{
System.Diagnostics.Debug.WriteLine(string.Format("1: {0}", remainingTime));
// Wait an insignificant amount of time so that the
// CPU usage doesn't hit the roof while we wait.
System.Threading.Thread.Sleep(restTime);
}
else
{
System.Diagnostics.Debug.WriteLine(string.Format("2: {0}", remainingTime));
if (remainingTime.Ticks > 0)
System.Threading.Thread.Sleep(remainingTime);
break;
}
}
}
But there seems to be some complication in using such a solution when it is called from within an event handler such as a button click. I think the system wants the button click event handler to return before it will continue processing other events because if I try to click again while the event handler is still running, the button depresses again even though I'm trying to drag the form and not click on the button.
So here's my alternative. Add a timer to the form and create a dealer class to handle dealing with cards by interacting with that timer. Set the Interval property of the timer to match the interval at which you want cards to be dealt. Here's my sample code.
public partial class Form1 : Form
{
CardDealer dealer;
public Form1()
{
InitializeComponent();
dealer = new CardDealer(timer1);
}
private void button1_Click(object sender, EventArgs e)
{
dealer.QueueCard(img1, cardImage1);
dealer.QueueCard(img2, cardImage2);
dealer.QueueCard(img3, cardImage1);
}
}
class CardDealer
{
// A queue of pairs in which the first value represents
// the slot where the card will go, and the second is
// a reference to the image that will appear there.
Queue<KeyValuePair<Label, Image>> cardsToDeal;
System.Windows.Forms.Timer dealTimer;
public CardDealer(System.Windows.Forms.Timer dealTimer)
{
cardsToDeal = new Queue<KeyValuePair<Label, Image>>();
dealTimer.Tick += new EventHandler(dealTimer_Tick);
this.dealTimer = dealTimer;
}
void dealTimer_Tick(object sender, EventArgs e)
{
KeyValuePair<Label, Image> cardInfo = cardInfo = cardsToDeal.Dequeue();
cardInfo.Key.Image = cardInfo.Value;
if (cardsToDeal.Count <= 0)
dealTimer.Enabled = false;
}
public void QueueCard(Label slot, Image card)
{
cardsToDeal.Enqueue(new KeyValuePair<Label, Image>(slot, card));
dealTimer.Enabled = true;
}
}
The cheap way out would be to loop with calls to Application.DoEvents() but a better alternative would be to set a System.Windows.Forms.Timer which you would stop after the first time it elapses. In either case you'll need some indicator to tell your UI event handlers to ignore input. You could even just use the timer.Enabled property for this purpose if it's simple enough.
I would try puting the code that deals the deck ( and calls Thread.Sleep) in another thread.

Categories

Resources