Fabian S.
Fabian S.

Reputation: 977

Binding visibility to task status

I'm binding the visibility of a ProgressBar to a property that returns true if a specific task is running:

View:

<ProgressBar IsIndeterminate="True" 
             Visibility="{Binding TaskRunning, Converter={StaticResource boolToVisibilityConverter}}"/>

ViewModel:

private Task someTask;
private CancellationTokenSource cancellationTokenSource;

public bool TaskRunning
{
    get
    {
        return someTask!= null && someTask.Status == TaskStatus.Running;
    }
}

private void StartTaskHandler()
{
    someTask = Task.Run(new Action(() =>
    {
         // Simulate some work
         cancellationTokenSource.Token.WaitHandle.WaitOne(10000);
    }), cancellationTokenSource.Token);
    someTask.ContinueWith((task) => RaisePropertyChanged("TaskRunning"));
    RaisePropertyChanged("TaskRunning");
}

When executing StartTaskHandler the ProgressBar will only sometimes turn visible. Why is that? How can I make sure the ProgressBar is visible when the task is running?

Upvotes: 1

Views: 712

Answers (2)

StepUp
StepUp

Reputation: 38189

I highly recommend to you to rename your property TaskRunning to IsBusy as it is good practice to name bool variables with prefix is.

You should have setter in your property TaskRunning to set true value:

private bool _isBusy;
public bool IsBusy
{
  get { return _isBusy; }
  set
    {
       _isBusy = value;
       RaisePropertyChanged("IsBusy");
    }
}

and at your code:

private void StartTaskHandler()
{
   IsBusy=true;
   someTask = Task.Run(new Action(() =>
   {
       // Simulate some work
       cancellationTokenSource.Token.WaitHandle.WaitOne(10000);
   }), cancellationTokenSource.Token);
   someTask.ContinueWith((task) => IsBusy=false;);       
}

You can raise PropertyChanged event from any threads cause WPF automatically dispatches arguments of PropertyChanged event to the UI thread.

Update:

Your code does not work as your property TaskRunning is just readable, and you cannot set true or false values. To set values to your property you should add setter and fire an event PropertyChanged when you set new value to the property TaskRunning:

private bool _taskRunning;
public bool TaskRunning
{
  get
    {
        if(someTask!= null && someTask.Status == TaskStatus.Running)
           _taskRunning=true;
       return _taskRunning;
    }
  set
    {
       _taskRunning=value;
       RaisePropertyChanged("TaskRunning");
    }
}

This row of code just fires the event with the current value(not new value):

someTask.ContinueWith((task) => RaisePropertyChanged("TaskRunning"));

but this row of code will set new value of property TaskRunning and fires event PropertyChanged to update your UI:

someTask.ContinueWith((task) => TaskRunning=true;);

Upvotes: 1

Fabian S.
Fabian S.

Reputation: 977

someTask.Status may sometimes be still TaskStatus.WaitingToRun. Adding this will solve the issue. But it's better to use async/await:

private bool isBusy = false;
public bool IsBusy
{
    get
    {
        return isBusy;
    }
    set
    {
        isBusy = value;
        RaisePropertyChanged("IsBusy");
    }
}

private async void StartTaskHandler()
{
    cancellationTokenSource = new CancellationTokenSource();
    IsBusy = true;
    someTask = Task.Run(new Action(() =>
    {
        // Simulate some work
        cancellationTokenSource.Token.WaitHandle.WaitOne(10000);
    }), cancellationTokenSource.Token);
    await someTask;
    IsBusy = false;
}

Upvotes: 0

Related Questions