Reputation: 634
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.
Upvotes: 0
Views: 3417
Reputation: 127603
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;
}
Upvotes: 1
Reputation: 634
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.
Upvotes: 0