【问题标题】:How can an attached behavior be added to a CollectionViewSource?如何将附加行为添加到 CollectionViewSource?
【发布时间】:2026-02-10 12:30:01
【问题描述】:

我正在尝试向 CollectionViewSource 添加附加行为,以便我可以在 XAML 中的视图模型上提供过滤谓词属性。

XAML 如下所示:

<DataGrid ItemsSource="{Binding}">
    <DataGrid.DataContext>
        <CollectionViewSource Source="{Binding Path=Items}"
                 acb:CollectionViewSourceItemFilter.ItemFilter="{Binding Path=ItemFilter}" />
    </DataGrid.DataContext>
</DataGrid>

但是,我收到一个错误:

A 'Binding' cannot be set on the 'SetItemFilter' property of type   
'CollectionViewSource'. A 'Binding' can only be set on a DependencyProperty of a 
DependencyObject.

CollectionViewSource 似乎是一个 DependencyObject。我不确定我做错了什么。

以下是行为代码:

public static class CollectionViewSourceItemFilter
{
    /// <summary>
    /// Gets the property value.
    /// </summary>
    public static Predicate<object> GetItemFilter(CollectionViewSource collectionViewSource)
    {
        return (Predicate<object>)collectionViewSource.GetValue(ItemFilter);
    }

    /// <summary>
    /// Sets the property value.
    /// </summary>
    public static void SetItemFilter(CollectionViewSource collectionViewSource, Predicate<object> value)
    {
        collectionViewSource.SetValue(ItemFilter, value);
    }

    /// <summary>
    /// The ItemFilter dependency property.
    /// </summary>
    public static readonly DependencyProperty ItemFilter =
        DependencyProperty.RegisterAttached(
        "ItemFilter",
        typeof(Predicate<object>),
        typeof(ItemFilterBehavior),
        new UIPropertyMetadata(null, OnItemFilterChanged));

    private static void OnItemFilterChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        CollectionViewSource collectionViewSource = depObj as CollectionViewSource;
        if (collectionViewSource == null)
            return;

        if (!Equals(e.NewValue, e.OldValue))
        {
            var newFilter = (Predicate<object>)e.NewValue;

            // Remove any previous filter.
            ItemFilterBehavior oldBehavior;
            if (behaviors.TryGetValue(collectionViewSource, out oldBehavior))
            {
                oldBehavior.Unregister();
                behaviors.Remove(collectionViewSource);
            }

            if (newFilter != null)
                behaviors.Add(collectionViewSource, new ItemFilterBehavior(collectionViewSource, newFilter));
        }
    }

    private class ItemFilterBehavior
    {
        public ItemFilterBehavior(CollectionViewSource collectionViewSource, Predicate<object> filter)
        {
            _collectionViewSource = collectionViewSource;
            _filter = filter;
            _collectionViewSource.Filter += collectionViewSource_Filter;
        }

        void collectionViewSource_Filter(object sender, FilterEventArgs e)
        {
            e.Accepted = _filter(e.Item);
        }

        public void Unregister()
        {
            _collectionViewSource.Filter -= collectionViewSource_Filter;
        }

        private readonly CollectionViewSource _collectionViewSource;
        private readonly Predicate<object> _filter;
    }

    private static readonly IDictionary<CollectionViewSource, ItemFilterBehavior> behaviors = new ConcurrentDictionary<CollectionViewSource, ItemFilterBehavior>();
}

【问题讨论】:

  • 试试这个:public static readonly DependencyProperty ItemFilterProperty = ...
  • 感谢您发布您的问题。这让我意识到有人试图做和我一样的事情。
  • @jpierson 周年纪念日后投票。有了所有关于在 MVVM 中过滤 CollectionViewSource 的讨论,您会认为这篇文章会引起更多关注。

标签: c# wpf xaml attached-properties


【解决方案1】:

在问题中发布行为代码让我弄清楚了。我的 DependencyProperty 使用 ItemFilterBehavior 而不是 CollectionViewSourceItemFilter 作为所有者类型。

【讨论】: