在 AvaloniaUI 中使用动态数据作为 WPF 的 CompositeCollection 替代品
Use Dynamic Data as WPF's CompositeCollection alternative in AvaloniaUI
我想在 AvaloniaUI 项目中使用动态数据作为 WPF 的 CompositeCollection 替代方案。
下面是一些暴露问题的代码:
public class MainWindowViewModel : ViewModelBase
{
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBind;
public ReadOnlyObservableCollection<ViewModelBase> TestBind => _testBind;
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes { get; set; }
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes2 { get; set; }
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes3 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject1 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject2 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject3 { get; set; }
public IObservable<IChangeSet<ViewModelBase>> InBoth { get; set; }
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBindTypes;
public ReadOnlyObservableCollection<ViewModelBase> TestBindTypes => _testBindTypes;
public MainWindowViewModel()
{
// TODO : those object collections should be of the real type and not from ancestor
// ListTypesObject1 = new ObservableCollection<Object1>()
ListTypesObject1 = new ObservableCollection<ViewModelBase>()
{
new Object1(),
};
ListTypesObject2 = new ObservableCollection<ViewModelBase>()
{
new Object2(),
};
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3(),
};
// Change observableCollection to IObservable to be running with engine ReactiveUI
SeveralListTypes = ListTypesObject1.ToObservableChangeSet();
SeveralListTypes2 = ListTypesObject2.ToObservableChangeSet();
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
//Group All Observable into one with Or operator
InBoth = SeveralListTypes.Or(SeveralListTypes2).Or(SeveralListTypes3);
// Bind => output to Binded Property for xaml
// Subscribe => to be notified when changes
var t = InBoth.Bind(out _testBindTypes)
.DisposeMany()
.Subscribe();
}
public void AddObject1()
{
var obj1 = new Object1("Added Object 1");
ListTypesObject1.Add(obj1);
}
public void AddObject2()
{
var obj2 = new Object2("Added Object 2");
ListTypesObject2.Add(obj2);
}
public void AddObject3()
{
if (ListTypesObject3 == null)
return;
var obj3 = new Object3("Added Object 3");
ListTypesObject3.Add(obj3);
}
public void DeleteObject1()
{
if(ListTypesObject1 != null && ListTypesObject1.Count > 0)
ListTypesObject1.RemoveAt(0);
}
public void DeleteObject2()
{
if (ListTypesObject2 != null && ListTypesObject2.Count > 0)
ListTypesObject2.RemoveAt(0);
}
public void DeleteObject3()
{
if (ListTypesObject3 != null && ListTypesObject3.Count > 0)
ListTypesObject3.RemoveAt(0);
}
public void DeleteObjectClear()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3("Added object 3 from new list 3"),
};
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
InBoth = InBoth.Or(SeveralListTypes3);
// TODO : the collection we want to remove is still binded, the new one is not
}
public void DeleteObject3List()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
// TODO : remove the Object3List from DynamicData
}
public void CreateObject3List()
{
if (ListTypesObject3 != null)
return;
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3("Added object 3 from new list 3"),
};
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
InBoth = InBoth.Or(SeveralListTypes3);
// TODO : the collection we want to remove is still binded, the new one is not
}
}
Object1、Object2 和 Object3 是 ViewModelBase 的继承。
DeleteObjectClear 方法从绑定中删除所有 Object3,但随后不显示新列表。
如何添加或删除 ObservableCollection 并刷新绑定对象 (TestBind)?
作为第二个问题,是否可以在 ObservableCollection 中使用真实类型的对象(具有共同的祖先)而不是 ViewModelBase 并且仍然使用动态数据来聚合所有集合?
这是突出问题的完整 github 存储库 POC:https://github.com/Whiletru3/pocbindingdmo
谢谢
我真的不能只用 DynamicData 来做,所以我想出了这个解决方案。
不是很优雅,但它有效...
我创建了一个 ObservableCollectionAggregate class。我可以分配和取消分配不同的(类型化的)ObservableCollections。
public class ObservableCollectionAggregate : ObservableCollection<ViewModelBase>
{
private ObservableCollection<Object1> _subCollection1;
private ObservableCollection<Object2> _subCollection2;
private ObservableCollection<Object3> _subCollection3;
public ObservableCollectionAggregate()
{
_subCollection1 = null;
_subCollection2 = null;
_subCollection3 = null;
}
public void UnassignCollectionObject1()
{
if (_subCollection1 != null)
{
RemoveItems(_subCollection1);
_subCollection1.CollectionChanged -= OnSubCollectionChanged;
_subCollection1 = null;
}
}
public void AssignCollectionObject1(ObservableCollection<Object1> collection)
{
if (_subCollection1 != null)
{
UnassignCollectionObject1();
}
_subCollection1 = collection;
AddItems(_subCollection1);
_subCollection1.CollectionChanged += OnSubCollectionChanged;
}
public void UnassignCollectionObject2()
{
if (_subCollection2 != null)
{
RemoveItems(_subCollection2);
_subCollection2.CollectionChanged -= OnSubCollectionChanged;
_subCollection2 = null;
}
}
public void AssignCollectionObject2(ObservableCollection<Object2> collection)
{
if (_subCollection2 != null)
{
UnassignCollectionObject2();
}
_subCollection2 = collection;
AddItems(_subCollection2);
_subCollection2.CollectionChanged += OnSubCollectionChanged;
}
public void UnassignCollectionObject3()
{
if (_subCollection3 != null)
{
RemoveItems(_subCollection3);
_subCollection3.CollectionChanged -= OnSubCollectionChanged;
_subCollection3 = null;
}
}
public void AssignCollectionObject3(ObservableCollection<Object3> collection)
{
if (_subCollection3 != null)
{
UnassignCollectionObject3();
}
_subCollection3 = collection;
AddItems(_subCollection3);
_subCollection3.CollectionChanged += OnSubCollectionChanged;
}
private void AddItems(IEnumerable<ViewModelBase> items)
{
foreach (ViewModelBase me in items)
Add(me);
}
private void RemoveItems(IEnumerable<ViewModelBase> items)
{
foreach (ViewModelBase me in items)
Remove(me);
}
private void OnSubCollectionChanged(object source, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
AddItems(args.NewItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveItems(args.OldItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Replace:
RemoveItems(args.OldItems.Cast<ViewModelBase>());
AddItems(args.NewItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Move:
throw new NotImplementedException();
case NotifyCollectionChangedAction.Reset:
if (source is ObservableCollection<Object1>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
if (source is ObservableCollection<Object2>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
if (source is ObservableCollection<Object3>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
break;
}
}
}
然后我可以在我的视图模型中使用它,使用 DynamicData 进行绑定:
public class MainWindowViewModel : ViewModelBase
{
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBind;
public ReadOnlyObservableCollection<ViewModelBase> TestBind => _testBind;
public ObservableCollectionAggregate AggregatedCollection { get; set; }
public IObservable<IChangeSet<ViewModelBase>> AggregatedChangeSetFull { get; set; }
public ObservableCollection<Object1> ListTypesObject1 { get; set; }
public ObservableCollection<Object2> ListTypesObject2 { get; set; }
public ObservableCollection<Object3> ListTypesObject3 { get; set; }
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBindTypes;
public ReadOnlyObservableCollection<ViewModelBase> TestBindTypes => _testBindTypes;
public MainWindowViewModel()
{
ListTypesObject1 = new ObservableCollection<Object1>()
{
new Object1(),
};
ListTypesObject2 = new ObservableCollection<Object2>()
{
new Object2(),
};
AggregatedCollection = new ObservableCollectionAggregate();
AggregatedCollection.AssignCollectionObject1(ListTypesObject1);
AggregatedCollection.AssignCollectionObject2(ListTypesObject2);
AggregatedChangeSetFull = AggregatedCollection.ToObservableChangeSet();
// Bind => output to Binded Property for xaml
// Subscribe => to be notified when changes
var t = AggregatedChangeSetFull
.DisposeMany()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _testBindTypes)
.Subscribe();
}
public void AddObject1()
{
if (ListTypesObject1 == null)
return;
var obj1 = new Object1("Added Object 1");
ListTypesObject1.Add(obj1);
}
public void AddObject2()
{
if (ListTypesObject2 == null)
return;
var obj2 = new Object2("Added Object 2");
ListTypesObject2.Add(obj2);
}
public void AddObject3()
{
if (ListTypesObject3 == null)
return;
var obj3 = new Object3("Added Object 3");
ListTypesObject3.Add(obj3);
}
public void DeleteObject1()
{
if(ListTypesObject1 != null && ListTypesObject1.Count > 0)
ListTypesObject1.RemoveAt(0);
}
public void DeleteObject2()
{
if (ListTypesObject2 != null && ListTypesObject2.Count > 0)
ListTypesObject2.RemoveAt(0);
}
public void DeleteObject3()
{
if (ListTypesObject3 != null && ListTypesObject3.Count > 0)
ListTypesObject3.RemoveAt(0);
}
public void DeleteObject3List()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
AggregatedCollection.UnassignCollectionObject3();
}
public void CreateObject3List()
{
if (ListTypesObject3 != null)
return;
ListTypesObject3 = new ObservableCollection<Object3>()
{
new Object3("Added object 3 from new list 3"),
};
AggregatedCollection.AssignCollectionObject3(ListTypesObject3);
}
}
存储库此分支中的完整工作示例:
https://github.com/Whiletru3/pocbindingdmo/tree/ObservableCollectionAggregate
我想在 AvaloniaUI 项目中使用动态数据作为 WPF 的 CompositeCollection 替代方案。
下面是一些暴露问题的代码:
public class MainWindowViewModel : ViewModelBase
{
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBind;
public ReadOnlyObservableCollection<ViewModelBase> TestBind => _testBind;
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes { get; set; }
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes2 { get; set; }
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes3 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject1 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject2 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject3 { get; set; }
public IObservable<IChangeSet<ViewModelBase>> InBoth { get; set; }
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBindTypes;
public ReadOnlyObservableCollection<ViewModelBase> TestBindTypes => _testBindTypes;
public MainWindowViewModel()
{
// TODO : those object collections should be of the real type and not from ancestor
// ListTypesObject1 = new ObservableCollection<Object1>()
ListTypesObject1 = new ObservableCollection<ViewModelBase>()
{
new Object1(),
};
ListTypesObject2 = new ObservableCollection<ViewModelBase>()
{
new Object2(),
};
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3(),
};
// Change observableCollection to IObservable to be running with engine ReactiveUI
SeveralListTypes = ListTypesObject1.ToObservableChangeSet();
SeveralListTypes2 = ListTypesObject2.ToObservableChangeSet();
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
//Group All Observable into one with Or operator
InBoth = SeveralListTypes.Or(SeveralListTypes2).Or(SeveralListTypes3);
// Bind => output to Binded Property for xaml
// Subscribe => to be notified when changes
var t = InBoth.Bind(out _testBindTypes)
.DisposeMany()
.Subscribe();
}
public void AddObject1()
{
var obj1 = new Object1("Added Object 1");
ListTypesObject1.Add(obj1);
}
public void AddObject2()
{
var obj2 = new Object2("Added Object 2");
ListTypesObject2.Add(obj2);
}
public void AddObject3()
{
if (ListTypesObject3 == null)
return;
var obj3 = new Object3("Added Object 3");
ListTypesObject3.Add(obj3);
}
public void DeleteObject1()
{
if(ListTypesObject1 != null && ListTypesObject1.Count > 0)
ListTypesObject1.RemoveAt(0);
}
public void DeleteObject2()
{
if (ListTypesObject2 != null && ListTypesObject2.Count > 0)
ListTypesObject2.RemoveAt(0);
}
public void DeleteObject3()
{
if (ListTypesObject3 != null && ListTypesObject3.Count > 0)
ListTypesObject3.RemoveAt(0);
}
public void DeleteObjectClear()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3("Added object 3 from new list 3"),
};
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
InBoth = InBoth.Or(SeveralListTypes3);
// TODO : the collection we want to remove is still binded, the new one is not
}
public void DeleteObject3List()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
// TODO : remove the Object3List from DynamicData
}
public void CreateObject3List()
{
if (ListTypesObject3 != null)
return;
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3("Added object 3 from new list 3"),
};
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
InBoth = InBoth.Or(SeveralListTypes3);
// TODO : the collection we want to remove is still binded, the new one is not
}
}
Object1、Object2 和 Object3 是 ViewModelBase 的继承。
DeleteObjectClear 方法从绑定中删除所有 Object3,但随后不显示新列表。
如何添加或删除 ObservableCollection 并刷新绑定对象 (TestBind)?
作为第二个问题,是否可以在 ObservableCollection 中使用真实类型的对象(具有共同的祖先)而不是 ViewModelBase 并且仍然使用动态数据来聚合所有集合?
这是突出问题的完整 github 存储库 POC:https://github.com/Whiletru3/pocbindingdmo
谢谢
我真的不能只用 DynamicData 来做,所以我想出了这个解决方案。 不是很优雅,但它有效...
我创建了一个 ObservableCollectionAggregate class。我可以分配和取消分配不同的(类型化的)ObservableCollections。
public class ObservableCollectionAggregate : ObservableCollection<ViewModelBase>
{
private ObservableCollection<Object1> _subCollection1;
private ObservableCollection<Object2> _subCollection2;
private ObservableCollection<Object3> _subCollection3;
public ObservableCollectionAggregate()
{
_subCollection1 = null;
_subCollection2 = null;
_subCollection3 = null;
}
public void UnassignCollectionObject1()
{
if (_subCollection1 != null)
{
RemoveItems(_subCollection1);
_subCollection1.CollectionChanged -= OnSubCollectionChanged;
_subCollection1 = null;
}
}
public void AssignCollectionObject1(ObservableCollection<Object1> collection)
{
if (_subCollection1 != null)
{
UnassignCollectionObject1();
}
_subCollection1 = collection;
AddItems(_subCollection1);
_subCollection1.CollectionChanged += OnSubCollectionChanged;
}
public void UnassignCollectionObject2()
{
if (_subCollection2 != null)
{
RemoveItems(_subCollection2);
_subCollection2.CollectionChanged -= OnSubCollectionChanged;
_subCollection2 = null;
}
}
public void AssignCollectionObject2(ObservableCollection<Object2> collection)
{
if (_subCollection2 != null)
{
UnassignCollectionObject2();
}
_subCollection2 = collection;
AddItems(_subCollection2);
_subCollection2.CollectionChanged += OnSubCollectionChanged;
}
public void UnassignCollectionObject3()
{
if (_subCollection3 != null)
{
RemoveItems(_subCollection3);
_subCollection3.CollectionChanged -= OnSubCollectionChanged;
_subCollection3 = null;
}
}
public void AssignCollectionObject3(ObservableCollection<Object3> collection)
{
if (_subCollection3 != null)
{
UnassignCollectionObject3();
}
_subCollection3 = collection;
AddItems(_subCollection3);
_subCollection3.CollectionChanged += OnSubCollectionChanged;
}
private void AddItems(IEnumerable<ViewModelBase> items)
{
foreach (ViewModelBase me in items)
Add(me);
}
private void RemoveItems(IEnumerable<ViewModelBase> items)
{
foreach (ViewModelBase me in items)
Remove(me);
}
private void OnSubCollectionChanged(object source, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
AddItems(args.NewItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveItems(args.OldItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Replace:
RemoveItems(args.OldItems.Cast<ViewModelBase>());
AddItems(args.NewItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Move:
throw new NotImplementedException();
case NotifyCollectionChangedAction.Reset:
if (source is ObservableCollection<Object1>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
if (source is ObservableCollection<Object2>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
if (source is ObservableCollection<Object3>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
break;
}
}
}
然后我可以在我的视图模型中使用它,使用 DynamicData 进行绑定:
public class MainWindowViewModel : ViewModelBase
{
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBind;
public ReadOnlyObservableCollection<ViewModelBase> TestBind => _testBind;
public ObservableCollectionAggregate AggregatedCollection { get; set; }
public IObservable<IChangeSet<ViewModelBase>> AggregatedChangeSetFull { get; set; }
public ObservableCollection<Object1> ListTypesObject1 { get; set; }
public ObservableCollection<Object2> ListTypesObject2 { get; set; }
public ObservableCollection<Object3> ListTypesObject3 { get; set; }
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBindTypes;
public ReadOnlyObservableCollection<ViewModelBase> TestBindTypes => _testBindTypes;
public MainWindowViewModel()
{
ListTypesObject1 = new ObservableCollection<Object1>()
{
new Object1(),
};
ListTypesObject2 = new ObservableCollection<Object2>()
{
new Object2(),
};
AggregatedCollection = new ObservableCollectionAggregate();
AggregatedCollection.AssignCollectionObject1(ListTypesObject1);
AggregatedCollection.AssignCollectionObject2(ListTypesObject2);
AggregatedChangeSetFull = AggregatedCollection.ToObservableChangeSet();
// Bind => output to Binded Property for xaml
// Subscribe => to be notified when changes
var t = AggregatedChangeSetFull
.DisposeMany()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _testBindTypes)
.Subscribe();
}
public void AddObject1()
{
if (ListTypesObject1 == null)
return;
var obj1 = new Object1("Added Object 1");
ListTypesObject1.Add(obj1);
}
public void AddObject2()
{
if (ListTypesObject2 == null)
return;
var obj2 = new Object2("Added Object 2");
ListTypesObject2.Add(obj2);
}
public void AddObject3()
{
if (ListTypesObject3 == null)
return;
var obj3 = new Object3("Added Object 3");
ListTypesObject3.Add(obj3);
}
public void DeleteObject1()
{
if(ListTypesObject1 != null && ListTypesObject1.Count > 0)
ListTypesObject1.RemoveAt(0);
}
public void DeleteObject2()
{
if (ListTypesObject2 != null && ListTypesObject2.Count > 0)
ListTypesObject2.RemoveAt(0);
}
public void DeleteObject3()
{
if (ListTypesObject3 != null && ListTypesObject3.Count > 0)
ListTypesObject3.RemoveAt(0);
}
public void DeleteObject3List()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
AggregatedCollection.UnassignCollectionObject3();
}
public void CreateObject3List()
{
if (ListTypesObject3 != null)
return;
ListTypesObject3 = new ObservableCollection<Object3>()
{
new Object3("Added object 3 from new list 3"),
};
AggregatedCollection.AssignCollectionObject3(ListTypesObject3);
}
}
存储库此分支中的完整工作示例: https://github.com/Whiletru3/pocbindingdmo/tree/ObservableCollectionAggregate