private void but_Click(object sender, RoutedEventArgs e)
{
(sender as Button).IsEnabled = false;
doSomeThing();//e.g run for more than 20 seconds
(sender as Button).IsEnabled = true;
}
When I press the button at first time it disables. Then starts doSomeThing()
and it contains UI code or some UI variable updated.
I mean while if I press the button again while doSomeThing()
is in progress then but_Click
event fires again after this button enables back.
It maintains queue of event fired,i.e. n number of times which i pressed.
So, how to prevent firing event while button is disabled? Please consider in this scenario 'doSomething' contains UI controls bind to code. So we can't run background Thread in this case. Help me with solution.
It seems some clarifications are in order...
There are several possible solutions, and I think @Amit was on the right track - using threads is probably the best way to go.
Here's a simplified version of what worked for me:
private void Button1_Click(object sender, EventArgs e)
{
disableControls(); // e.g.- Button1.Enabled = false;
// run "doSomething" in a separate thread
new Thread(new ThreadStart(doSomething)).Start();
}
private void doSomething()
{
// do something... make sure it's thread-safe!!
// ...
enableControls(); // a thread safe enabling of relevant controls
}
For a good explanation why NOT to use Application.DoEvents()
, look here
For thread safe manipulation of controls' state, see: How to: Make Thread-Safe Calls to Windows Forms Controls
The issue with this code is doSomeThing()
method is running in UI Thread. So, the Button is not properly disabled. If we refactor the code so that doSomeThing()
method runs in a different thread, it will just work fine. Here is a simple example using BackgroundWorker
; however the idea is we should not run time consuming stuffs in UI thread. Here is the refactored code:
public partial class ButtonEnableTest : Window
{
private BackgroundWorker worker = new BackgroundWorker();
public ButtonEnableTest()
{
InitializeComponent();
this.worker.DoWork += new DoWorkEventHandler(worker_DoWork);
this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.btn.IsEnabled == false)
{
this.btn.IsEnabled = true;
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
doSomeThing();
}
private void doSomeThing()
{
int i = 5;
while ( i > 0)
{
Thread.Sleep(TimeSpan.FromMilliseconds(2000));
System.Diagnostics.Debug.WriteLine("Woke up " + i);
i--;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button) sender;
System.Diagnostics.Debug.WriteLine("at ButtonClick");
if (btn.IsEnabled)
{
btn.IsEnabled = false;
this.worker.RunWorkerAsync();
}
}
}
I did not follow any coding conversions here as I just wanted to share my idea. Please note that, I named the WPF button as "btn".
Another approach is to use the PeekMessage API function, as shown in the following code.
using System.Runtime.InteropServices;
using System.Security;
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr handle;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool PeekMessage(out NativeMessage message,
IntPtr handle, uint filterMin, uint filterMax, uint flags);
private const UInt32 WM_MOUSEFIRST = 0x0200;
private const UInt32 WM_MOUSELAST = 0x020D;
public const int PM_REMOVE = 0x0001;
// Flush all pending mouse events.
private void FlushMouseMessages()
{
NativeMessage msg;
// Repeat until PeekMessage returns false.
while (PeekMessage(out msg, IntPtr.Zero,
WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
;
}
This code includes a bunch of declarations for the API function and its parameters. (You also need to add using statements for the System.Runtime.InteropServices and System.Security namespaces. Download the example for the details.)
The FlushMouseMessages method calls PeekMessage telling it to discard any message in the range WM_MOUSELAST to PM_REMOVE. The code calls PeekMessage repeatedly until it returns false to indicate that there are no such messages.
The following button event handler calls FlushMouseMessage so you cannot click the button while its code is still running.
private void but_Click(object sender, RoutedEventArgs e)
{
(sender as Button).IsEnabled = false;
doSomeThing();//e.g run for more than 20 seconds
(sender as Button).IsEnabled = true;
FlushMouseMessages();
}
I picked the above code from the site http://csharphelper.com/blog/2015/08/flush-click-events-in-c/ This works for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With