Упорядочение ObservableCollection с Linq и IComparer

Я хочу заказать ObservableCollection в алфавитном порядке, потому что мне не нужно создавать другую привязку. Я видел, что мы могли бы использовать Linq и метод OrderBy. Я также хотел бы использовать персонального IComparer, потому что мне придется организовывать сложные данные (списки в другие списки), но VS говорит мне, что я не могу использовать свой собственный компаратор:

(дать вам ссылку на ошибку, потому что мой VS на французском языке)

http://msdn.microsoft.com/en-gb/library/hxfhx4sy%28v=vs.90%29.aspx

Есть идеи?

private void SortGraphicObjectsAscending(object sender, RoutedEventArgs e)
    {
        GraphicObjects.OrderBy(graphicObject => graphicObject.Nom, new GraphicObjectComparer(true));

    }

И мой собственный компаратор (который уже проверен)

public class GraphicObjectComparer : IComparer<GraphicObject>
{
    int order = 1;

    public GraphicObjectComparer(Boolean ascending)
    {
        if (!ascending)
        {
            order = -1;
        }
    }

    public GraphicObjectComparer()
    {

    }

    public int Compare(GraphicObject x, GraphicObject y)
    {
        return String.Compare(x.Nom, y.Nom, false) * order;
    }

}

Спасибо за ваши ответы. Во всяком случае у меня есть другая проблема. Как сказал Майкл, я использую более сложный компаратор, но для другого объекта. Эта сущность отображается с иерархическим деревом, и объект может содержать список других объектов того же типа.

Я не смог протестировать свой GraphicObject, потому что у меня нет доступа к БД (в данный момент не было объекта). С тестами на моем VideoEntity кажется, что моя ObservableCollection не отсортирована так, как я хочу (я создаю другую). Я хочу изменить это в алфавитном порядке, но это не работает.

public class VideoEntityComparer : IComparer<VideoEntity>
{

    int order = 1;

    public VideoEntityComparer(Boolean ascending)
    {
        if (!ascending)
        {
            this.order = -1; // so descending
        }
    }

    public VideoEntityComparer()
    {

    }

    public int Compare(VideoEntity x, VideoEntity y)
    {
        if ((x is BaseDirectory && y is BaseDirectory) || (x is BaseSite && y is BaseSite) || (x is VideoEncoder && y is VideoEncoder))
        {
            return string.Compare(x.Nom, y.Nom, false) * order; // only objects of the same type are sorted alphabetically
        }
        else if ((x is BaseDirectory && y is BaseSite) || (x is BaseSite && y is VideoEncoder))
        {
            return -1;
        }else
        {
            return 1;
        }
    }
}

private void SortDirectoriesDescending(object sender, RoutedEventArgs e)
    {
        ObservableCollection<BaseDirectory> tempDir  = new ObservableCollection<BaseDirectory>(
            Directories.OrderBy(directory => directory, new VideoEntityComparer(false)));
        Directories = tempDir;
    }

PS: кстати, я действую по DependancyProperty. Это правильный способ сделать это? (Я новичок в WPF)

3 ответа

Решение

Проблема с этой линией в определении OrderBy что вы пытаетесь использовать:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    IComparer<TKey> comparer
)

У этого метода есть два разных общих параметра: TSource а также TKey, TSource очевидно, это так же, как TSource для источника IEnumerable, TKey Параметр - это тот, который компилятор пытается и не может сделать вывод, потому что вы пытаетесь использовать два разных типа. Ваш звонок:

GraphicObjects.OrderBy(graphicObject => graphicObject.Nom, new GraphicObjectComparer(true));

Ваш первый параметр Func<GraphicObject, string> но ваш второй параметр IComparer<GraphicObject>; это означает, что вы используете TKey = string в одном месте и TKey = GraphicObject в другом.

Первый параметр, Func<> делегат, является "ключевым селектором"; это как ты говоришь OrderBy какие значения сортировать. Так как ваш IComparer<> сортировка на основе всего GraphicObject Например, это то, что вы должны выбрать в качестве ключа:

GraphicObjects.OrderBy(go => go, new GraphicObjectComparer(true));

Я предполагаю, что ваш пользовательский объект сравнения действительно сложнее, чем вы показали, потому что ваш пример сравнения довольно избыточен:

var asc = GraphicObjects.OrderBy(go => go.Nom);
var desc = GraphicObjects.OrderByDescending(go => go.Nom);

Кроме того, обратите внимание, что ваш пример кода на самом деле ничего не делает с недавно отсортированным списком, поэтому его просто выбрасывают. Операции LINQ никогда не изменяют перечисляемый источник, вместо этого они всегда возвращают новую преобразованную копию.

Ваш comparer сравнивает GraphicObject, Так что ваши OrderBy должно быть

 GraphicObjects.OrderBy(graphicObject => graphicObject,
                                                 new GraphicObjectComparer(true));

или просто использовать

GraphicObjects.OrderBy(graphicObject => graphicObject.Nom);

КСТАТИ, OrderBy не сортирует на месте, вы должны назначить возвращаемый IEnumerable<GraphicsObject> к переменной

Проблема в том, что ваш Comparer использует GraphicObject, но вы сравниваете строки.

Если вам действительно нужен ваш собственный заказ, вы можете использовать этот Comparer:

public class GraphicObjectComparer : IComparer<string>
{
    int order = 1;

    public GraphicObjectComparer(Boolean ascending)
    {
        if (!ascending)
        {
            order = -1;
        }
    }

    public GraphicObjectComparer()
    {

    }

    public int Compare(string x, string y)
    {
        return String.Compare(x, y, false) * order;
    }
}

Или вы предоставляете GraphicObject для вашего компаратора, а не строку.

Чтобы отсортированный ObservableCollection отражал все изменения в исходной коллекции, вы можете использовать мою библиотеку ObservableComputations. Используя эту библиотеку, вы можете написать такой код:

var sortedGraphicObjects = GraphicObjects.Ordering(
    graphicObject => graphicObject.Nom, new GraphicObjectComparer(true));

sortedGraphicObjects является ObservableCollection и отражает все изменения в коллекции GraphicObjects и свойстве Nom. Убедитесь, что свойство Nom уведомляет об изменениях через интерфейс INotifyPropertyChanged.

Другие вопросы по тегам