R-C
R-C

Reputation: 341

Is there a way to create a collection view that transforms it's source?

I have an ObservableCollection<T> that holds one type of object. I need this to be the source for several list controls but I also want to transform the data. For simplicity, imagine I have an ObservableCollection<int> and want to get an ICollectionView that returns strings - maybe just the integer converted to a string.

Is there a way to create such a view?

Something nice like:

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

Upvotes: 0

Views: 811

Answers (3)

Dennis
Dennis

Reputation: 37780

Collection views are about sorting, filtering, grouping. They are not about data transformation.

Actually, there's no direct need in such collection in your task interpretation. You just need to set up view properly, e.g. providing correct DataTemplate for items in your source collection:

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>

Update. According to you comment, I'd wrap entities into view models. Moreover, this is a purpose of view models:

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 ... }
    }
}

Then, instead of collection of models, I'd bind the control to the collection of view models:

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; }
}

This will keep your data class clear from extra properties, which are intended for view only.

Upvotes: 1

Luke Puplett
Luke Puplett

Reputation: 45253

I mostly agree with Dennis, however, using a binding converter to do the final transformation of the underlying bound value might help you in this situation.

I assume you have one collection of types and want different, live, projections of the data in different places.

You could bind to the same collection in each case and 'insert' a different custom converter for each case, probably in a custom DataTemplate.

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

Silverlight demo, but its the same.

Upvotes: 0

Ivan Manzhos
Ivan Manzhos

Reputation: 743

You can do a projection with LINQ

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

Will parse each element of your collection and return an integer

Upvotes: 0

Related Questions