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

13

我已经在XAML设计器中使用ICollectionView绑定了一个GridView,但由于CollectionView中的实体已转换为Object类型且无法访问实体属性,因此属性未知。它可以运行,没有错误,但设计师显示为错误。如果我绑定到集合,我可以正常访问属性。

例如,实体是具有string Name属性的Person,我将它们放置在ObservableCollection<Person>中并从中获取视图,然后将其绑定到GridView.ItemsSource。现在,当我尝试设置列标题DataMemberBinding.FirstName属性时,设计师将其显示为错误:

无法解析类型为对象的数据上下文中的“FirstName”属性

这是一个错误还是Resharper在跟我开玩笑?

示例代码:

public class Person 
{
    public string FirstName{
       get { return _firstName; }
       set { SetPropertyValue("FirstName", ref _firstName, value); }
    }
}
public class DataService 
{
    public IDataSource DataContext { get; set; }
    public ICollectionView PersonCollection{ get; set; }

    public DataService()
    {
        DataContext = new DataSource();
        //QueryableCollectionView is from Telerik 
        //but if i use any other CollectionView same thing
        //DataContext Persons is an ObservableCollection<Person> Persons
        PersonCollection = new QueryableCollectionView(DataContext.Persons);
    }
}

<telerik:RadGridView x:Name="ParentGrid" 
    ItemsSource="{Binding DataService.PersonCollection}"
    AutoGenerateColumns="False">
    <telerik:RadGridView.Columns >
        <telerik:GridViewDataColumn Header="{lex:Loc Key=FirstName}"  
            DataMemberBinding="{Binding FirstName}"/>
    </telerik:RadGridView.Columns>
</telerik:RadGridView>

这里输入图片描述

3个回答

9

Resharper提示您在XAML视图中的警告,是因为控件的设计时视图不知道它的数据上下文是什么类型。您可以使用d:DesignInstance来帮助绑定。

请添加以下内容(请根据需要替换程序集/命名空间/绑定目标名称)

<UserControl x:Class="MyNamespace.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup‐compatibility/2006"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lcl="clr‐namespace:MyAssembly"
d:DataContext="{d:DesignInstance Type=lcl:ViewModel}">

1
你的实体没有被转换为对象,这是因为界面ICollectionView不是通用集合,因此ReSharper无法知道它保存的是Person的集合。
你可以创建一个ICollectionView的通用版本,并将其用于PersonCollection属性,如此文章https://benoitpatra.com/2014/10/12/a-generic-version-of-icollectionview-used-in-a-mvvm-searchable-list/所示。
首先是一些接口:
public interface ICollectionView<T> : IEnumerable<T>, ICollectionView
{
}

public interface IEditableCollectionView<T> : IEditableCollectionView
{
}

实现方式如下:
public class GenericCollectionView<T> : ICollectionView<T>, IEditableCollectionView<T>
{
    readonly ListCollectionView collectionView;

    public CultureInfo Culture
    {
        get => collectionView.Culture;
        set => collectionView.Culture = value;
    }

    public IEnumerable SourceCollection => collectionView.SourceCollection;

    public Predicate<object> Filter
    {
        get => collectionView.Filter;
        set => collectionView.Filter = value;
    }

    public bool CanFilter => collectionView.CanFilter;

    public SortDescriptionCollection SortDescriptions => collectionView.SortDescriptions;

    public bool CanSort => collectionView.CanSort;

    public bool CanGroup => collectionView.CanGroup;

    public ObservableCollection<GroupDescription> GroupDescriptions => collectionView.GroupDescriptions;

    public ReadOnlyObservableCollection<object> Groups => collectionView.Groups;

    public bool IsEmpty => collectionView.IsEmpty;

    public object CurrentItem => collectionView.CurrentItem;

    public int CurrentPosition => collectionView.CurrentPosition;

    public bool IsCurrentAfterLast => collectionView.IsCurrentAfterLast;

    public bool IsCurrentBeforeFirst => collectionView.IsCurrentBeforeFirst;

    public NewItemPlaceholderPosition NewItemPlaceholderPosition
    {
        get => collectionView.NewItemPlaceholderPosition;
        set => collectionView.NewItemPlaceholderPosition = value;
    }

    public bool CanAddNew => collectionView.CanAddNew;

    public bool IsAddingNew => collectionView.IsAddingNew;

    public object CurrentAddItem => collectionView.CurrentAddItem;

    public bool CanRemove => collectionView.CanRemove;

    public bool CanCancelEdit => collectionView.CanCancelEdit;

    public bool IsEditingItem => collectionView.IsEditingItem;

    public object CurrentEditItem => collectionView.CurrentEditItem;

