简体   繁体   中英

Prevent button to be double-clicked in UWP

I have an ( async ) operation, which gets executed when a Button is clicked. Indeed, I don't want to execute that code (for example opening a ContentDialog ) when the button is double-clicked.

My current approach is to increment a variable when the method is entered and decrement it when the method is leaved.

int locker = 0;

private async void HandleDialog_Click(object sender, RoutedEventArgs e) {
    if(locker > 0) return;
    locker++;

    ContentDialog editConfirmationDialog = new ContentDialog {
        Title = "a",
        Content = "b",
        PrimaryButtonText = "c",
    };


    ContentDialogResult result = await editConfirmationDialog.ShowAsync();
    locker--;
}

Unfortunately, there are a lot of buttons which needed to be protected that way and I don't want to repeat myself all the time with this locker -code. Therefore I'm looking for a way to extract this behaviour in a subclass of Button , in a property or something reusable.

Is there any way to do this in UWP?

You do have to add code in every method that you want to do this. You can however do it a bit smarter by creating a class for this.

Something like this:

public class DoubleClickPreventer : IDisposable
{
    private readonly Button _button;

    public DoubleClickPreventer(object sender)
    {
        _button = (Button)sender;
        _button.Enabled = false;
    }

    public void Dispose()
    {
        _button.Enabled = true;
    }
}

That code allows you to add minimal code to your callbacks:

private async void HandleDialog_Click(object sender, RoutedEventArgs e) 
{
    using (var buttonLock = new DoubleClickPreventer(sender))
    {

        ContentDialog editConfirmationDialog = new ContentDialog {
            Title = "a",
            Content = "b",
            PrimaryButtonText = "c",
        };


        ContentDialogResult result = await editConfirmationDialog.ShowAsync();
    }
}

The great thing is that the button with also get enabled if something goes wrong (an exception is thrown).

If you make your own button you can't (easily) change the existing Click event handlers to do what you want, but if you're willing to make your own event for handlers that disable the button while running that's easy enough.

public class ButtonThatDisablesWhileClickHandlerIsRunning : Button //TODO consider renaming
{
    public event Func<ButtonThatDisablesWhileClickHandlerIsRunning, Task> ClickThatDisablesWhileRunning;

    protected override async void OnClick(EventArgs e)
    {
        base.OnClick(e);
        this.Enabled = false;
        foreach(Delegate handler in ClickThatDisablesWhileRunning?.GetInvocationList())
        {
            await ((Func<ButtonThatDisablesWhileClickHandlerIsRunning, Task>)handler).Invoke(this);
        }
        this.Enabled = true;
    }
}

For this approach the existing Click event won't work because the delegate doesn't return the Task to let this control know when the handlers have finished.

If you really wanted to use the Click event, then you'd need to make your own SynchronizationContext , override the Winform's current sync context, call the click event handlers, then forward all post/send requests form that custom sync context to the winforms sync context until there are no pending continuations. That's not nearly as easy, and is also not infallible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM