Prevent a button from being clicked repeatably

Topics: Actions & Coroutines, Getting Started
Sep 11, 2013 at 2:08 AM
I have a button in my View which CM is binding by convention to a method and guard in my VM.
When the user clicks the button, I need to disable it whilst the VM is doing some processing and then re-enable it afterwards.

Is this possible - if so how? Can I use co-routines to do this?

Regards
Alan
Sep 11, 2013 at 7:12 AM
Edited Sep 11, 2013 at 7:13 AM
You can simply use action guards for this.
private bool _isBusy;

public bool CanPerformAction
{
        get { return _isBusy; }
        private set
        {
                if(_isBusy != value)
                {
                        _isBusy = value;
                        RaisePropertyChanged(() => IsBusy);
                }
        }
 }

public void PerformAction()
{
        if(!_isBusy)
        {
                try
                {
                        IsBusy = true;
                        ... //Perform the action...
                }
                finally
                {
                        IsBusy = false;
                }
        }
}
If the action performs an async job, then you need to delay resetting the IsBusy flag until the operation completes (and make it robust, so that an eventual exception does not prevent the flag reset).
Sep 12, 2013 at 10:19 PM
Bladewise,
Thanks for the reply. That's what I thought and I previously tried something very similar to what you posted. Based on your code, I've implemented the following
private bool isBusy = false;

public bool IsBusy
{
    get { return isBusy; }
    set
    {
        if (isBusy != value) {
            isBusy = value;
            NotifyOfPropertyChange(() => IsBusy);
            NotifyOfPropertyChange(() => CanRequestData);
        }
    }
}

public bool CanRequestData
{
    get
    {
         return !IsBusy && AccountViewModel.Accounts.Count > 0;
    }
}

public void RequestData()
{
    if (!IsBusy) {
        try {
            IsBusy = true;
            ... // Perform the action ...            
        }
        finally {
            IsBusy = false;
        }
    }
}
This doesn't seem to disable the button whilst the action is taking place and the clicks are simply queued up. Do I need to use RaisePropertyChanged, because I couldn't find this method on my VM which is inheriting from Screen.

Regards
Alan
Sep 13, 2013 at 7:49 AM
Note that CanRequestData changes should be triggered whenever AccountViewModel.Accounts.Count changes, otherwise the displayed state would not match the actual guard value.

If the action is performed on the UI thread, there is an high changes that repeated clicks are queued, and executed upon the action execution.
If you are using an async operation, you need to reset the IsBusy flag when the async job has completed, and not when the action RequestData returns.
public void RequestData()
{
    if (!IsBusy) {
            IsBusy = true;
            Task.Factory.StartNew(() => { //Do async stuff... }).ContinueWith(t => IsBusy = false);
    }
}