简体   繁体   中英

INotifyPropertyChanged 'Double' binding

I'm trying to bind some XAML code to a property in my ViewModel.

<Grid Visibility="{Binding HasMovies, Converter={StaticResources VisibilityConverter}}">
...
</Grid>

My ViewModel is setup like this:

private bool _hasMovies;
        public bool HasMovies
        {
            get { return _hasMovies; }
            set { _hasMovies = value; RaisePropertyChanged("HasMovies"); }
        }

In the constructor of the ViewModel, I set the HasMovies link:

MovieListViewModel()
{
   HasMovies = CP.Connection.HasMovies;
}

in CP:

public bool HasMovies
        {
            get { return MovieList != null && MovieList.Count > 0; }
        }

private ObservableCollection<Movie> _movies;
        public ObservableCollection<Movie> MovieList
        {
            get { return _movies; }
            set
            {
                _movies = value;
                RaisePropertyChanged("MovieList");
                RaisePropertyChanged("HasMovies");
                _movies.CollectionChanged += MovieListChanged;
            }
        }

        private void MovieListChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            RaisePropertyChanged("HasMovies");
        }

What am I doing wrong? How should I change this binding so that it reflects the current state of CP.Connection.HasMovies ?

Either directly expose the object in the ViewModel and bind directly through that (so that the value is not just copied once which is what happens now) or subscribe to the PropertyChanged event and set HasMovies to the new value every time it changes in your source object.

eg

CP.Connection.PropertyChanged += (s,e) =>
    {
        if (e.PropertyName = "HasMovies") this.HasMovies = CP.Connection.HasMovies;
    };

First of all, the setter for a collection type, such as your MovieList property, is not called when you change the content of the collection (ie. Add/Remove items).

This means all your setter code for the MovieList property is pointless.

Secondly, it's very silly code. A much better solution, is to use NotifyPropertyWeaver . Then your code would look like this, in the viewmodel:

[DependsOn("MovieList")]
public bool HasMovies
{
    get { return MovieList != null && MovieList.Count > 0; }
}

public ObservableCollection<Movie> MovieList
{
    get;
    private set;
}

Alternatively you would have to add a listener for the CollectionChanged event when you initialize the MovieList property the first time (no reason to have a backing property, really really no reason!), and then call RaisePropertyChanged("HasMovies") in the event handler.

Example:

public class CP : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public CP()
    {
        MovieList = new ObservableCollection<Movie>();
        MovieList.CollectionChanged += MovieListChanged;
    }

    public bool HasMovies
    {
        get { return MovieList != null && MovieList.Count > 0; }
    }

    public ObservableCollection<Movie> MovieList
    {
        get;
        private set;
    }

    private void MovieListChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        RaisePropertyChanged("HasMovies");
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM