2

First, I want to say that after several study on 2 different threads (shown below), I decided to post this question since it's quite different.


So, I want to bind an ItemsControl from my view to a property to get a reversed version of a collection.

I have this view (trimmed for clarity) :

<UserControl x:Class="NumberedMusicalScoresWriter.V.NotationGroupV" ...>
    ...
        <Grid>
            <ItemsControl ... 
                      ItemsSource="{Binding ReversedNotationVMs, Mode=OneWay}">
                ...
            </ItemsControl>
        </Grid>
    ...
</UserControl>

And, I have this viewmodel (trimmed for clarity) :

public class NotationGroupVM : ...
{
    ...

    public ObservableCollection<NotationVM> ReversedNotationVMs
    {
        get { return (ObservableCollection<NotationVM>)NotationVMs.Reverse(); //ERROR!! }
    }

    public ObservableCollection<NotationVM> NotationVMs
    {
        get { return _notationVMs; }
        set { _notationVMs = value; NotifyPropertyChanged("NotationVMs"); NotifyPropertyChanged("ReversedNotationVMs"); }
    }
}

But there's this error (See error comment above to spot the problematic line) :

Unable to cast object of type 'd__a01[NumberedMusicalScoresWriter.VM.NotationVM]' to type 'System.Collections.ObjectModel.ObservableCollection1[NumberedMusicalScoresWriter.VM.NotationVM]'.

I've also tried to apply .ToList<NotationVM>() before reversing, and making a new collection each time the main field got updated. But they didn't work out.

I also need to keep the reversed to be sync-ed with the un-reversed one. NOT just one time reversion only

I've also read an issue about it here and here, but all of them provide either the xaml solution only or i didn't understand them. I need the VM one.

Thanks.

Community
  • 1
  • 1
Moses Aprico
  • 1,951
  • 5
  • 30
  • 53
  • 1
    what about ICollectionView this is for sorting and filtering. – blindmeis Sep 26 '14 at 16:32
  • @blindmeis instead of `ObservableCollection`? If yes, then, I think `ObservableCollection` is mandatory in mvvm – Moses Aprico Sep 26 '14 at 16:33
  • no not instead. the ObservableCollection is your source. but you can create ICollectionViews with different sorting on it. look at CollectionViewSource to create ICollectionViews from your ObservableCollection. http://stackoverflow.com/questions/16634194/wpf-multiple-collectionview-with-different-filters-on-same-collection – blindmeis Sep 26 '14 at 16:39
  • @blindmeis never use and learn that before tbh. Is there any other possible option (with just via property in the VM)? Just in case. – Moses Aprico Sep 26 '14 at 16:43
  • ICollectionViews would be my first option but you could also try Reverse as an iEnumerable. – paparazzo Sep 26 '14 at 17:10
  • Im not against it though. Just, i need to go on business trip for 2 days in a couple hours. So if i could solve it right now, then it's better rather than postponing an unsolved problem. I'll learn about ICV after i get back though :) – Moses Aprico Sep 26 '14 at 17:11
  • @Blam you mean `NotationVMs.AsEnumerable().Reverse()`? If yes, ive tried it and failed. – Moses Aprico Sep 26 '14 at 17:13
  • did you public iEnumerable ReversedNotationVMs ? – paparazzo Sep 26 '14 at 17:14
  • Oh. Hmm.. i haven't tried that. Good point. Never crossed my mind. Thanks. – Moses Aprico Sep 26 '14 at 17:32

2 Answers2

5

I agree with the comments above that a different approach may give you a better result, but to answer the question as asked:

NotationVMs.Reverse() returns an IEnumerable. You can't cast this directly to an ObservableCollection because, even though ObservableCollection is one implementation of IEnumerable, it happens to not be the implementation that this particular function is returning. You can always cast an ObservableCollection to an IEnumerable, but the opposite is not always true (all squares are rectangles, but not all rectangles are squares).

To return a reversed collection, try this:

public ObservableCollection<NotationVM> ReversedNotationVMs
{
    get { return new ObservableCollection<NotationVM>(NotationVMs.Reverse()); }
}

In order to keep this in sync with the NotationVMs collection, you'll need to watch for collection changed events:

public ObservableCollection<NotationVM> NotationVMs
{
    get { return _notationVMs; }
    set 
    { 
        if (_notationVMs != null)
        {
            _notationVMs.CollectionChanged -= OnNotationVMsCollectionChanged;
        }
        _notationVMs = value;
        if (_notationVMs != null)
        {
            _notationVMs.CollectionChanged += OnNotationVMsCollectionChanged;
        } 
        NotifyPropertyChanged("NotationVMs"); 
        NotifyPropertyChanged("ReversedNotationVMs");
    }
}

private void OnNotationVMsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
     NotifyPropertyChanged("ReversedNotationVMs");
}

This will sync changes in your NotationVMs to your ReversedNotationVMs, but not the other way around. Since your binding to ReversedNotationVMs is one way, this should suffice.

Dan Oliphant
  • 501
  • 4
  • 6
3

just from my mind and maybe not the full answer for you. lets say we have a collection ordered by ID

  public OberservableCollection<MyNotation> MySource {get;set;}

then i can create a default view and a Reverse view

 public ICollectionView MyViewOrderedByID {get;set;}
 public ICollectionView MyViewOrderedByIDReversed {get;set;}

 //ctor
 this.MyViewOrderedByID = CollectionViewSource.GetDefaultView(this.MySource);//default
 this.MyViewOrderedByID.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Ascending));

 this.MyViewOrderedByIDReversed= new CollectionViewSource{ Source=this.MySource}.View;//new one
 this.MyViewOrderedByIDReversed.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Descending));

xaml

<ItemsControl ItemsSource="{Binding MyViewOrderedByID}"/>
<ItemsControl ItemsSource="{Binding MyViewOrderedByIDReversed}"/>

then views changes whenever the Source is changing

blindmeis
  • 22,175
  • 7
  • 55
  • 74