That is a common problem but how you handle it depends upon how your button handler works. It sounds to me like your button handler is making an async call and therefore the UI regains control but you're not done with your work.
In that case the handler should first disable the button before it does any additional work that may occur async. Programmatically you can do this using the IsEnabled property. But if you're following a standard command pattern approach the better way would be to use the CanExecute method on Command
. Set a busy indicator field in whatever code is handling the command (a model generally). The CanExecute
method looks at the field to determine its state. If it is disabled then the button should be disabled as well and the user cannot interact with it. But to be sure this hasn't happened, your Execute
method can also look at the busy indicator and ignore the command if it is called again anyway.
But you mentioned that you also support cancellation on the same button. So you cannot leave the button/command disabled forever. At some point you have to switch it to "cancel" mode. Once you've determined the command can now be cancelled you will need to update the fields in the handler that the UI bounces off of so the UI updates. While a single busy field may work here I suspect you may need to track busy and cancellable if the delay between starting the work and when it can be cancelled is different.
You can see how to do some of this here.