tags:

views:

34

answers:

3

Hi all

I started implementing MVVM for one of my Silverlight applications. (I'm not using any toolkit).

My page contains a section with two combo boxes. Selecting an item in one of these combos triggers a search that updates a grid visible below the combos.

Each combo's selected item is bound to a property in my view model. The setter of these properties raise the INotifyPropertyChanged property change and updates the data bound to the grid automatically.

Everything was fine until I needed to add a reset button which purpose is to reset the search parameters i.e.: each combo box should not indicate any item and the grid should be empty.

  • If the reset function in the viewmodel updates the backing fields, the UI won't reflect the changes as RaisePropertyChanged will not be called.
  • If the reset function in the viewmodel updates the properties, the UI will reflect the changes but the grid will be updated twice: when reseting the first property to null and also for the second

Any help appreciated

/// <summary>Selected user.</summary>
public User SelectedUser
{
    get { return _selectedUser; }
    set
    {
        _selectedUser = value;
        RaisePropertyChanged("SelectedUser");

        UpdateProducts();
    }
}

/// <summary>Selected product category.</summary>
public ProductCategory SelectedProductCategory
{
    get { return _selectedProductCategory; }
    set
    {
        _selectedProductCategory = value;
        RaisePropertyChanged("SelectedProductCategory");

        UpdateProducts();
    }
}

// Reset option 1
public void Reset()
{
    _selectedUser = null;
    _selectedProductCategory = null;
    _products = null;
}

// Reset option 2
public void Reset()
{
    SelectedUser = null;
    SelectedProductCategory = null;
    // No need to update Products which has already been updated twice...
}
A: 

If you use the backing Fields you would have to call

RaisePropertyChanged("SelectedUser");
RaisePropertyChanged("SelectedProductCategory");

in the Reset() method.

Tokk
Thanks for your answer, that's what I'm trying to avoid: duplicating RaisePropertyChanged for the same property. I guess theoritically I should not but as you suggested I may not have the choice...
vc 74
A: 

This is something that really urks me in many frameworks, WPF included. What you need is some concept of delaying the response to change notifications so that the user never sees intermediate states. However, you can't change the way WPF responds to your notifications, so the best you can do is to delay your notifications until "after the dust has settled". In your case, you will want to change both of the backing fields before any notifications are sent. Your reset method can encode this idea as follows:

public void Reset()
{
    _selectedUser = null;
    _selectedProductCategory = null;
    _products = null;

    RaisePropertyChanged("SelectedUser");
    RaisePropertyChanged("SelectedProductCategory");
}

In my opinion, the way WPF synchronously updates the display in response to notifications of change is just plain wrong. Their DependencyProperty system gives them an opportunity to merely mark dependencies as dirty and perform recalculation at a later time.

I use the idea of marking as dirty and asynchronous recalculation as a general solution to the problem you've noted in this question and these days I could not imagine programming without it. It's a shame that more frameworks do not work this way.

Daniel Paull
Thanks for your answer, I agree with you. It's not only theoritically 'wrong', if I happen to change the name of the properties, refactoring will not be able to update the associated RaisePropertyChanged, that's OK if they're located in one single place (property setter) but to me it seems dangerous that they are available elsewhere
vc 74
Don't get me started on the property change notifications using strings... Oh so fragile and inefficient. You'd think that the guys at Microsoft defining core APIs would be a little brighter.
Daniel Paull
A: 

You can raise a single PropertyChanged event for all properties after you updated the backing fields:

RaisePropertyChanged(String.Empty);
Thomas Levesque
Thanks. That would certainly refresh all the controls even those not concerned by the changes. Isn't my whole UI going to 'blink'?
vc 74
Yes, it will refresh everything that is bound to your viewmodel. But I doubt you will be able to see it "blink"...
Thomas Levesque