I have Image control with PreviewMouseLeftButtonDown event handled.
The logic is to change image content when single click occured and to activate other visual style when double click occured.
I know about ClickCount property like some answers said (e.g. this) and successfully distinguish between single/double clicks, but problem is that single click occures always, wherether double click follow or not follow the next moment (which is fair enought, anyway). So, with double click both actions processed - for single click and the next moment for double click.
The question: is there any method to prevent single click before occuring right after that double click, other than handling this situation with some kind of timers magic?
Edit:
I found question with good comment, which makes an analogy with windows explorer - how it wait after single click on selected file, and start renaming just ensured that no other click occured after first click.
Delay will definitely exist in purpose to solve this problem, but does it mean that windows explorer using exactly timer, or maybe it have some other option (some property or event that can be awaited) to hold single click in case double click occured?
Finally there were no suggestions received with timer-unrelated solution (and I didn't find any either), so here is simple example how to prevent single click when double click occurred.
Xaml:
<Window x:Class="StackOverflow.DoubleClickExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="100" Width="150"
MouseDown="RootElement_OnMouseDown">
</Window>
Code-behind:
namespace StackOverflow.DoubleClickExample
{
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
public static extern uint GetDoubleClickTime();
public MainWindow()
{
this.InitializeComponent();
}
private Guid lastGuid = Guid.Empty;
private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
// Create new unique id and save it into field.
var guid = Guid.NewGuid();
this.lastGuid = guid;
// Run task asynchronously for ensuring that there is no another click
// happened in time interval when double-click can occure.
Task.Run(async () =>
{
// Wait system double-click time interval.
await Task.Delay((int)GetDoubleClickTime());
// If no double-click occured in awaited time interval, then
// last saved id (saved when first click occured) will be unchanged.
if (guid == this.lastGuid)
{
// Here is any logic for single-click handling.
Trace.WriteLine("Single-click occured");
}
});
return;
}
// Can be here only when e.ClickCount > 1, so must change last saved unique id.
// After that, asynchronously running task (for single-click) will detect
// that id was changed and so will NOT run single-click logic.
this.lastGuid = Guid.NewGuid();
// Here is any logic for double-click handling.
Trace.WriteLine("Double-click occured");
}
}
}
For testing, make clicks in window area and track messages writing into output window in visual studio (menu View -> Output).
Another way is using CancellationTokenSource and trigger its Cancel method when double-click occured. Just replace lastGuid field and RootElement_OnMouseDown method:
private CancellationTokenSource cancellationTokenSource;
private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
try
{
this.cancellationTokenSource = new CancellationTokenSource();
var token = this.cancellationTokenSource.Token;
// Run task asynchronously for ensuring that there is no another click
// happened in time interval when double-click can occure.
Task.Run(async () =>
{
// Wait system double-click time interval.
await Task.Delay((int)GetDoubleClickTime(), token);
// Here is any logic for single-click handling.
Trace.WriteLine("Single-click occured");
}, token);
}
catch (OperationCanceledException)
{
// This exception always occure when task is cancelled.
// It happening by design, just ignore it.
}
return;
}
// Cancel single-click task.
if (this.cancellationTokenSource != null)
{
this.cancellationTokenSource.Cancel();
}
// Here is any logic for double-click handling.
Trace.WriteLine("Double-click occured");
}
I guess you need to use a timer. For getting the max time that is valid for a double click still to occur you could use following function (tested; output is 500 ms):
[DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
(source: how to get the double-click-time in WPF)
Usually when you have several values you want to bind to one WPF control you use something like ItemsSource and bind it to a List in view model. But I guess that doesn't work for Image control. So you should go with a timer and use the value of the function above for your timer.
Related
I created a custom Toast Notification for my Winforms application, whenever a custom result is returned, I use a ShowMessage extension to display the notification.
On my login screen, I have a unlock application button that will show the toaster message whenever the password is incorrect.
When I put a breakpoint right before the result.ShowMessage(); the notification appears. But when I remove the breakpoint, it does not appear anymore. I do not change any debug values.
How can I resolve this? I did try implementing a thread.sleep but it did not change anything, the notification only appears when I insert a breakpoint and continue over it.
It is worth noting that the notification works perfectly everywhere else in the application.
Code for the Unlock Application:
private void UnlockApplication()
{
var result = new Business.Server.User().Get(_loginModel.UserName, _loginModel.Password.Encrypt());
if (result.IsSuccessful)
{
// Perform log in process
}
else
{
result.ShowMessage(); //Only works when I put breakpoint here
}
}
Code for ShowMessage extension
public static void ShowMessage<T>(this Models.Result<T> result) where T : class
{
Helpers.ToasterNotificationHelper.ShowNotification(result.ResultTypeKey.ToDescription(), result.Message, result.ResultImage(), result.ResultColor(), result.ResultTypeKey == Enums.ResultTypeEnum.Warning ? 5000 : 2500);
}
public static void ShowNotification(string header, string message, Image icon, Color backgroundColor, int durationInMilliseconds = 1000)
{
if (Application.OpenForms[0] is MyApplication)
{
var toasterNotification = new ToasterNotificationControl(header, message, icon, backgroundColor);
(Application.OpenForms[0] as MyApplication).toastNotificationCollectionControl1.AddNotification(toasterNotification, durationInMilliseconds);
}
}
My Toast Notification control is a DevExpress flyout panel control that gets added to the Main application form
Your "Get(_loginModel.UserName ..." seems to be asynchronous. Try to do the next to make the call awaitable:
private async System.Threading.Tasks.Task UnlockApplicationAsync()
{
var result = await new Business.Server.User().Get(_loginModel.UserName, _loginModel.Password.Encrypt());
...
As Peter Duniho points out in a comment here, I was fixated on a red herring when I should have been focusing on something else altogether.
When I use Symbol.Barcode.Reader and Symbol.Barcode.ReaderData in one form, they work fine. I use them as I document here.
However, when I go from one form that uses the barcode scanning code to another one that also does, all Dallas breaks loose. I get the following exception in the second form, on startup:
Symbol.Exceptions.OperationFailureException: SCAN_GetInterfaceParams
at Symbol.Barcode.InterfaceParams.GetInterfaceParams()
at Symbol.Barcode.InterfaceParams..ctor(Reader reader)
at Symbol.Barcode.Actions.Enable()
at HHS.frmFind.InitReader()
at HHS.frmFind.textBoxScan_GotFocus(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnGotFocus(EventArgs e)
at System.Windows.Forms.Control.WnProc(WM wm, Int32 wParam, Int32 lParam)
at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
at Microsoft.AGL.Forms.WL.SetVis(IntPtr hwnThis, BOOL fVis)
at System.Windows.Forms.Control.set_Visible(Boolean value)
at System.Windows.Forms.Form.ShowDialog()
The barcode scanning code between the two forms is identical, so it's not the code itself (it works fine the first time, in the first form).
The exception occurs immediately in the second form when the textbox that is set up for scanning is entered. It's GotFocus() event fires because the textbox gets focus when the form is displayed; OnGotFocus() calls InitReader(), which then fails. InitReader() is:
private bool InitReader()
{
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader");
// If reader is already present then retreat
if (this.barcodeReader != null)
{
return false;
}
// Create new reader, first available reader will be used.
this.barcodeReader = new Symbol.Barcode.Reader();
// Create reader data
this.barcodeReaderData = new Symbol.Barcode.ReaderData(
Symbol.Barcode.ReaderDataTypes.Text,
Symbol.Barcode.ReaderDataLengths.MaximumLabel);
// Create event handler delegate
this.barcodeEventHandler = this.BarcodeReader_ReadNotify;
// Enable reader, with wait cursor
this.barcodeReader.Actions.Enable();
this.barcodeReader.Parameters.Feedback.Success.BeepTime = 0;
this.barcodeReader.Parameters.Feedback.Success.WaveFile = "\\windows\\alarm3.wav";
// Attach to activate and deactivate events
this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;
return true;
}
The objects being dealt with there are in Symbol.Barcode.dll, from Motorola. They are declared in the form like so:
private Symbol.Barcode.Reader barcodeReader;
private Symbol.Barcode.ReaderData barcodeReaderData;
If I bypass the first form, which has the same type of barcode scannig code, and go straight to this form, it doesn't crash.
So apparently it's that the closely related bits of code can't coexist. Why not and, more importantly, how can I prevent this revolting development?
UPDATE
With this logging added to InitReader:
private bool InitReader()
{
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader");
// If reader is already present then retreat
if (this.barcodeReader != null)
{
return false;
}
// Create new reader, first available reader will be used.
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #2");
this.barcodeReader = new Symbol.Barcode.Reader();
// Create reader data
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #3");
this.barcodeReaderData = new Symbol.Barcode.ReaderData(
Symbol.Barcode.ReaderDataTypes.Text,
Symbol.Barcode.ReaderDataLengths.MaximumLabel);
// Create event handler delegate
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #4");
this.barcodeEventHandler = this.BarcodeReader_ReadNotify;
// Enable reader, with wait cursor
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #5");
this.barcodeReader.Actions.Enable();
this.barcodeReader.Parameters.Feedback.Success.BeepTime = 0;
this.barcodeReader.Parameters.Feedback.Success.WaveFile = "\\windows\\alarm3.wav";
// Attach to activate and deactivate events
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #6");
this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;
ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #7");
return true;
}
...I see this in the log file after opening the Find form and crashing the app (it hangs for about 20 seconds before disappearing):
Date: 3/19/2009 11:43:38 PM
Message: Reached frmFind.InitReader
Date: 3/19/2009 11:43:38 PM
Message: Reached frmFind.I
...so it's crashing almost instantaneously after this block of code:
if (this.barcodeReader != null)
{
return false;
}
...as it only gets through half of the next logging line before rudely interrupting itself.
UPDATE 2
Fejesjoco may still be right (in his comment below), but I submit this logging code as exhibit "A":
public void WriteLog(string message)
{
if (!HHSConsts.Logging) return;
StringBuilder formattedMessage = new StringBuilder();
formattedMessage.AppendLine("Date: " + DateTime.Now.ToString());
formattedMessage.AppendLine("Message: " + message);
_streamWriter.WriteLine(formattedMessage.ToString());
_streamWriter.Flush();
}
The stream should get flushed after each line is written to the log.
UPDATE 3
The failure is always very consistent as to how long it "hangs" before it crashes; I can see the cursor blinking in the bar code text box of the find form, and it pulsates about 20 times (I actually counted it last time: 24).
This also is a bit odd; in Update 2, I showed the contents of the log file with all the log entries sprinkled into the InitReader method. With those commented out (except for the first one), the log file ends with:
Date: 3/20/2009 12:01:22 AM
Message: Reached frmFind.InitReader
Date: 3/20/2009 12:01:22 AM
Message: From application-wide exception handler: Symbol.Exceptions.OperationFailureException: SCAN_GetInterfaceParams
at Symbol.Barcode.InterfaceParams.GetInterfaceParams()
at Symbol.Barcode.InterfaceParams..ctor(Reader reader)
at Symbol.Barcode.Actions.Enable()
at HHS.frmFind.InitReader()
...so the additional log file entries were preventing the exception msg from getting logged.
UPDATE 4
Actions.Enable?
I was unfamiliar with this, and when I added "Actions." I got a choice between Symbol.Barcode.Actions and Symbol.Generic.Actions.
I chose the first first, but it has no "Enable" method. Adding it scolded me with, "An object reference is required for the non-static field, method, or property 'Symbol.Generic.Actions.Enable()'"
I then commented out the using that was added, entered "Actions." again, and this time chose Symbol.Generic.Actions (and got the same err msg for my troubles).
How can I use Actions.Enable()?
UPDATE 5
C.Evenhuis: by "some events require re-attaching each time they occur" do you mean this:
private void StartRead()
{
// If we have both a reader and a reader data
if ((this.barcodeReader != null) && (this.barcodeReaderData != null))
{
// Submit a read
this.barcodeReader.ReadNotify += this.barcodeEventHandler;
this.barcodeReader.Actions.Read(this.barcodeReaderData);
}
}
private void StopRead()
{
// If we have a reader
if (this.barcodeReader != null)
{
// Flush (Cancel all pending reads)
this.barcodeReader.ReadNotify -= this.barcodeEventHandler;
this.barcodeReader.Actions.Flush();
}
}
...(barcodeReader.ReadNotify is attached in StartRead and detached in StopRead), or is more necessary?
I also have, in InitReader():
this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;
...which are implemented like so:
private void ReaderForm_Activated(object sender, EventArgs e)
{
// If there are no reads pending on barcodeReader start a new read
if (!this.barcodeReaderData.IsPending)
{
this.StartRead();
}
}
private void ReaderForm_Deactivate(object sender, EventArgs e)
{
this.StopRead();
}
InitReader() is called from textBoxScan_GotFocus(); textBoxScan has the focus when the form displays.
UPDATE 6
As to "Explicitly Close() classes before you Dispose() them", I call Dispose on two things, Symbol.Barcode.Reader and Symbol.Barcode.ReaderData, and neither one allows a Close() call.
UPDATE 7
This statement from C. Evenhuis: "you can't have two (foreground) readers enabled" led me to try the following:
private void FrmDelivery_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
this.barcodeReader.Actions.Disable();
}
...which seems to have pretty much done the trick - I can open the Find form without a hang resulting in an eventual crash. I can scan into the Find form after having scanned into the Delivery form. The only (unrelated?) problem that I still see is that my Find form is still partially obscured for some reason...
Symbol Motorola Zebra is building on a legacy C++ library with a legacy .NET wrapper. There are quite some pitfalls (some events require re-attaching each time they occur, some classes require explicit Close() before you Dispose() them, etc).
As you may have found out, you can invoke members of the Symbol classes from multiple threads / forms, but you can't have two (foreground) readers enabled, and some properties can only be set if no background readers are enabled, and there is no way to determine whether there are background readers enabled (ie DataWedge).
At our company we chose to initialize the scanner in the Form.Activated event and deinitialize it in the Deactivated event. Then when scanning was required, a call to Actions.Enable() would do the trick. Setting reader properties is done in try catch blocks :(
Quite odd that your application crashes at GetInterfaceParams, I would expect Setting them to cause trouble. Try your application with the bare minimum; don't set the BeepTime or WaveFile properties.
I'm not familiar with this type of scanner, but I can imagine initreader will do something at a hardware level. It will probably try to open some port. Because the port is already open it fails.
You need to implement a singleton service for your scanner instead of addressing it directly in your form. Then you can initialize the reader once for the complete application and use it wherever you'd like.
This will be a lot more 'SOLID' and thus more maintainable.
well, I'm writing a bot that will use certain coordinates on screen and then will simulate 15 clicks on them (every click with different coordinates). I already made it work with coordinates I entered manually on the code but now I need a way to record those coordinates. What i wanted to do is: the users press a button, then the program shows a messagebox saying "right click the main menu", the user right clicks that and those coordinates will be recorded on an array, then the program will show a second messagebox asking to right click the next button and so... My problem is that I don't know how to make the method wait for the user to right click to continue.
I tested my program by making an event that would trigger everytime I right click and show the coordinates in a messagebox, using a UserActivityHook class with contains the event OnMouseActivity:
UserActivityHook actHook;
void MainFormLoad(object sender, System.EventArgs e)
{
actHook = new UserActivityHook();
// crate an instance with global hooks
// hang on events
actHook.OnMouseActivity+=new MouseEventHandler(MouseMoved);
}
public void MouseMoved(object sender, MouseEventArgs e)
{
if (e.Clicks > 0)
{
if (e.Button.Equals(MouseButtons.Right))
{
MessageBox.Show("X:" + e.X + " Y:" + e.Y);
}
}
}
I've trying to do something like:
private void button1_Click(object sender, EventArgs e)
{
RecordMacro(cords, 1);
}
public void RecordMacro(int coordinates[][], int slotnumber){
MessageBox.show("Right click main menu");
//saves coordinates on [0][0] and [0][1]
WaitForRightClickAndSaveCords(coordinates[][]);
MessageBox.show("Right click resupply button");
//saves coordinates on [1][0] and [1][1]
WaitForRightClickAndSaveCords(coordinates[][]);
...
}
I'm still a newbie and this is my first question in StackOverflow (I usually find an answer browsing here and don't have the need to ask myself) so I'll gladly accept any critics.
This is easiest to implement using C# 5.0's asynchrony model. We'll start out by creating a method that will generate a Task that will be completed when your conditions are met. It will do this by creating a TaskCompletionSource, adding a handler to the event, and marking the task as completed in the handler. Throw in some boilerplate code to make sure the handler is removed when done, return the Task from the completion source, and we're set:
public static Task<Point> WhenRightClicked(this UserActivityHook hook)
{
var tcs = new TaskCompletionSource<Point>();
MouseEventHandler handler = null;
handler = (s, e) =>
{
if (e.Clicks > 0 && e.Button == MouseButtons.Right)
{
tcs.TrySetResult(new Point(e.X, e.Y));
hook.OnMouseActivity -= handler;
}
};
hook.OnMouseActivity += handler;
return tcs.Task;
}
Now you can write:
public async void RecordMacro(int[][] coordinates, int slotnumber)
{
MessageBox.Show("Right click main menu");
Point mainMenuPosition = await actHook.WhenRightClicked();
MessageBox.Show("Right click resupply button");
Point resupplyButtonPosition = await actHook.WhenRightClicked();
}
There are a myriad number of ways to make this work, none of which you should remotely do. The reason is, that assuming you managed to stop execution of the thread with WaitForRightClick, you would be blocking the UI thread!
By doing that, you prevent the user from being able to click on the element you want (among lots of other reasons to never block the UI thread).
You could thread it or use asynchornous methods, as Servy suggests. This blocks the method (or executes it asynchronously) without blocking the UI thread itself.
While more complex, you could also queue up a bunch of object representing a "ClickTarget". Then, you would listen on the right-click event and record the associated coordinates with the current ClickTarget, dequeue to get the next instruction, and so on.
The complete code would be too long for StackOverflow, but to give you some ideas:
public class ClickTarget
{
Point Coordinates {get; set;}
String TargetName {get; set;}
}
Queue<ClickTarget> clickTargets;
//Obviously you instantiate/populate this somewhere
private void onRightClick(...)
{
ClickTarget target = clickTargets.Dequeue();
target.Coordinates = clickLocation;
MessageBox.Show("Please click on " + clickTargets.Peek().TargetName);
}
Background:
A WinForms app, originally written in .NET 1.1 migrated via Visual
Studio to .NET 4.0. Typical menu bar at the top of main app form (NB:
not migrated to ToolStripMenuItem), as you might expect there is a
File menuitem that contains an Exit menu item.
I have implemented Ctrl-L shortcut which will bring up a modal lock
form. I also have a timer component on the main form which will
automatically bring up the lock form if there's no activity for
configurable amount of time.
When the lock form is shown you can either unlock (requiring user to
login again) or quit. If you choose to quit then my code does a
fileExitMenuItem.PerformClick() call.
Problem:
For some strange reason after the migration if I quit from the lock
form which was displayed either automatically or due to Ctrl-L
shortcut then I get a NullReferenceException thrown on the
fileExitMenuItem.PerformClick() line of code.
fileExitMenuItem is not null; with break-when-exception-thrown
switched on I can browse all of fileExitMenuItems properties.
I can break in the designer code of the form and watch the click event
handler being attached. If I use the File >> Exit menu item directly I
can break on the code in the event handler.
So this is a total WTF moment. Any suggestions on what to look at will be greatly appreciated
[UPDATE 1] As requested here is some code - this method is called whenever the user presses Ctrl-L or the lock timer elapses:
private void LockApplication()
{
try
{
// Stop the timer so we don't get any more elapsed events whilst we are waiting
// for a user to respond to the lockdown dialog. In addition stop the callout reminder
// time as once we have locked we don't want that doing it's thing.
lockdownTimer.Stop();
calloutsReminderTimer.Stop();
// Clone the current identity so we can check it later.
var previousIdentity = (CudosIdentity)BOUtilities.CurrentIdentity.Clone();
// Show lockdown form.
System.Windows.Forms.DialogResult result;
using (var lockForm = new Forms.applicationLockedForm())
result = lockForm.ShowDialog(this);
if (result == DialogResult.OK)
{
// Did we unlock with a different login?
if (!previousIdentity.Equals(BOUtilities.CurrentIdentity))
{
// Yes, so lose all changes.
CloseOpenForms();
if (_currentLoadSpec != null)
_currentLoadSpec.CancelContent();
}
RefreshLockTimerSetting(null);
}
else
fileExitMenuItem.PerformClick();
}
catch (Exception ex)
{
Helper.LogError(ex);
}
finally
{
lockdownTimer.Start();
calloutsReminderTimer.Start();
}
}
This is the code for the exit menu item:
private void fileExitMenuItem_Click(object sender, System.EventArgs e)
{
Application.Exit();
}
When the following line in LockApplication method from above is called I get the NullReferenceException:
fileExitMenuItem.PerformClick();
[UPDATE 2] Call stack info when the above line is executed:
[External Code]
Cudos.exe!Cudos.mainForm.LockApplication() Line 1132 + 0x10 bytes C#
Cudos.exe!Cudos.mainForm.fileLockCudosMenuItem_Click(object sender, System.EventArgs e) Line 1594 + 0x8 bytes C#
[External Code]
Cudos.exe!Cudos.mainForm.Main() Line 1880 + 0x1d bytes C#
[External Code]
I am not sure, but I will try to remove the restart of the timers if you call the Perform_Click.
The Tick event could be called when there is no more the application because you call Application.Exit().
private void LockApplication()
{
try
{
lockdownTimer.Stop();
calloutsReminderTimer.Stop();
.....
if (result == DialogResult.OK)
{
......
lockdownTimer.Start();
calloutsReminderTimer.Start();
}
else
fileExitMenuItem.PerformClick();
}
catch (Exception ex)
{
Helper.LogError(ex);
lockdownTimer.Start();
calloutsReminderTimer.Start();
}
// remove the finally clause
}
In the end I gave up and just hacked it by changing the fileExitMenuItem.PerformClick() to Application.Exit(). So I still don't have any idea why it was throwing the exception but it at least now works. I guess if I put more logic into the fileExitMenuItem click handler I will just have to remember to extract it into a method and update this hack to call that method to.
I have a button click event handler with a switch case in it that controls multiple buttons in one event handler.
I need to use a queue because while one button is clicked and doing some processing, second button click won't interfere with the first button click, but added to the queue. I don't want to use .enabled=false; because it'll discard the second click completely, and I'm currently editing someone's software at work so I don't want to break things that I don't know, so what are you suggesting?
The best idea, I think, is to create a producer/consumer queue.
Another question is explaining this technique.
Basically, the idea is to have a worker thread that will consume a queue to get the job to do, while other thread produce job by queuing operation in the queue.
I did succeed this with System.Collections.Queue
The code is :
private Queue<Button> Button_Queue = new Queue<Button>();
private bool isProcessing = false;
private void Button_Click((object sender, EventArgs e){
if(isProcessing){
Button_Queue.Enqueue(this);
}
else
{
isProcessing = true;
// code here
isProcessing = false;
while(Button_Queue.Count > 0){
Button_Queue.Dequeue().PerformClick();
}
}
of course mine is slightly different from this because I need to pass some variables and my click method is modified for this.
Dirty, but simple solution.
public partial class DataRefresh : Form //DataRefresh is just "some form"
{
...
...
public DateTime ClickTime; //Time when click is processed by system
public DateTime LastExecutionRunTime = DateTime.MinValue; //Time when the all the click code finish
private void buttonDataRefresh_Click(object sender, EventArgs e)
{
ClickTime = DateTime.Now;
if (ClickTime.Subtract(LastExecutionRunTime).TotalSeconds < 5 )
{
//It will keep returning - hopefully until all events in que are satisfied
return;
}
//Long running code
//Importing whole table from remote DB
...
...
//End of the Long running code
LastExecutionRunTime = DateTime.Now;
}
}