views:

21

answers:

2

I'm seeing some really weird behavior where WPF isn't doing what I expect it to do. I've managed to boil the problem down the following bit of code:

XAML:

<Window x:Class="WpfApplication3.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">

    <TabControl x:Name="tabControl">
        <TabControl.ContentTemplate>
            <DataTemplate DataType="{x:Type List}">
                <UserControl>

                    <UserControl.Resources>
                        <CollectionViewSource x:Key="filteredValues" Source="{Binding}" Filter="CollectionViewSource_Filter" />
                    </UserControl.Resources>

                    <ListBox ItemsSource="{Binding Source={StaticResource filteredValues}}" />

                </UserControl>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>

</Window>

Code-behind:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;

namespace WpfApplication3
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.tabControl.ItemsSource = new List<List<string>>()
            {
                new List<string>() { "a", "b", "c"},
            };
        }

        private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
        {
            string item = (string)e.Item;
            e.Accepted = item.StartsWith("b");
        }
    }
}

I'd expect that this code would result in a TabControl with a single tab that has a ListBox with a single item that says "b." But, instead, I get a ListBox with all 3 of the strings. Setting a breakpoint inside CollectionViewSource_Filter shows that the filter never even runs.

What's going on here? Why isn't the filter working?

I was thinking maybe it has something to do with the CollectionViewSource being a resource in a DataTemplate. The events on the ListBox fire properly. If the UserControl is not part of a DataTemplate, the Filter event works fine.

EDIT:

For example, the following works as expected, with the List being filtered as expected.

XAML:

<Window x:Class="WpfApplication3.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">

    <UserControl>

        <UserControl.Resources>
            <CollectionViewSource x:Key="filteredValues" Source="{Binding}" Filter="CollectionViewSource_Filter" />
        </UserControl.Resources>

        <ListBox ItemsSource="{Binding Source={StaticResource filteredValues}}" />

    </UserControl>

</Window>

Code behind:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;

namespace WpfApplication3
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new List<string>() { "a", "b", "c" };
        }

        private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
        {
            string item = (string)e.Item;
            e.Accepted = item.StartsWith("b");
        }
    }
}
A: 

You're using the Filter as if it's a property on the CollectionViewSource which always gets used.

It isn't. It's an event. It says, "When you filter this CollectionViewSource, this event will be called." It will respond to requests to filter, but won't trigger those requests itself.

I don't know a huge amount about CollectionViewSource, but I assume you'd have to bind it to a filtering control in order for this event to be triggered, like a Grid which allowed filtering.

Lunivore
That doesn't seem to be it. I don't think I explained what I meant in the last line well enough. But, if you just have the `UserControl` in the above XAML as a direct child of the Window, the Filter event is called as expected, and the list is filtered as expected. I'll edit the question in a moment to make it clearer.
Ashley
The MSDN article, and the sample program it links seems to agree with the way I am using:http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.filter.aspx
Ashley
Looks like you managed to solve it, anyhow - thank you for making the question clearer, and for posting your solution. Now it makes more sense.
Lunivore
A: 

Well, I don't know why it doesn't work, but at this point, I'm assuming it's a Microsoft bug. I'll probably be filing a Connect report shortly.

To work around the bug, I did the following. I created a subclass of CollectionViewSource like this:

using System.Windows.Data;

namespace WpfApplication3
{
    internal class CustomFilteredCollectionViewSource : CollectionViewSource
    {
        public CustomFilteredCollectionViewSource()
            : base()
        {
            this.Filter += CustomFilter;
        }

        private void CustomFilter(object sender, FilterEventArgs args)
        {
            string item = (string)args.Item;
            args.Accepted = item.StartsWith("b");
        }
    }
}

I then replaced

<CollectionViewSource x:Key="filteredValues" Source="{Binding}" Filter="CollectionViewSource_Filter" />

with

<local:CustomFilteredCollectionViewSource x:Key="filteredValues" Source="{Binding}" />

and it now works perfectly.

Ashley