Можно ли использовать BindingList <T>с элементом управления ListView аналогично элементу управления ListBox? - PullRequest
2 голосов
/ 20 января 2011

Простой пример:

BindingList<Dog> dogs = kennel.Dogs;

// Works great!
listBoxDogs.DataSource = dogs;

// Confuses me.
listViewDogs.? = dogs;

Я исследовал свойство listViewDogs.DataBindings, но мне не удалось выявить из него поведение, аналогичное тому, которое я вижу в элементе управления listBox с использованием DataSource.

Должен быть лучший способ обновить коллекцию listViewDogs.Items, затем перехватить событие dogs.ListChanged и выполнить манипулирование коллекцией listViewDogs.Items вручную.

Что мне не хватает?

Ответы [ 2 ]

1 голос
/ 31 июля 2018

Ну, пример с CodeProject из принятого ответа ужасен.

Итак,

есть еще много всего!

Ногде?

Подожди, вот один.Я реализовал способ проще, протестирован и, что самое важное, готов к использованию расширения ListView.

Ну, это не совсем привязка, но он поддерживает любой универсальный класс, который реализует INotifyCollectionChanged, лежащий в основеТип реализует 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 является новое свойство с именем Collection типа INotifyCollectionChanged.Вы все еще можете манипулировать коллекцией Items, но я бы не советовал.Ожидается, что объект, который вы предоставляете в качестве источника данных, реализует интерфейс IEnumerable, а также его базовый тип реализует INotifyPropertyChanged.Причина, по которой я не ограничивал свойство Collection этими интерфейсами, заключалась в том, что я хотел избежать дополнительного приведения при назначении свойства.Вы всегда можете добавить дополнительную проверку для этих интерфейсов и создать исключение ArgumentException, чтобы избежать непредвиденного поведения.

  2. Существует дополнительное свойство с именем DefaultBrowsableState, которое устанавливает видимость по умолчанию столбцов, представляющих свойстваобъект источника данных, для которого не указано BrowsableAttribute.Причина этого заключается в том, что вы используете этот ListView вместе с другим элементом управления, который использует BrowsableAttribute (например, PropertyGrid), и вы хотите скрыть некоторые свойства в списке, сохраняя их видимость на другом элементе управления.Затем вы можете установить для DefaultBrowsableState значение false и добавить атрибут [Browsable(true)] ко всем свойствам, которые вы хотите видеть в списке.

1 голос
/ 20 января 2011

Представление списка, к сожалению, не поддерживает привязку данных таким образом.

здесь приведено руководство по его реализации путем создания нового элемента управления.

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

пс.там еще много всего!

...