Maui app user tap a button more than once problem

Haviv Elbsz 2,091 Reputation points
2022-11-22T15:01:48.657+00:00

Hi all

In my app I have a button called "Start"
when user tap it the app run

and the button text changed to be
called "Cancel"

if the user tap again the button the app
stop.

I n some situation when the app has delay
the user may tap more than once
and the app can hang.

how I disable those redundant taps
programmatically.

mainly remove all tapps except one.

Thank you very much

Developer technologies | .NET | .NET MAUI
Developer technologies | C#
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Michael Taylor 60,326 Reputation points
    2022-11-22T15:31:44.983+00:00

    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.


  2. Haviv Elbsz 2,091 Reputation points
    2022-11-22T21:14:00.803+00:00
    Notes: This async/await pattern was taken from Arlen Feldman's website  
      
    Handling long operations with cancel and progress in C# with async  
    https://cowthulu.com/handling-long-operations-with-cancel-and-progress-in-c-with-async/  
      
    I addopt it to fit my need as shown below  
      
    The #if  #endif directives is for skiping async debuging  
      
      
        private async void RunButton_Clicked(object sender, EventArgs e)  
        {  
      
          progressBar.Progress = 0.0;  
      
          if(!IsAppRunning)  
          {  
            IsAppRunning = true;  
            RunButton.Text = "Cancel";  
            RunButton.TextColor = Colors.White;  
            RunButton.Background = Colors.Red;  
      
            m_cancelTokenSource = new CancellationTokenSource();  
      
            try  
            {  
      
    #if (!DEBUG)  
      
              if (actionOperator == "mul")  
                if (CheckIsThisASpatialMul())  
                  await alfaStarsSpatialMul(m_cancelTokenSource.Token);  
                else  
                  await alfaStarsMul(m_cancelTokenSource.Token);  
      
              else if (actionOperator == "div0")  
                await alfaStarsSpatialDiv(m_cancelTokenSource.Token);  
              else if (actionOperator == "div1")  
                await alfaStarsDiv1(m_cancelTokenSource.Token);  
              else if (actionOperator == "div2")  
                await alfaStarsDiv2(m_cancelTokenSource.Token);  
              else if (actionOperator == "div3")  
                await alfaStarsDiv3(m_cancelTokenSource.Token);  
              else if (actionOperator == "div4")  
                await alfaStarsDiv4(m_cancelTokenSource.Token);  
      
              else if (actionOperator == "addsub")  
                await alfaStarsAdSb(m_cancelTokenSource.Token);  
      
              else if (actionOperator == "add")  
                await alfaStarsAdd(m_cancelTokenSource.Token);  
      
              else if (actionOperator == "sub")  
                await alfaStarsSub(m_cancelTokenSource.Token);  
      
              else if (actionOperator == "sqr")  
                await alfaStarsSqr(m_cancelTokenSource.Token);  
      
              else  
              {  
                //thisMsg = "Wrong Input : \r\n";  
              }   
    #endif  
      
    #if (DEBUG)  
      
              if (actionOperator == "mul")  
                if (CheckIsThisASpatialMul())  
                  alfaStarsSpatialMul();  
                else  
                  alfaStarsMul();  
      
              else if (actionOperator == "div0")  
                alfaStarsSpatialDiv();  
              else if (actionOperator == "div1")  
                alfaStarsDiv1();  
              else if (actionOperator == "div2")  
                alfaStarsDiv2();  
              else if (actionOperator == "div3")  
                alfaStarsDiv3();  
              else if (actionOperator == "div4")  
                alfaStarsDiv4();  
      
              else if (actionOperator == "addsub")  
                alfaStarsAdSb();  
      
              else if (actionOperator == "add")  
                alfaStarsAdd();  
      
              else if (actionOperator == "sub")  
                alfaStarsSub();  
      
              else if (actionOperator == "sqr")  
                alfaStarsSqr();  
      
              else  
              {  
                //thisMsg = "Wrong Input : \r\n";  
              }  
       
    #endif  
      
            }  
            catch (OperationCanceledException)  
            {  
              //  
            }  
            finally  
            {  
      
              RunButton.Text = "Start";  
              RunButton.TextColor = Colors.White;  
              RunButton.Background = Microsoft.Maui.Graphics.Color.FromArgb("512BD4");  
              IsAppRunning = false;  
              m_cancelTokenSource = null;  
              progressBar.Progress = 0.0;  
               
            }  
          }  
          else  
          {  
            m_cancelTokenSource.Cancel();  
          }  
      
          return;  
      
        }  
      
      
    //-----------------------------------------------------------------------------  
      
          
    #if (DEBUG)  
        public void alfaStarsSpatialMul()  
        {  
    #endif  
    #if (!DEBUG)  
        public Task alfaStarsSpatialMul(CancellationToken ct)  
        {  
          return Task.Run(() =>  
          {  
    #endif  
            try  
            {  
              int cnt;  
              int starsQty = 0;  
              int i;  
              ulong loopsCnt = 0;  
      
                
              for(combs = 0; combs < combsCnt; combs++)  
              {  
      
    #if (!DEBUG)  
      
                progValue = (double)combs / (double)combsCnt;            
                MainThread.BeginInvokeOnMainThread(() => { progressBar.Progress = progValue; });  
      
    #endif  
                iPN = 1;  
                while (iPN < PN)  
                {  
      
      
                    iPY = 0;  
                    while(iPY < PY)//*** while(iPY < PY) Algorithm section ******************  
                    {  
      
    #if (!DEBUG)  
                      if (ct.IsCancellationRequested)  
                      {     
                        stopwatch.Stop();  
                        strOutputTBText += $"\r\nCanceled at Loop {loopsCnt}";  
                        strOutputTBText += "\r\n";  
                        strOutputTBText += "Time elapsed:\r\n" + stopwatch.Elapsed;  
                        strOutputTBText += "\r\n";  
      
                        MainThread.BeginInvokeOnMainThread(() => {OutputTB.Text += strOutputTBText;});  
      
                        if (fromToCKB.IsChecked == false) IsFromToSetToZero = false;  
                        if(alphaCKB.IsChecked == false) IsAlphaMode = false;  
                     
                        throw new OperationCanceledException(ct);  
                      }  
    #endif              
      
      
      
                    }// while(iPY < PY)  
      
      
                }// while (iPN < PN)  
      
        
      
              }//for(combs  
      
      
              strOutputTBText += $"\r\n {loopsCnt} Loops";  
              strOutputTBText += "\r\n";  
      
              stopwatch.Stop();  
              strOutputTBText += "Time elapsed:\r\n" + stopwatch.Elapsed;  
              strOutputTBText += "\r\n";  
              MainThread.BeginInvokeOnMainThread(() => {OutputTB.Text += strOutputTBText;});  
              NoSolution();  
            }  
            catch (Exception ex)  
            {  
              if(ex.ToString().Contains("OperationCanceledException"))  
              {  
                //  
              }  
              else  
              {  
                strOutputTBText += "\r\n\r\n";  
                strOutputTBText += $"Exception = " + ex + "\r\n";  
                strOutputTBText += $"Invalid Puzzle";  
                strOutputTBText += "\r\n\r\n";  
                MainThread.BeginInvokeOnMainThread(() => {OutputTB.Text += strOutputTBText;});  
              }  
            }  
            finally  
            {  
             if (fromToCKB.IsChecked == false) IsFromToSetToZero = false;  
             if(alphaCKB.IsChecked == false) IsAlphaMode = false;  
              progressBar.Progress = 0.0;  
            }  
      
    #if (!DEBUG)  
          }, ct);  
    #endif  
        }//alfaStarsSpatialMul()  
      
      
      
      
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.