Conductor<T>.Collection.OneActive/AllActive - IChild.Parent not reset on clear
See original GitHub issuevoid Main()
{
var conductor = new Conductor<TestItem>.Collection.AllActive();
var t1 = new TestItem();
var t2 = new TestItem();
var t3 = new TestItem();
conductor.Items.Add(t1);
conductor.Items.Add(t2);
conductor.Items.Add(t3);
if (t1.Parent == null || t2.Parent == null || t3.Parent == null)
throw new Exception("t1.Parent + t2.Parent + t3.Parent should have been set by conductor");
conductor.Items[0] = null;
if (t1.Parent != null)
throw new Exception("t1.Parent should have been set to <null> by conductor");
conductor.Items.Remove(t2);
if (t2.Parent != null)
throw new Exception("t2.Parent should have been set to <null> by conductor");
conductor.Items.Clear();
if (t3.Parent != null)
throw new Exception("t3.Parent should have been set to <null> by conductor");
}
The code above fails on ‘t3.Parent != null’, it seems like the conductor implementation only resets the child’s parent property on a normal or index-based removal, but not on a clear operation.
The root cause seems to be located within the BindableCollection<T> implementation: A simple (not really pretty) workaround:
public class Workaround<T>
where T : class
{
private void ConductorItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this._mirroredItems.AddRange(e.NewItems.OfType<T>());
break;
case NotifyCollectionChangedAction.Remove:
e.OldItems.OfType<T>().Apply(i => this._mirroredItems.Remove(item: i));
break;
case NotifyCollectionChangedAction.Replace:
this._mirroredItems.AddRange(e.NewItems.OfType<T>());
e.OldItems.OfType<T>().Apply(i => this._mirroredItems.Remove(item: i));
break;
case NotifyCollectionChangedAction.Reset:
if (this._conductorItems.Count == 0) // clear called
{
this._mirroredItems.OfType<IChild>().Apply(i => i.Parent = null);
this._mirroredItems.Clear();
return;
}
// re-synchronize mirrored items
this._mirroredItems.Where(i => !this._conductorItems.Contains(item: i)).OfType<IChild>().Apply(i => i.Parent = null);
this._mirroredItems.Clear();
this._mirroredItems.AddRange(collection: this._conductorItems);
break;
}
}
public Workaround(IObservableCollection<T> conductorItems)
{
if (conductorItems == null) throw new ArgumentNullException(nameof(conductorItems));
this._conductorItems = conductorItems;
this._mirroredItems = new List<T>(collection: conductorItems);
conductorItems.CollectionChanged += this.ConductorItems_CollectionChanged;
}
}
=>
new Workaround<TestItem>(conductor.Items);
lets the initial example pass without errors.
Issue Analytics
- State:
- Created 6 years ago
- Comments:6 (4 by maintainers)
Top Results From Across the Web
Caliburn.Micro IChild.Parent is null unless activated by ...
When the Parent VM is displayed using Conductor.ActivateItem() , Caliburn.Micro does the usual labours of searching through the view model ...
Read more >Parent slicer not resetting child slicer
Solved: I have two drop down slicers that I would like to act in a parent child interaction. I have a single table...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
So this looks like it comes out of an issue around
ObservableCollection<T>
not populating theNotifyCollectionChangedEventArgs.OldItems
when the collection is cleared. Because of this we don’t hace access to the items in the collection that were removed.The solution above maintains a second separate list in order to determine which items were removed. I’m not sure yet if this is the right approach.
At the moment I’d recommend using something like
Items.Apply(i => Items.Remove(i));
to get what you need.I’ve had issues with this as some more advanced controls trigger different animations when items are removed and a mass remove like that would be performance issue.
Leaning further towards that custom event at the moment.