Есть ли способ создать представление коллекции, которое преобразует его источник?

У меня есть ObservableCollection<T>, который содержит один тип объекта. Мне нужно, чтобы это было источником для нескольких элементов управления списком, но я также хочу преобразовать данные. Для простоты представьте, что у меня есть ObservableCollection<int> и я хочу получить ICollectionView, который возвращает строки — может быть, просто целое число, преобразованное в строку.

Есть ли способ создать такое представление?

Что-то приятное, например:

var view = new MagicalCollectionView(myCollection, x => x.ToString())?

person R-C    schedule 09.07.2013    source источник


Ответы (3)


Представления коллекции — это сортировка, фильтрация, группировка. Они не о преобразовании данных.

На самом деле прямой необходимости в такой коллекции в интерпретации вашей задачи нет. Вам просто нужно правильно настроить view, например. предоставление правильного DataTemplate для элементов в вашей исходной коллекции:

public class MyEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MyViewModel
{
    public ObservableCollection<MyEntity> Entities { get; private set; }
    public ICollectionView EntitiesView
    {
        if (view == null)    
        {
            view = new ListCollectionView(Entities);
        }
        return view;
    }
    private ICollectionView view;
}

XAML:

<DataTemplate DataType="{x:Type local:MyEntity}">
    <!-- This "transforms" visual representation of your entity, but the entity itself (and its container) remains unchanged -->
    <TextBlock Text="{Binding Name}"/>
</DataTemplate>

Обновить. Согласно вашему комментарию, я бы обернул объекты в модели просмотра. Более того, это цель моделей просмотра:

public class MyEntityViewModel
{
    private readonly MyEntity model;

    public MyEntityViewModel(MyEntity model)
    {
        this.model = model;
    }

    public int MyInt
    {
        get { return model. // some logic to retrieve int ... }
    }

    public string MyString
    {
        get { return model. // some logic to retrieve string ... }
    }
}

Затем вместо коллекции моделей я бы привязал элемент управления к коллекции моделей представлений:

public class MyViewModel
{
    public MyViewModel(ICollection<MyEntity> entities)
    {
        this.Entities = new ObservableCollection<MyEntityViewModel>(entities.Select(e => new MyEntityViewModel(e)));

        // this will keep two collections synchronized:
        this.Entities.CollectionChanged += (sender, args) =>
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    entities.Add((MyEntity)e.NewItems[0]);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    entities.Remove((MyEntity)e.OldItems[0]);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    entities.Remove((MyEntity)e.OldItems[0]);
                    entities.Add((MyEntity)e.NewItems[0]);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    entities.Clear();
                    break;
            }
        }
    }            

    public ObservableCollection<MyEntityViewModel> Entities { get; private set; }
}

Это защитит ваш класс данных от дополнительных свойств, которые предназначены только для просмотра.

person Dennis    schedule 09.07.2013
comment
К сожалению, это сделано для того, чтобы обойти ограничение элемента управления, для которого у меня нет исходного кода. Элемент управления привязывается к коллекции и отображает два списка. Коллекция может быть связана обычным образом, но списки извлекаются с использованием имени свойства, а не привязки. Я никак не могу установить шаблон, и я не хочу добавлять дополнительные свойства в свой класс данных. Атрибуты XAML аналогичны List1Property=MyInt List2Property=MyString. Идея представления заключалась в том, чтобы получить оба свойства, не затрагивая базовый тип коллекции. - person R-C; 09.07.2013
comment
Мое предложение состояло бы в том, чтобы переписать элемент управления и взять под контроль то, как он работает, вместо того, чтобы бороться с ограничениями программного обеспечения, которое не соответствует вашим требованиям. Это будет лучшим вложением времени и усилий в среднесрочной и долгосрочной перспективе. - person Alex; 09.07.2013
comment
Если бы это было возможно! Управление очень сложное - наверное месяц работы как минимум - и решение нужно немедленно (разве не всегда так!) - person R-C; 09.07.2013

Вы можете сделать проекцию с LINQ

myCollection.Select(x=> int.Parse(x))

Будет анализировать каждый элемент вашей коллекции и возвращать целое число

person Ivan Manzhos    schedule 09.07.2013
comment
А теперь попробуйте вставить что-нибудь в исходную коллекцию и отразить изменения в представлении... - person Dennis; 09.07.2013
comment
Деннис опередил меня - это должно оставаться синхронизированным. - person R-C; 09.07.2013

Я в основном согласен с Деннисом, однако использование преобразователя привязки для окончательного преобразования базового связанного значения может помочь вам в этой ситуации.

Я предполагаю, что у вас есть одна коллекция типов и вам нужны разные живые проекции данных в разных местах.

Вы можете привязываться к одной и той же коллекции в каждом случае и «вставлять» другой пользовательский преобразователь для каждого случая, возможно, в пользовательский шаблон данных.

http://www.silverlightshow.net/items/Data-Conversion-in-Silverlight-2-Data-Binding.aspx

Демо Silverlight, но это то же самое.

person Luke Puplett    schedule 09.07.2013