2

一个简单的例子:

BindingList<Dog> dogs = kennel.Dogs;

// Works great!
listBoxDogs.DataSource = dogs;

// Confuses me.
listViewDogs.? = dogs;

我已经探索了 listViewDogs.DataBindings 属性,但我无法梳理出与我在使用 DataSource 的 listBox 控件中看到的类似行为。

必须有更好的方法来更新 listViewDogs.Items 集合,然后捕获 dog.ListChanged 事件并手动操作 listViewDogs.Items 集合。

我错过了什么?

4

2 回答 2

1

不幸的是,列表视图不支持这种方式的数据绑定。

这是一个关于如何通过创建新控件来实现它的教程。

http://www.codeproject.com/KB/list/ListView_DataBinding.aspx

附言。还有更多!

于 2011-01-20T16:32:23.620 回答
1

好吧,接受答案中的 CodeProject 示例很糟糕。

所以,

还有更多!

但是哪里?

等等,这里有一个。我已经实现了一种更简单、经过测试并且最重要的是可以使用的ListView.

好吧,它并不是真正可绑定的,但它支持任何实现INotifyCollectionChanged其底层类型为 implementation 的泛型类INotifyPropertyChanged,例如ObservableCollection<T> where T : INotifyPropertyChanged.

public class BindableListView : ListView
{
    private const string DataCategoryName = "Data";


    private INotifyCollectionChanged _collection;
    [Category(DataCategoryName)]
    public INotifyCollectionChanged Collection
    {
        get { return _collection; }
        set
        {
            if (_collection != null) _collection.CollectionChanged -= CollectionChanged;
            _collection = value;
            BindObject(_collection);
            if (_collection != null) _collection.CollectionChanged += CollectionChanged;
        }
    }

    private const bool DefaultDefaultBrowsableState = false;
    [Category(DataCategoryName)]
    [DefaultValue(DefaultDefaultBrowsableState)]
    public bool DefaultBrowsableState { get; set; } = DefaultDefaultBrowsableState;


    private void BindObject(object obj)
    {
        Clear();
        if (obj != null)
        {
            Columns.AddRange(obj.GetType().GetGenericArguments().FirstOrDefault()?.GetProperties().Where(p =>
            {
                return p.GetCustomAttributes(true).OfType<BrowsableAttribute>().FirstOrDefault()?.Browsable ?? DefaultBrowsableState;
            }).Select(p =>
            {
                return new ColumnHeader()
                {
                    Name = p.Name,
                    Text = p.GetCustomAttributes(true).OfType<DisplayNameAttribute>().FirstOrDefault()?.DisplayName ?? p.Name
                };
            }).ToArray());
            AddItems(obj as System.Collections.IEnumerable);
            AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
            AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
        }
    }

    private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                AddItems(e.NewItems);
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (var oldItem in e.OldItems)
                {
                    if (Items.OfType<ListViewItem>().FirstOrDefault(item => Equals(item.Tag, oldItem)) is ListViewItem itemToRemove)
                    {
                        UnregisterItem(oldItem);
                        Items.Remove(itemToRemove);
                    }
                }
                break;
            case NotifyCollectionChangedAction.Replace:
                if (e.OldItems.Count == e.NewItems.Count)
                {
                    var count = e.OldItems.Count;
                    for (var i = 0; i < count; i++)
                    {
                        var itemPair = new { Old = e.OldItems[i], New = e.NewItems[i] };
                        if (Items.OfType<ListViewItem>().FirstOrDefault(item => Equals(item.Tag, itemPair.Old)) is ListViewItem itemToReplace)
                        {
                            UnregisterItem(itemPair.Old);
                            RegisterItem(itemPair.New);
                            itemToReplace.Tag = itemPair.New;
                            foreach (ColumnHeader column in Columns)
                            {
                                itemToReplace.SubItems[column.Index].Text = itemPair.New.GetType().GetProperty(column.Name).GetValue(itemToReplace)?.ToString();
                            }
                        }
                    }
                }
                break;
            case NotifyCollectionChangedAction.Move:
                foreach (var oldItem in e.OldItems)
                {
                    if (Items.OfType<ListViewItem>().FirstOrDefault(item => Equals(item.Tag, oldItem)) is ListViewItem itemToMove)
                    {
                        Items.Remove(itemToMove);
                        Items.Insert(e.NewStartingIndex, itemToMove);
                    }
                }
                break;
            case NotifyCollectionChangedAction.Reset:
                Items.Clear();
                break;
            default:
                break;
        }
        AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
    }

    private void AddItems(System.Collections.IEnumerable items)
    {
        Items.AddRange((items ?? Enumerable.Empty<object>()).OfType<object>().Select(item =>
        {
            RegisterItem(item);
            return new ListViewItem(Columns.OfType<ColumnHeader>().Select(column =>
            {
                return item.GetType().GetProperty(column.Name).GetValue(item)?.ToString() ?? "";
            }).ToArray())
            {
                Tag = item
            };
        }).ToArray());
    }

    private void RegisterItem(object item)
    {
        if(item is INotifyPropertyChanged observableItem) observableItem.PropertyChanged += ObservableItem_PropertyChanged;
    }

    private void UnregisterItem(object item)
    {
        if (item is INotifyPropertyChanged observableItem) observableItem.PropertyChanged -= ObservableItem_PropertyChanged;
    }

    private void ObservableItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (Items.OfType<ListViewItem>().FirstOrDefault(itm => Equals(itm.Tag, sender)) is ListViewItem item)
        {
            if (Columns[e.PropertyName] is ColumnHeader column)
                item.SubItems[column.Index].Text = sender.GetType().GetProperty(e.PropertyName).GetValue(sender)?.ToString();
        }
    }
}

关于使用这个类,你只需要知道两件事。

  1. Out of the box 之间的主要区别在于ListView名为Collectiontype的新属性INotifyCollectionChanged。您仍然可以操纵Items集合,但我不建议这样做。预计您作为数据源提供的对象正在实现IEnumerable接口,并且其底层类型正在实现INotifyPropertyChanged。我没有将Collection属性限制到这些接口的原因是我想在分配属性时避免额外的强制转换。您始终可以为这些接口添加额外的检查并抛出 ArgumentException 以避免任何意外行为。

  2. 还有一个称为附加属性的属性,它设置表示未指定DefaultBrowsableState的数据源对象属性的列的默认可见性。BrowsableAttribute原因是当您将它ListView与另一个使用BrowsableAttribute(例如PropertyGrid)的控件一起使用时,您希望隐藏列表中的某些属性,同时保留它们在另一个控件上的可见性。然后,您可以将 设置DefaultBrowsableState为 false 并将[Browsable(true)]属性添加到您希望在列表中看到的所有属性。

于 2018-07-30T23:15:19.460 回答