In my program I use a background worker thread to open files. The main structure of my program is a data bound TreeView
. During the file read in process, dynamic TreeView
nodes are added to the TreeView
as they are read in from the file. These TreeView
nodes that I mention are bound to containers called UICollections
(a custom class inherited from ObservableCollection<T>
). I've created the UICollection<T>
class in order to make sure that CollectionViews
of this type never have their SourceCollections
changed from the background worker thread. I do this by changing a property in a UICollection
called IsNotifying
to false
.
My UICollection<T>
class:
public class UICollection<T> : ObservableCollection<T>
{
public UICollection()
{
IsNotifying = true;
}
public UICollection(IEnumerable<T> source)
{
this.Load(source);
}
public bool IsNotifying { get; set; }
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (IsNotifying)
base.OnPropertyChanged(e);
}
//Does not raise unless IsNotifying = true
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (IsNotifying)
base.OnCollectionChanged(e);
}
//Used whenever I re-order a collection
public virtual void Load(IEnumerable<T> items)
{
if (items == null)
throw new ArgumentNullException("items");
this.IsNotifying = false;
foreach (var item in items)
this.Add(item);
//ERROR created on this line because IsNotifying is always set to true
this.IsNotifying = true;
this.Refresh();
}
public Action<T> OnSelectedItemChanged { get; set; }
public Func<T, bool> GetDefaultItem { get; set; }
public void Refresh()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
With that being said, I am having problems implementing this UICollection<T>
with my control structure, which involves adding UICollections
from a background worker thread.
For clarity, my program moves as follows:
Is a file being opened?: YES -> go into Background worker thread
In background worker thread:
Do we need to create new UICollections?: YES -> go to method in UIThread that does so
(iterate as needed)Close thread.
The main concept that needs to be understood is that UICollection.IsNotifying
has to be set to false
if the background worker thread is open. I have no problem doing this for collections that are already known about, but for the dynamic ones I run into problems.
Sample of what my Background worker thread does:
private void openFile()
{
//Turn off CollectionChanged for known Collections
KnownCollections1.IsNotifying = false;
KnownCollections2.IsNotifying = false; //... and so on
//Do we need to create new collections? YES -> Call to AddCollection in UIThread
//Refresh known collections
App.Current.Dispatcher.BeginInvoke((Action)(() => KnownCollections1.Refresh()));
App.Current.Dispatcher.BeginInvoke((Action)(() => KnownCollections2.Refresh())); //... and so on
//If new collections exist find them and refresh them...
}
Method in UIThread that adds collections to TreeView
:
public void AddCollection(string DisplayName, int locationValue)
{
node.Children.Add(CreateLocationNode(displayName, locationValue)); //Add to parent node
for (int i = 0; i < node.Children.Count(); i++)
{
//make sure IsNotifying = false for newly added collection
if (node.Children[i].locationValue == locationValue)
node.Children[i].Children.IsNotifying = false;
}
//Order the collection based on numerical value
var ordered = node.Children.OrderBy(n => n.TreeView_LocValue).ToList();
node.Children.Clear();
node.Children.Load(ordered); //Pass to UICollection class -- *RUNS INTO ERROR*
}
With all of that, one of two things will happen... if I comment out the line this.IsNotifying = true;
, an exception will blow in OnCollectionChanged
because it gets raised while the backround thread is open. If I leave the line as is, the collection will never reflect in the view because OnCollectionChanged
never gets raised, notifying the view. What do I need to do to allow the creation of these collections without running into these errors? I'm guessing the problem is either in my AddCollection()
function, or in the UICollection<T>
class.