0

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())?
Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
R-C
  • 321
  • 4
  • 11

3 Answers3

1

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.

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • Unfortunately this is to get around a limitation in a control that I don't have the source for. The control binds to collection and displays two lists. The collection can be bound normally but the lists are extracted using a property name, not a binding. There's no way I can see to set a template and I don't want to add extra properties to my data class. The XAML attributes are similar to- List1Property="MyInt" List2Property="MyString". The idea of the view was to get both properties without affecting the underlying collection type. – R-C Jul 09 '13 at 11:03
  • My suggestion would be to rewrite the control and take control of how it operates, instead of struggling around limitations of a piece of software which doesn't meet your requirements. It's going to be a better investement of time and effort in the medium-to-long run. – Alex Jul 09 '13 at 11:48
  • If only that were possible! The control is very complex - probably a month's work at the bare minimum - and a solution is needed immediately (isn't that always the case!) – R-C Jul 09 '13 at 14:16
0

You can do a projection with LINQ

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

Will parse each element of your collection and return an integer

Ivan Manzhos
  • 743
  • 6
  • 12
0

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.

Luke Puplett
  • 42,091
  • 47
  • 181
  • 266