    public event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add => ((ICollectionView) collectionView).CollectionChanged += value;
        remove => ((ICollectionView) collectionView).CollectionChanged -= value;
    }

    public event CurrentChangingEventHandler CurrentChanging
    {
        add => ((ICollectionView) collectionView).CurrentChanging += value;
        remove => ((ICollectionView) collectionView).CurrentChanging -= value;
    }

    public event EventHandler CurrentChanged
    {
        add => ((ICollectionView) collectionView).CurrentChanged += value;
        remove => ((ICollectionView) collectionView).CurrentChanged -= value;
    }

    public GenericCollectionView([NotNull] ListCollectionView collectionView)
    {
        this.collectionView = collectionView ?? throw new ArgumentNullException(nameof(collectionView));
    }

    public IEnumerator<T> GetEnumerator()
    {
        return (IEnumerator<T>) ((ICollectionView) collectionView).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((ICollectionView) collectionView).GetEnumerator();
    }

    public bool Contains(object item)
    {
        return collectionView.Contains(item);
    }

    public void Refresh()
    {
        collectionView.Refresh();
    }

    public IDisposable DeferRefresh()
    {
        return collectionView.DeferRefresh();
    }

    public bool MoveCurrentToFirst()
    {
        return collectionView.MoveCurrentToFirst();
    }

    public bool MoveCurrentToLast()
    {
        return collectionView.MoveCurrentToLast();
    }

    public bool MoveCurrentToNext()
    {
        return collectionView.MoveCurrentToNext();
    }

    public bool MoveCurrentToPrevious()
    {
        return collectionView.MoveCurrentToPrevious();
    }

    public bool MoveCurrentTo(object item)
    {
        return collectionView.MoveCurrentTo(item);
    }

    public bool MoveCurrentToPosition(int position)
    {
        return collectionView.MoveCurrentToPosition(position);
    }

    public object AddNew()
    {
        return collectionView.AddNew();
    }

    public void CommitNew()
    {
        collectionView.CommitNew();
    }

    public void CancelNew()
    {
        collectionView.CancelNew();
    }

    public void RemoveAt(int index)
    {
        collectionView.RemoveAt(index);
    }

    public void Remove(object item)
    {
        collectionView.Remove(item);
    }

    public void EditItem(object item)
    {
        collectionView.EditItem(item);
    }

    public void CommitEdit()
    {
        collectionView.CommitEdit();
    }

    public void CancelEdit()
    {
        collectionView.CancelEdit();
    }
}

最后,用法如下:
ICollectionView<Person> PersonCollectionView { get; }

在构造函数中:
var view = (ListCollectionView) CollectionViewSource.GetDefaultView(PersonCollection);
PersonCollectionView = new GenericCollectionView<Person>(view);

0

对于带有CollectionViewSource的DataGrid,既不能直接使用d:DataContext =“{d:DesignInstance Type=lcl:ViewModel}”,也不能直接使用GenericCollectionView。

    <DataGrid AutoGenerateColumns="False"
              ItemsSource="{Binding collectionViewSource.View}" 
              SelectedItem="{Binding SelectedRow}" 

我们无法设置 "d:DataContext",因为我们经常需要将多个属性绑定到视图模型上。
CollectionViewSource 创建新的 ListCollectionView,在每次设置 Source 属性时运行实例化。由于设置 Source 属性是刷新一系列行的唯一合理方法,因此我们无法保留 GenericCollectionView。
我的解决方案可能非常明显,但我放弃了 CollectionViewSource。通过创建一个属性来使其创建...
private ObservableCollection<ListingGridRow> _rowDataStoreAsList;
public GenericCollectionView<ListingGridRow> TypedCollectionView
{
  get => _typedCollectionView;
  set { _typedCollectionView = value; OnPropertyChanged();}
}

public void FullRefresh()
{
    var listData = _model.FetchListingGridRows(onlyListingId: -1);
    _rowDataStoreAsList = new ObservableCollection<ListingGridRow>(listData);
    var oldView = TypedCollectionView;
    var saveSortDescriptions = oldView.SortDescriptions.ToArray();
    var saveFilter = oldView.Filter;
    TypedCollectionView = new GenericCollectionView<ListingGridRow>(new ListCollectionView(_rowDataStoreAsList));
    var newView = TypedCollectionView;
    foreach (var sortDescription in saveSortDescriptions)
    {
        newView.SortDescriptions.Add(new SortDescription()
        {
            Direction = sortDescription.Direction,
            PropertyName = sortDescription.PropertyName
        });
    }
    newView.Filter = saveFilter;
}
internal void EditItem(object arg)
{
    var view = TypedCollectionView;
    var saveCurrentPosition = view.CurrentPosition;
    var originalRow = view.TypedCurrentItem;
    if (originalRow == null)
        return;
    var listingId = originalRow.ListingId;
    var rawListIndex = _rowDataStoreAsList.IndexOf(originalRow);
    // ... ShowDialog ... DialogResult ...
    var lstData = _model.FetchListingGridRows(listingId);
    _rowDataStoreAsList[rawListIndex] = lstData[0];
    view.MoveCurrentToPosition(saveCurrentPosition);
    view.Refresh();
}

在Maxence提供的GenericCollectionView中添加以下代码: public T TypedCurrentItem => (T)collectionView.CurrentItem;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接