Reputation: 1733
I have some sort of cascaded containers that contain notes, where there is one master container, that contains all notes. The notes containers are made in a tree like hierarchy that get more specific the deeper you are in tree-structure. The reason why I have only one list has to do with a very complex house keeping of the data and it's not part of the problem.
The master note container has an ObservableCollection
and all sub notes containers are bound to the ObservableCollection
via CollectionView
. The sub notes containers have a filter that filter out their notes. In regular code everything works fine and the view always shows the elements, but when I bind them to a e. g. ListBox
, the elements are not filtered and all elements from the master list are shown without filtering. Of course I know there is a ListCollectionView
, but since CollectionView is from IEnumerable
, I'm curious how the ListBox
has access to the master list, if it does not access the SourceCollection
from the CollectionView
.
In other words, I'm not quite sure why I need the ListCollectionView
for very basic behavior where the ColletionView
should fit. It seems to me that the ListCollectionView
is mandatory and not other view really fits to the ListBox
?
Here is a small sample
XAML:
<Window x:Class="ListCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal">
<ListBox Width="100" ItemsSource="{Binding Model}"></ListBox>
<ListBox Width="100" ItemsSource="{Binding View1}"></ListBox>
<ListBox Width="100" ItemsSource="{Binding View2}"></ListBox>
</StackPanel>
</Window>
C#:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace ListCollection
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<int> Model
{
get; private set;
}
public ICollectionView View1
{
get; private set;
}
public ICollectionView View2
{
get; private set;
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Model = new ObservableCollection<int>();
View1 = new CollectionView(Model);
View1.Filter = (o) =>
{
return ((int)o) > 50;
};
View2 = new CollectionView(View1);
View2.Filter = (o) =>
{
return ((int)o) > 70;
};
for (int i = 0; i < 100; i++)
Model.Add(i);
}
}
}
Thanks Martin
Upvotes: 2
Views: 2660
Reputation: 3404
The very first line in the remarks section of the documentation for the CollectionView class says:
You should not create objects of this class in your code.
So, I am guessing it is probably not designed to be used the way you are using it.
I always use CollectionViewSource.GetDefaultView(collection)
(which will end up returning a ListCollectionView
for ObservableCollection<T>
, but it is returned as an ICollectionView
).
EDIT: To hopefully clear some things up, here is some additional information about collection views.
There is a pretty decent overview of collection views on the Data Binding Overview documentation page which is a good read. It explains why there is one default view per collection. It is created by the framework any time a collection is used as the source of a data binding. The GetDefaultView
method gets that view (and creates it if it did not already exist). Subsequent calls to the method will always return the same view.
If you bind an ItemsControl.ItemsSource
directly to a collection, it will use the default view. So, binding to the default view and binding to the collection itself have the same result. If you want to have multiple views into the same collection, then you will want to create your own collection views and bind to those explicitly, rather than binding to the collection or the default view.
There are a couple ways to create collection views, depending if you are creating them from code or xaml.
Creating from Viewmodel Code
Create a new ListCollectionView
, passing the collection to the constructor. If you are in viewmodel code, you can then expose the view as a property (usually of type ICollectionView
).
Viewmodel code:
private ObservableCollection<Item> mItems;
public ICollectionView MyView { get; private set; }
public MyVM()
{
mItems = new ObservableCollection<Item>();
ListCollectionView myView = new ListCollectionView(mItems);
// Do whatever you want with the view here
MyView = myView;
}
View code:
<ItemsControl ItemsSource="{Binding MyView}" />
Creating from XAML View
Create a CollectionViewSource
and set its Source
property to the collection. You can also set other properties such as Filter
, which will call into the code-behind to run the filter.
Viewmodel code:
private ObservableCollection<Item> mItems;
public IEnumerable<Item> Items { get { return mItems; } }
public MyVM()
{
mItems = new ObservableCollection<Item>();
}
View code:
<Grid>
<Grid.Resources>
<CollectionViewSource
x:Key="MyItemsSource"
Source="{Binding Items}" />
</Grid.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource MyItemsSource}}" />
</Grid>
Upvotes: 5