首先,对不起,如果问题有点长,但我需要解释所有细节,以提供可验证的例子。
问题摘要
在我的应用程序中,我使用名为ObservableCollection
的扩展类AsyncObservableCollection
,此类允许我跨多个线程向集合中添加项。
一切正常,但是当我在CollectionViewSource
上使用排序时会出现问题,事实上我需要在集合的对象上实现IComparable
接口以避免这种情况例外:
无法比较数组中的两个元素
此错误仅在我第二次向集合中添加项目时发生,因为第一次定价都运行良好。
代码结构
首先,我需要向您展示AsyncObservableCollection
,此类具有以下实现:
public class AsyncObservableCollection<T> : ObservableCollection<T>
{
private readonly SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
public AsyncObservableCollection()
{
}
public AsyncObservableCollection(IEnumerable<T> list)
: base(list)
{
}
private void ExecuteOnSyncContext(Action action)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
action();
}
else
{
_synchronizationContext.Send(_ => action(), null);
}
}
protected override void InsertItem(int index, T item)
{
ExecuteOnSyncContext(() => base.InsertItem(index, item));
}
protected override void RemoveItem(int index)
{
ExecuteOnSyncContext(() => base.RemoveItem(index));
}
protected override void SetItem(int index, T item)
{
ExecuteOnSyncContext(() => base.SetItem(index, item));
}
protected override void MoveItem(int oldIndex, int newIndex)
{
ExecuteOnSyncContext(() => base.MoveItem(oldIndex, newIndex));
}
protected override void ClearItems()
{
ExecuteOnSyncContext(() => base.ClearItems());
}
public void RemoveAll(Predicate<T> predicate)
{
//Controllo se l'insieme viene modificato
CheckReentrancy();
List<T> itemsToRemove = Items.Where(x => predicate(x)).ToList();
itemsToRemove.ForEach(item => Items.Remove(item));
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
在此AsyncObservableCollection
我还需要传递一个名为CheckedListItem
的自定义类型,此类允许我在ComboBox
中为每个Item
添加一个{{1} 1}}与CheckBox
。例如this。
Item Name
的实施是这样的:
CheckedListItem
在public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
public CheckedListItem() { }
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
我ComboBox
AsyncObservableCollection<CheckedListItem<T>>
列表Country
,基本上Country
类是这样的:
public class Country
{
public string Name { get; set; }
public string ISO { get; set; }
}
每个Country
都有一个ISO
,有关ISO
的详细信息(这个问题并不重要,您可以看到here)
XAML和收藏定价
在我的代码中,用户可以将特定Country
加载到AsyncObservableCollection<CheckedListItem<Country>>
,这将显示在以这种方式定义的ComboBox
内:
<ComboBox ItemsSource="{Binding Source={StaticResource GroupedData}}"
ItemTemplate="{StaticResource CombinedTemplate}" />
GroupedData
绑定了ISO的AsyncObservableCollection<CheckedListItem<Country>>
排序:
<CollectionViewSource Source="{Binding Countries}" x:Key="GroupedData">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Item.ISO" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<DataTemplate x:Key="NormalItemTemplate">
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Item.Name}"
Checked="Countries_Checked" Unchecked="Countries_Unchecked"/>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
</DataTemplate>
基本上当我将数据添加到集合时,我会做类似的事情:
var ckli = new CheckedListItem<Country>();
ckli.Item = new Country
{
Name = "England",
ISO = "EN"
}
Countries.Add(ckli);
其中Countries
以这种方式定义:
private AsyncObservableCollection<CheckedListItem<Country>> _countries = new AsyncObservableCollection<CheckedListItem<Country>>();
public AsyncObservableCollection<CheckedListItem<Country>> Countries
{
get { return _countries; }
set
{
_countries = value;
OnPropertyChanged(); //<- INotifyPropertyChanged of ViewModel
}
}
问题
正如我所说,当我第一次向Country
添加Countries
时,一切正常,但是当我执行相同的方法添加用户指定的Country
时,我得到上面的例外:
无法比较数组中的两个元素
我试图通过在IComparable
类上实现Country
接口来解决这个问题。所以我想我应该在ISO
属性上实现,我做了类似的事情:
public class Country : IComparer<Country>
{
public string Name { get; set; }
public string ISO { get; set; }
public int Compare(Country x, Country y)
{
if(x.ISO == y.ISO)
return 0;
if(x.ISO != y.ISO)
return -1;
return 1;
}
}
我不知道这是不是一个好的实现,我真的不使用这个接口,但我不知道是否需要对sorted属性或所有属性实现IComparer
。无论如何,这个问题并没有通过我的解决方案解决,所以我希望有人读到这个沉重而无聊的问题,给我一个帮助。
感谢。
更新#1
按照建议的示例,我尝试以这种方式实现IComparable
:
public class Country : IComparable<Country>
{
public string Name { get; set; }
public string ISO { get; set; }
public int CompareTo(object obj)
{
return CompareTo(obj as Country);
}
public int CompareTo(League other)
{
if(other == null)
{
return 1;
}
return this.ISO.CompareTo(other.ISO);
}
}
我在这一行得到同样的例外:
似乎AsyncObservableCollection
无法找到IComparable
实现,可能这应该在CheckedListItem<T>
而不是Country
中实现?