Shorn Jacob
Shorn Jacob

Reputation: 1251

Multiple Binding updates from ViewModel

I have an MVVM model

View

<StatusBar>
<StatusBarItem>
<TextBlock Text="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Width="Auto"/>
</StatusBarItem>
</StatusBar>

ViewModel

ViewModel()
{
    public string Status { get; set; }

    void UpdateProcess()
    {
         UpdateStatus("Updating Database");
         Thread.Sleep(TimeSpan.FromSeconds(5));
         UpdateStatus("Database updated");
    }


    void UpdateStatus(string message)
    {
         Status = message;
         OnPropertyChanged("Status");
    }
}

When the application runs, it only shows "Updating Database" and not "Database updated" after "Updating Database" . It seems view gets updated only after the method UpdateProcess runs

I need to update the view as soon as the property changes. Is this possible?

Even when I debug through the code step by step , UI is not updating when it passes through the first OnPropertyChanged . UI gets changed to ""Database updated" once the whole UpdateProcess() finishes.

Upvotes: 0

Views: 65

Answers (2)

Dan Bryant
Dan Bryant

Reputation: 27495

I think the issue is that you're trying to display status messages during a long-running operation, but they are not showing up. Most likely this is because your long-running operation is executing on (and hence blocking) the UI thread. While the UI thread is blocked, nothing on the UI will update; in fact, if you stay blocked long enough, Windows will suggest to the user that your application has stopped responding and should be shut down.

If you're using a version of C# with async/await, you can do something like this:

    private async Task UpdateProcess()
    {
        UpdateStatus("Updating Database");
        await Task.Run(UpdateDatabaseMethod);
        UpdateStatus("Database updated");
    }

    private void UpdateDatabaseMethod()
    {
        //Do long-running stuff in background...
        //Note that this will no longer be executing in the UI thread
        //so you may need to watch out for thread safety issues that you could previously ignore
        //This might mean adding some locks around your database access methods...
    }

For versions prior to async/await support, check out BackgroundWorker.

The thing to be wary of is that you're now running your long-running task in a background thread, which will behave differently from doing it while blocking the UI. This can create a lot of new challenges and headaches, but it's unfortunately a necessary evil if you want to run the UI in parallel with long-running tasks.

Other gotchas include the fact that the user can now click on other things while the database is being updated. If you need to block UI interaction, you need some other mechanism to disable controls that could interfere while your long-running operation is going. One typical approach to this is some sort of 'busy indicator' dialog with a status message like this.

Upvotes: 1

Gawie Schneider
Gawie Schneider

Reputation: 1150

You can expand your setter of the Status property to raise the PropertyChanged once the internal value changed

private string _status;
public string Status
{
  get
  {
    return _status;
  }
  set
  {
    _status = value;
    OnPropertyChanged("Status");
  }
}

What would then happen is that the moment you set your Status property, the PropertyChanged will fire and notify the UI to update the field.

This is the only way WPF will know to update the control value from the binding other than after creation of the control.

Anothing thing that probably has an impact is the mode of the binding.

<StatusBar>
  <StatusBarItem>
    <TextBlock Text="{Binding Status, UpdateSourceTrigger=PropertyChanged}, Mode=TwoWay" Width="Auto"/>
  </StatusBarItem>
</StatusBar>

You can have a look here to see a better description of the mode

Upvotes: 1

Related Questions