如何将IComparable实现为自定义类型?

时间:2017-12-24 11:39:34

标签: c# wpf

首先,对不起,如果问题有点长,但我需要解释所有细节,以提供可验证的例子。

问题摘要

在我的应用程序中,我使用名为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);
    }
}

我在这一行得到同样的例外:

enter image description here

似乎AsyncObservableCollection无法找到IComparable实现,可能这应该在CheckedListItem<T>而不是Country中实现?

0 个答案:

没有答案