Wpf ICollectionView 绑定项无法解析类型对象的属性

作者:编程家 分类: swift 时间:2025-07-01

使用 WPF 的开发人员都知道,ICollectionView 是一种非常有用的界面控制器,它允许我们对集合数据进行排序、过滤和分组。然而,有时候我们会遇到一种情况,就是无法解析绑定项的某个属性,这给我们带来了很多困扰。本文将介绍这个问题,并提供一些解决方案。

首先,我们来看一个简单的示例。假设我们有一个 Person 类,其中包含 Name 和 Age 两个属性。我们想要在界面上显示一个人的名字和年龄,并且能够对这些人根据年龄进行排序。我们可以使用一个 ICollectionView 对象来实现这个功能。

csharp

public class Person

{

public string Name { get; set; }

public int Age { get; set; }

}

public class MainViewModel

{

public ICollectionView People { get; set; }

public MainViewModel()

{

List people = new List

{

new Person { Name = "Alice", Age = 25 },

new Person { Name = "Bob", Age = 30 },

new Person { Name = "Charlie", Age = 20 }

};

People = CollectionViewSource.GetDefaultView(people);

People.SortDescriptions.Add(new SortDescription("Age", ListSortDirection.Ascending));

}

}

在这个示例中,我们创建了一个 MainViewModel 类,其中包含一个 People 属性,它是一个 ICollectionView 对象。在构造函数中,我们创建了一个 Person 对象的列表,并将其赋值给 People 属性。然后,我们添加了一个 SortDescription 对象,指定按照 Age 属性进行升序排序。

接下来,我们在 XAML 中绑定这个 ICollectionView 对象,将人的名字和年龄显示在界面上。

xaml

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:local="clr-namespace:WpfCollectionViewExample"

Title="MainWindow" Height="450" Width="800">

运行这个示例,我们可以看到界面上显示了三个人的名字和年龄,并且按照年龄进行了排序。这是我们期望的结果。

然而,有时候我们会遇到一种情况,就是无法解析绑定项的某个属性。比如,我们想要给人的名字添加一个长度属性,以便根据名字长度进行排序。我们可以在 Person 类中添加一个 Length 属性,并将其与 Name 属性关联起来。

csharp

public class Person

{

public string Name { get; set; }

public int Age { get; set; }

public int Length => Name.Length;

}

然后,我们需要修改 MainViewModel 类,将 SortDescription 对象的排序属性从 "Age" 改为 "Length"。

csharp

People.SortDescriptions.Add(new SortDescription("Length", ListSortDirection.Ascending));

然而,当我们运行这个示例时,会发现界面上什么也没有显示。这是因为 ICollectionView 无法解析绑定项的 Length 属性。这个问题可能会让我们感到困惑和无奈。

解决方案

那么,我们该如何解决这个问题呢?其实,有几种方法可以解决这个问题。下面,我们将介绍其中的两种方法。

方法一:使用自定义排序

第一种方法是使用自定义排序。我们可以通过实现 IComparer 接口来定义一个自定义的比较器,并将其应用到 ICollectionView 对象上。

csharp

public class NameLengthComparer : IComparer

{

public int Compare(object x, object y)

{

if (x is Person personX && y is Person personY)

{

return personX.Length.CompareTo(personY.Length);

}

return 0;

}

}

然后,在 MainViewModel 类的构造函数中,我们需要创建一个 CollectionViewSource 对象,并将自定义的比较器应用到其中。

csharp

CollectionViewSource collectionViewSource = new CollectionViewSource();

collectionViewSource.Source = people;

collectionViewSource.SortDescriptions.Add(new SortDescription("Length", ListSortDirection.Ascending));

collectionViewSource.SortDescriptions.Add(new SortDescription(string.Empty, ListSortDirection.Ascending));

collectionViewSource.CustomSort = new NameLengthComparer();

People = collectionViewSource.View;

在这个示例中,我们首先创建了一个 CollectionViewSource 对象,并将 Person 对象的列表作为其 Source 属性。然后,我们添加了一个 SortDescription 对象,指定按照 Length 属性进行升序排序。接下来,我们添加了一个 SortDescription 对象,它的属性名为空字符串,这是为了确保自定义的比较器会被调用。最后,我们将自定义的比较器赋值给 CollectionViewSource 对象的 CustomSort 属性。

运行这个示例,我们可以看到界面上按照人名长度进行了排序,并且名字和年龄也被正确地显示出来了。

方法二:使用动态对象

第二种方法是使用动态对象。我们可以通过实现 IDynamicMetaObjectProvider 接口来定义一个动态对象,并在其中添加我们想要的属性。

csharp

public class DynamicPerson : DynamicObject

{

private readonly Person _person;

public DynamicPerson(Person person)

{

_person = person;

}

public override bool TryGetMember(GetMemberBinder binder, out object result)

{

switch (binder.Name)

{

case "Name":

result = _person.Name;

return true;

case "Age":

result = _person.Age;

return true;

case "Length":

result = _person.Name.Length;

return true;

default:

result = null;

return false;

}

}

}

然后,在 MainViewModel 类的构造函数中,我们需要将 Person 对象转换为动态对象,并将其赋值给 ICollectionView 对象。

csharp

List dynamicPeople = people.Select(person => new DynamicPerson(person)).ToList();

People = CollectionViewSource.GetDefaultView(dynamicPeople);

People.SortDescriptions.Add(new SortDescription("Length", ListSortDirection.Ascending));

在这个示例中,我们首先创建了一个 DynamicPerson 类,它继承自 DynamicObject。在其中,我们重写了 TryGetMember 方法,根据属性名返回对应的值。然后,我们创建了一个 DynamicPerson 对象的列表,并将其转换为 ICollectionView 对象。

运行这个示例,我们可以看到界面上按照人名长度进行了排序,并且名字和年龄也被正确地显示出来了。

在本文中,我们介绍了使用 WPF 的 ICollectionView 对象绑定项无法解析类型对象的属性的问题,并提供了两种解决方案。第一种方法是使用自定义排序,通过实现 IComparer 接口来定义一个自定义的比较器,并应用到 ICollectionView 对象上。第二种方法是使用动态对象,通过实现 IDynamicMetaObjectProvider 接口来定义一个动态对象,并在其中添加我们想要的属性。希望这些解决方案能够帮助到你解决类似的问题。