-1
\$\begingroup\$

Let's say I have a label and a button. If I click the button, the label is set to some text, say "hello, world!", and after 5 seconds, it should disappear. Easy enough, right? I have the following implementation:

private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
  Label = "hello, world!";
  await Task.Delay(5_000);
  Label = string.Empty;
}

Now let's say I have the additional requirement that the button should remain responsive on each subsequent button click, and now the label text should remain hello, world! for the next 5 seconds i.e. the label text should remain for 5 seconds since the last time the button was clicked. The problem with the previous code is that if I had clicked the button 2.5 seconds ago and click it again now, the label text will disappear after 2.5 seconds. My idea to solve this was to maintain a list of tasks and add the delay task and await the task list before clearing the label. This is the implementation:

private static readonly List<Task> TaskList = new List<Task>();
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
  Label = "hello, world!";
  TaskList.Add(Task.Delay(5_000));      
  await Task.WhenAll(TaskList);
  if (TaskList.TrueForAll(t => t.IsCompleted))
  {
    TaskList.RemoveAll(t => t.IsCompleted);
    Label = string.Empty;
  }
}

Implementation with a cancellation token:

private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
  Label = "hello, world!";
  _cancellationTokenSource.Cancel();
  _cancellationTokenSource = new CancellationTokenSource();
  try
  {
    await Task.Delay(5_000, _cancellationTokenSource.Token);
    Label = string.Empty;
  } catch (TaskCanceledException) { }
}

As far as I know, these implementation works, but I don't know much about asynchronous programming so I don't know how to be absolutely sure it works.

Questions:
How would you implement this? Is there a better way to implement this? I originally figured that Task.WhenAll() would take care of waiting for all tasks to complete, but this does not seem to be the case. What am I getting wrong?

\$\endgroup\$
  • \$\begingroup\$ This looks very much like pseudocode. Can you post your real solution? \$\endgroup\$ – t3chb0t Oct 30 '17 at 8:09
  • \$\begingroup\$ Did you think about a timer instead of tasks? \$\endgroup\$ – JanDotNet Oct 30 '17 at 8:12
  • 1
    \$\begingroup\$ @t3chb0t It's not. This is the actual source code. \$\endgroup\$ – asfeynman Oct 30 '17 at 10:42
  • \$\begingroup\$ I have rolled back the last edit. Please see What should I do when someone answers my question? \$\endgroup\$ – t3chb0t Oct 30 '17 at 13:51
  • \$\begingroup\$ You have Label = "hello, world!"; in your actual source? Why? \$\endgroup\$ – Mast Oct 31 '17 at 7:46
1
\$\begingroup\$

I would use a timer.

On callback, you set the content of your label to empty. You just have to restart the timer on every button click.

I think it simplifies a lot the code.

\$\endgroup\$
  • \$\begingroup\$ I added an implementation using your ideas at the bottom. Can you please comment? Thanks. \$\endgroup\$ – asfeynman Oct 30 '17 at 13:49
  • \$\begingroup\$ @asfeynman: thanks for sharing your improved solution. If you want to discuss it, it is better to post it as new question (follow up). \$\endgroup\$ – JanDotNet Oct 30 '17 at 14:48
0
\$\begingroup\$

Here's an implementation suggested by @ogomrun using the timer class:

private Timer timer;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
  Label = "hello, world!";
  if (timer == null)
  {
    timer = new Timer(state => Label = string.Empty, null, 5_000, Timeout.Infinite);
  }
  else
  {
    timer.Change(5_000, Timeout.Infinite);
  }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.