简体   繁体   中英

ObservableCollection Collection Changed event not firing

I have an observable collection in my ViewModel, bound to a datagrid. I want to implement some logic for refreshing the data in other windows based on changes to the collection/ updates to the database (using LINQ to SQL).

Here is my view model code:

    public FTViewModel(int JobID)
    {
        _windowCloseAction = new DelegateCommand(OnWindowClose);
        _oFTrn = new ObservableFilesTransmitted(_dataDc, JobID);
        _oFTrn.CollectionChanged += oFTrnCollectionChanged;
    }

    void oFTrnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (FilesTransmitted f in e.NewItems)
                f.PropertyChanged += FilesTransmitted_PropertyChanged;
        }
        if (e.OldItems != null)
        {
            foreach (FilesTransmitted f in e.OldItems)
                f.PropertyChanged -= FilesTransmitted_PropertyChanged;
        }
    }

    void FilesTransmitted_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "DocumentNumber")
        {
            _filesTransmittedChange = true;
        }
        _refreshViews = true;
    }

and the ObservableCollection constructor:

class ObservableFilesTransmitted : ViewableCollection<FilesTransmitted>
{
    public ObservableFilesTransmitted(DocControlDC dataDc, int ID)
    {
        foreach (FilesTransmitted ftran in dataDc.FilesTransmitteds.Where(x=>x.JobID==ID).OrderByDescending(x => x.TransmittalName))
        {
            this.Add(ftran);
        }
    }
}

The debugger does not stop in the oFTrnCollectionChanged. I think because the call to create the observable collection happens before I add the CollectionChanged event. But obvously I can't switch those two lines. I've looked at various StackOverflow and CodeProject topics on this, and it seems like what I have should work. Do I need to add and remove a dummy item just to get the CollectionChanged hander called? What am I missing?

It seems like perhaps I should have a constructor (for the observable collection) that does not add any members, and a function that adds the members from the database. Then I can call new, add the collectionchanged handler, and then fill the collection. I am hoping to avoid that level of rewrite though, but perhaps it's the only reasonable way.

When I run in to this the easiest way to solve it is just subscribe manually at the start.

public FTViewModel(int JobID)
{
    _windowCloseAction = new DelegateCommand(OnWindowClose);
    _oFTrn = new ObservableFilesTransmitted(_dataDc, JobID);
    foreach(var item in _oFTrn)
    {
        item.PropertyChanged += FilesTransmitted_PropertyChanged;
    }
    _oFTrn.CollectionChanged += oFTrnCollectionChanged;
}

However a even better solution is instead of using a class derived from ObserveableCollection<T> use a class derived from BindingList<T> . Any member raising their PropertyChanged event will cause the collection to raise ListChanged with the change type of ItemChanged

public FTViewModel(int JobID)
{
    _windowCloseAction = new DelegateCommand(OnWindowClose);
    _oFTrn = new ObservableFilesTransmitted(_dataDc, JobID);
    _oFTrn.CollectionChanged += oFTrnListChanged;
}

void oFTrnListChanged(object sender, ListChangedEventArgs e)
{
    if (e.ListChangedType == ListChangedType.ItemChanged)
    {
        if (e.PropertyDescriptor.Name == "DocumentNumber")
        {
            _filesTransmittedChange = true;
        }
    }
    _refreshViews = true;
}

I have simply changed the ObservableCollection constructor and added a populate function:

New view model code:

    public FTViewModel(int JobID)
    {
        _oFTrn = new ObservableFilesTransmitted(_dataDc, JobID);
        _oFTrn.CollectionChanged += oFTrnCollectionChanged;
        _oFTrn.FillCollection();

    }

new ObservableCollection class:

class ObservableFilesTransmitted : ViewableCollection<FilesTransmitted>
{
    DocControlDC _dc = null;
    int _jobID = 0;

    public ObservableFilesTransmitted(DocControlDC dataDc, int ID)
    {
        _dc = dataDc;
        _jobID = ID;
    }

    public void FillCollection()
    {
        foreach (FilesTransmitted ftran in _dc.FilesTransmitteds.Where(x=>x.JobID==_jobID).OrderByDescending(x => x.TransmittalName))
        {
            this.Add(ftran);
        }
    }
}

And it all works as expected. But it gets called for each item added. I may play with the idea that I simply loop through the collection and add the propertychanged handler for each item in the viewmodel constructor. Seems like less of a performance hit that way.

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