主题已经把问题说清楚了。
<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元素符号来设置该处理程序)
实际上,您甚至不需要访问CollectionViewSource
实例,您可以直接在ViewModel中直接过滤源集合:
ICollectionView view = CollectionViewSource.GetDefaultView(collection);
view.Filter = predicate;
(请注意,ICollectionView.Filter
不像CollectionViewSource.Filter
那样是一个事件,它是一种名为Predicate<object>
类型的属性)
WPF会在将任何IEnumerable
派生的源数据绑定到ItemsControl.ItemsSource
属性时自动创建一个CollectionView
,或其派生类型之一,例如ListCollectionView
或BindingListCollectionView
。您获得哪种类型的CollectionView
取决于您提供的数据源在运行时检测到的功能。
有时即使您尝试显式地将自己特定的CollectionView
派生类型绑定到ItemsSource
,WPF数据绑定引擎也可能会使用内部类型CollectionViewProxy
对其进行包装。
ItemsControl
的ItemsSource
属性(作为绑定目标)之外,您还可以"同时"访问已应用的过滤/排序结果的有效集合--作为System.Windows.Controls.ItemCollection
的一个CollectionView
派生实例--通过从控件的Items
属性(作为绑定源)进行绑定。这使得许多简化的XAML方案成为可能。IEnumerable
源具有单个、全局共享的筛选/排序功能,则直接绑定到 ItemsSource
即可。仍然只在XAML中,您可以通过将同一控件上的Items
属性视为绑定源的 ItemCollection
来筛选/排序项目。它有许多有用的可绑定属性来控制筛选/排序。如前所述,在以这种方式绑定到相同源 IEnumerable
的所有 UI 元素之间共享筛选/排序。--或--自己创建并应用一个或多个不同(非“默认”)的CollectionView
实例。这允许每个数据绑定目标具有独立的筛选/排序设置。这也可以在XAML中完成,或者您可以创建自己的(List)CollectionView
派生类。这种方法已经得到了广泛的覆盖,但我想在这里指出的是,在许多情况下,可以使用相同的技术来简化 XAML,即通过将ItemsControl.Items
属性作为绑定源进行数据绑定,以便访问有效的CollectionView
。
摘要:
仅使用XAML,您就可以将数据绑定到表示当前WPF ItemsControl
上任何CollectionView
过滤/排序的有效结果集合,只需将其Items
属性视为只读绑定源。这将是一个System.Windows.Controls.ItemCollection
,它公开了可绑定/可变属性,用于控制活动过滤和排序条件。
[编辑] - 进一步思考:
请注意,在将您的IEnumerable
直接绑定到ItemsSource
的简单情况下,您可以在ItemsControl.Items
处绑定到的ItemCollection
将是原始集合的CollectionViewSource.GetDefaultView()
的包装器。正如上面所讨论的那样,在使用XAML时,绑定到这个UI包装器(通过ItemsControl.Items
)而不是绑定到它包装的底层视图(通过CollectionViewSource.GetDefaultView
)是一个不需要费脑筋的方法,因为前者的方法可以节省您明确提及任何CollectionView
的麻烦(在XAML中,这很尴尬)。
ItemCollection
包装了默认的CollectionView
,因此在代码后台(选择不太明显的情况下),绑定到UI发布的视图可能更加实用,因为这样可以最好地调整数据源和其UI控件目标的实际运行时能力。我使用.NET Framework 4.6.1(旧版本,我知道,但不幸的是,这是我目前情况下的限制)遇到了以下问题,即H.B.提供的已接受的解决方案:
严重程度 | 代码 | 描述 |
---|---|---|
错误 | XDG0012 | 成员“Filter”未被识别或无法访问。 |
错误 | 无法在元素“FilterExtension”上设置内容属性“Filters”。'Filters'具有不正确的访问级别或其程序集不允许访问。第xx行,第yy个位置。 |
通过更改以下内容轻松解决了此问题:
public ICollection<IFilter> Filters { get { return _filters; } }
到
public Collection<IFilter> Filters { get { return _filters; } }
public ICollection<IFilter> Filters
有关,而不是public Collection<IFilter> Filters
,请参见我的回答。 - Jonathan