private BoundList( StorageObjectID storageobjectID, EditorFile file, FactorySet <T> factoryset, ViewerSet <T> viewerset, BoundList <T> master ) : base( storageobjectID, file, null //TODO ) { this.factoryset = factoryset; this.viewerset = viewerset; this.master = master; hub_obj = File.Storage[StorageObjectID]; var propertybinders = new Dictionary <string, PropertyBinder <string> >(); listener_add = hub_obj.CreateListen( IOEvent.ChildAdded, (key, objID) => { int i = int.Parse(key); if (Objects.Count > i && Objects.HasItemAt(i) && Objects[i].StorageObjectID == objID) { return; } var obj = master == null ? FactorySet.Load(objID, File) : master[objID]; var namedobj = obj as INamedObject; if (namedobj != null) { if (master == null) { var name_obj = File .Storage [objID] .GetOrMake("name"); var name_val = name_obj.ReadAllString(); if (map_name.ContainsKey(name_val)) { if (master == null) { if (AutomaticallyAvoidNameCollisionsWithUnderlines) { name_val += "_"; } else { throw new ArgumentException($"Name \"{name_val}\" already in use."); } } } name_obj.WriteAllString(name_val); var binder = namedobj.Name.Bind(name_obj); namedobj.Name.AfterChange += propertybinders.Rename; propertybinders.Add(binder.Property.Value, binder); } } if (Objects.Contains(obj)) { throw new InvalidOperationException(); } Objects.Insert(i, obj); if (master == null) { if (isallowedtobindobjects) { obj.Bind(); } } } ); listener_remove = hub_obj.CreateListen( IOEvent.ChildRemoved, (key, objID) => { var i = int.Parse(key); var obj = Objects.FirstOrDefault(_ => _.StorageObjectID == objID); if (obj != null) { var namedobj = obj as INamedObject; if (namedobj != null) { if (master == null) { namedobj.Name.AfterChange -= propertybinders.Rename; propertybinders[namedobj.Name.Value].Dispose(); propertybinders.Remove(namedobj.Name.Value); } } if (master == null) { obj.Unbind(); } Objects.Remove(obj); } } ); listener_move = hub_obj .Graph .CreateListen( msg => { var old_i = int.Parse(msg.Relation); var new_i = int.Parse(msg.NewRelation); Objects.Move(old_i, new_i); }, hub_obj.ID, IOEvent.ChildRekeyed ); Objects.ItemInserted += (obj, i) => { if (!hub_obj.HasChild(obj.StorageObjectID)) { if (master == null) { throw new InvalidOperationException(); } hub_obj.Add(i.ToString(), obj.StorageObjectID); } var namedobj = obj as INamedObject; if (namedobj != null) { namedobj.Name.BeforeChange += Object_Renaming; namedobj.Name.AfterChange += Object_Renamed; map_name.Add(namedobj.Name.Value, obj); map_name_inverse.Add(obj, namedobj.Name.Value); } map_storageobjectID.Add(obj.StorageObjectID, obj); map_storageobjectID_inverse.Add(obj, obj.StorageObjectID); }; Objects.ItemWithdrawn += (obj, i) => { if (hub_obj.HasChild(obj.StorageObjectID)) { hub_obj.Remove(obj.StorageObjectID); } var namedobj = obj as INamedObject; if (namedobj != null) { namedobj.Name.BeforeChange -= Object_Renaming; namedobj.Name.AfterChange -= Object_Renamed; namedobj.Name.AfterChange -= propertybinders.Rename; map_name.Remove(namedobj.Name.Value); map_name_inverse.Remove(obj); } map_storageobjectID.Remove(obj.StorageObjectID); map_storageobjectID_inverse.Remove(obj); }; Objects.ItemMoved += (item, oldindex, newindex) => { var sign = Math.Sign(newindex - oldindex); for (int i = oldindex; i != newindex; i += sign) { if (isallowedtobindobjects) { throw new NotImplementedException(); } else { // The bound list is still loading items from storage. // The 'moving' is really just initialization to sync with // the back-end store, if the code ran this else clause. } } }; }