真的,主题说明了一切。
<CollectionViewSource x:Key="MyData"
Source="{Binding}" Filter="{ SomethingMagicInXaml? }" />
并不是说我不能有代码。它只是对我唠叨。
真的,主题说明了一切。
<CollectionViewSource x:Key="MyData"
Source="{Binding}" Filter="{ SomethingMagicInXaml? }" />
并不是说我不能有代码。它只是对我唠叨。
如果您“足够努力”,您几乎可以在 XAML 中做任何事情,甚至可以在其中编写整个程序。
您将永远无法绕过代码背后的问题(好吧,如果您使用库,则不必编写任何库,但应用程序当然仍然依赖它),下面是在这种特定情况下您可以做什么的示例:
<CollectionViewSource x:Key="Filtered" Source="{Binding DpData}"
xmlns:me="clr-namespace:Test.MarkupExtensions">
<CollectionViewSource.Filter>
<me:Filter>
<me:PropertyFilter PropertyName="Name" Value="Skeet" />
</me:Filter>
</CollectionViewSource.Filter>
</CollectionViewSource>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Windows;
using System.Text.RegularExpressions;
namespace Test.MarkupExtensions
{
[ContentProperty("Filters")]
class FilterExtension : MarkupExtension
{
private readonly Collection<IFilter> _filters = new Collection<IFilter>();
public ICollection<IFilter> Filters { get { return _filters; } }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new FilterEventHandler((s, e) =>
{
foreach (var filter in Filters)
{
var res = filter.Filter(e.Item);
if (!res)
{
e.Accepted = false;
return;
}
}
e.Accepted = true;
});
}
}
public interface IFilter
{
bool Filter(object item);
}
// Sketchy Example Filter
public class PropertyFilter : DependencyObject, IFilter
{
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register("PropertyName", typeof(string), typeof(PropertyFilter), new UIPropertyMetadata(null));
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(PropertyFilter), new UIPropertyMetadata(null));
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty RegexPatternProperty =
DependencyProperty.Register("RegexPattern", typeof(string), typeof(PropertyFilter), new UIPropertyMetadata(null));
public string RegexPattern
{
get { return (string)GetValue(RegexPatternProperty); }
set { SetValue(RegexPatternProperty, value); }
}
public bool Filter(object item)
{
var type = item.GetType();
var itemValue = type.GetProperty(PropertyName).GetValue(item, null);
if (RegexPattern == null)
{
return (object.Equals(itemValue, Value));
}
else
{
if (itemValue is string == false)
{
throw new Exception("Cannot match non-string with regex.");
}
else
{
return Regex.Match((string)itemValue, RegexPattern).Success;
}
}
}
}
}
如果您想在 XAML 中做某事,标记扩展是您的朋友。
(您可能想拼出扩展名的名称,即me:FilterExtension
由于 Visual Studio 中的即时检查可能会无缘无故地抱怨,它当然仍然可以编译和运行,但警告可能很烦人。也
不要指望CollectionViewSource.Filter
出现在 IntelliSense 中,它不希望您通过 XML-element-notation 设置该处理程序)
实际上你甚至不需要访问CollectionViewSource
实例,你可以直接在 ViewModel 中过滤源集合:
ICollectionView view = CollectionViewSource.GetDefaultView(collection);
view.Filter = predicate;
(请注意,这ICollectionView.Filter
不是事件CollectionViewSource.Filter
,它是 type 的属性Predicate<object>
)
每当您将任何派生源数据绑定到属性时, WPF都会自动创建一个CollectionView
— 或其派生类型之一,例如ListCollectionView
, 或—。您获得哪种类型取决于在运行时在您提供的数据源上检测到的功能。BindingListCollectionView
IEnumerable
ItemsControl.ItemsSource
CollectionView
有时,即使您尝试将自己的特定CollectionView
派生类型显式绑定到ItemsSource
,WPF 数据绑定引擎也可能会包装它(使用内部类型CollectionViewProxy
)。
自动提供的CollectionView
实例由系统基于每个集合创建和维护(注意:不是 每个 UI 控件或每个 绑定目标)。换句话说,您绑定到的每个s̲o̲u̲r̲c̲e̲集合都将有一个全球共享的“默认”视图,并且CollectionView
可以随时通过传递相同的“原始”IEnumerable
实例来检索(或按需创建)这个唯一实例再次使用静态方法CollectionViewSource.GetDefaultView()
。
CollectionView
是一个能够跟踪排序和/或过滤状态而不实际更改源的垫片。因此,如果相同的源数据被几个不同的Binding
用法引用,每个用法都不同CollectionView
,它们不会相互干扰。“默认”视图旨在优化不需要或不期望过滤和排序的非常常见且更简单的情况。
简而言之,每一个ItemsControl
具有数据绑定ItemsSource
属性的东西都会以排序和过滤功能结束,这要归功于一些流行的CollectionView
. 您可以通过从属性中IEnumerable
获取和操作“默认值”轻松地对任何给定值执行过滤/排序,但请注意,UI 中最终使用该视图的所有数据绑定目标 - 要么是因为您明确绑定到,要么因为您的来源根本不是- 都将共享相同的排序/过滤效果。CollectionView
ItemsControl.Items
CollectionViewSource.GetDefaultView()
CollectionView
在这个主题上不常提到的是,除了将源集合绑定到(作为绑定目标)的
ItemsSource
属性之外,您还可以“同时”访问应用过滤/排序结果的有效集合——暴露为—— --by从控件的属性绑定的派生实例(作为绑定源)。ItemsControl
CollectionView
System.Windows.Controls.ItemCollection
Items
这支持许多简化的 XAML 方案:
如果对给定源具有单一的、全局共享的过滤器/排序功能对IEnumerable
您的应用程序来说就足够了,那么只需直接绑定到ItemsSource
. 仍然仅在XAML中,您可以通过将Items
同一 Control 上的属性视为ItemCollection
绑定源来过滤/排序项目。它有许多有用的可绑定属性来控制过滤器/排序。如前所述,过滤/排序将在IEnumerable
以这种方式绑定到同一源的所有 UI 元素之间共享。 - 或者 -
自己创建和应用一个或多个不同的(非“默认”)CollectionView
实例。这允许每个数据绑定目标具有独立的过滤器/排序设置。这也可以在XAML中完成,和/或您可以创建自己的(List)CollectionView
派生类。这种方法在其他地方得到了很好的介绍,但我想在这里指出的是,在许多情况下,XAML 可以通过使用与属性(作为绑定源)相同的数据绑定ItemsControl.Items
技术来简化,以便访问有效的。CollectionView
摘要:
仅使用XAML ,您可以通过将其属性视为只读绑定源,将数据绑定到表示 WPF 上任何当前过滤/排序的有效结果的集合。这将公开用于控制活动过滤器和排序标准的可绑定/可变属性。CollectionView
ItemsControl
Items
System.Windows.Controls.ItemCollection
[编辑] - 进一步的想法:
请注意,在将您IEnumerable
直接绑定到的简单情况下ItemsSource
,ItemCollection
您可以随意绑定到ItemsControl.Items
原始集合的CollectionViewSource.GetDefaultView()
. 如上所述,在使用XAML的情况下,绑定到这个 UI 包装器(通过ItemsControl.Items
)是不费吹灰之力,而不是绑定到它包装的底层视图(通过CollectionViewSource.GetDefaultView
),因为前一种方法为您节省了(在XAML中,尴尬)不得不明确提及任何CollectionView
的麻烦。
但更进一步,因为它ItemCollection
包含了 default CollectionView
,所以在我看来,即使在代码隐藏(选择不太明显)中,绑定到 UI 发布的视图也可能更实用,因为这样最适合数据源及其 UI 控件目标的实际运行时功能。