2
votes

Hello I have written small program in MVVM fashion to test usage of CollectionViewSource. I have a UserControl that contains ListBox and has Dependency Property Items, which "forwards" binding items from this control to ListBox ItemsSource:

<UserControl x:Class="TestingCollectionViewSource.TestControlWithListBox"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:TestingCollectionViewSource="clr-namespace:TestingCollectionViewSource" 
                mc:Ignorable="d" 
                d:DesignHeight="300" d:DesignWidth="300">
       <Grid>
           <ListBox ItemsSource="{Binding Items,
           RelativeSource={RelativeSource AncestorType=TestingCollectionViewSource:TestControlWithListBox}}" />
       </Grid>
   </UserControl>


public partial class TestControlWithListBox : UserControl
    {
        public static readonly DependencyProperty ItemsProperty =
            DependencyProperty.Register("Items", typeof (IEnumerable<string>), typeof (TestControlWithListBox), new PropertyMetadata(default(IEnumerable<string>)));

        public IEnumerable<string> Items
        {
            get { return (IEnumerable<string>) GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }

        public TestControlWithListBox()
        {
            InitializeComponent();
        }
    }

I tried binding to Items with ObservableCollection and with View of CollectionViewSource. It deos work for ObservableCollection but does not for CollectionViewSource.

<Window x:Class="TestingCollectionViewSource.MainWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:TestingCollectionViewSource="clr-namespace:TestingCollectionViewSource" Width="500" Height="500">
    <Grid>
        <TestingCollectionViewSource:TestControlWithListBox Items="{Binding Path=ItemsCollectionViewSource.View}"/>
    </Grid>
</Window>

public class MainWindowViewModel : Screen
{
    private ObservableCollection<string> items;

    public ObservableCollection<string> Items
    {
        get { return items; }
        set
        {
            items = value;
            NotifyOfPropertyChange(() => Items);
        }
    }

    private CollectionViewSource itemsCollectionViewSource;

    public CollectionViewSource ItemsCollectionViewSource
    {
        get { return itemsCollectionViewSource; }
        set
        {
            itemsCollectionViewSource = value;
            NotifyOfPropertyChange(() => ItemsCollectionViewSource);
        }
    }

    public MainWindowViewModel()
    {
        DisplayName = "Testing testing testing";
        Items = new ObservableCollection<string>()
            {
                "1",
                "2 2",
                "3 3 3",
                "4 4 4 4"
            };

        ItemsCollectionViewSource = new CollectionViewSource() { Source = Items};
    }
}

However if I would try to bind CollectionViewSource.View to ListBox there is no problem and all list items are included in ListBox like in the picture below:

CollectionViewSource Test

What could be the reason of this behaviour and is there any solution?

1

1 Answers

4
votes

For binding to work, source property and target property should be of same data types.

If you closely look at your Output window, you will see binding error logged over there for mismatch data types of source and target property.


CollectionViewSource.View property is of type ICollectionView which inherits from IEnumerable and not from IEnumerable<string>. Hence, source property type is IEnumerable, whereas your Items DP is of type IEnumerable<string>. Hence, its not working.

Whereas it works for ObservableCollection<string> because it implements both IEnumerable<string> and IEnumerable.

As evident from above that all list directly or indirectly implements IEnumerable. So, what you can do is make your Items DP of type IEnumerable instead of IEnumerable<string> and it will work for CollectionViewSource as well.

public static readonly DependencyProperty ItemsProperty =
      DependencyProperty.Register("Items", typeof (IEnumerable),
    typeof (TestControlWithListBox), new PropertyMetadata(default(IEnumerable)));

public IEnumerable<string> Items
{
   get { return (IEnumerable) GetValue(ItemsProperty); }
   set { SetValue(ItemsProperty, value); }
}