【问题标题】:Is it possible to bind a List to a ListView in WinForms?是否可以将列表绑定到 WinForms 中的 ListView?
【发布时间】:2011-02-17 10:43:15
【问题描述】:

我想将 ListView 绑定到 List<string>。我正在使用此代码:

somelistview.DataBindings.Add ("Items", someclass, "SomeList");

我遇到了这个异常:无法绑定到属性“项目”,因为它是只读的。

如果 Items 属性是只读的,我不知道应该如何绑定?

【问题讨论】:

    标签: c# winforms data-binding


    【解决方案1】:

    我使用以下技术将数据绑定到 ListView。

    它支持正确的(不是基于文本的)排序。在上述情况下,按字符串、日期时间和整数。

    上面的 ListView 是用这段代码生成的:

    var columnMapping = new List<(string ColumnName, Func<Person, object> ValueLookup, Func<Person, string> DisplayStringLookup)>()
    {
        ("Name", person => person.Name, person => person.Name),
        ("Date of birth", person => person.DateOfBirth, person => $"{person.DateOfBirth:dd MMM yyyy}"),
        ("Height", person => person.HeightInCentimetres, person => Converter.CentimetresToFeetInchesString(person.HeightInCentimetres))
    };
    
    var personListview = new ListViewEx<Person>(columnMapping)
    {
        FullRowSelect = true,
        View = View.Details,
        Left = 20,
        Top = 20,
        Width = 500,
        Height = 300,                
    };
    
    var people = new[]
    {
        new Person("Cathy Smith", DateTime.Parse("1980-05-15"), 165),
        new Person("Bill Wentley", DateTime.Parse("1970-10-30"), 180),
        new Person("Alan Bridges", DateTime.Parse("1990-03-22"), 190),
    };
    
    personListview.AddRange(people);
    
    Controls.Add(personListview);
    

    在列映射中,您会注意到您必须指定如何获取项目的 value(用于排序)以及 string(用于显示) .

    完整来源:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace GUI
    {
        public class ListViewEx<T> : ListView
        {
            public ListViewEx(IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> columnInfo) : base()
            {
                ColumnInfo = columnInfo;
                DoubleBuffered = true;
    
                //Create the columns
                columnInfo
                    .Select(ci => ci.ColumnName)
                    .ToList()
                    .ForEach(columnName =>
                    {
                        var col = Columns.Add(columnName);
                        col.Width = -2;
                    });
    
                //Add the sorter
                lvwColumnSorter = new ListViewColumnSorter<T>(columnInfo);
                ListViewItemSorter = lvwColumnSorter;
                ColumnClick += ListViewEx_ColumnClick;
            }
    
            IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> ColumnInfo { get; }
    
            private readonly ListViewColumnSorter<T> lvwColumnSorter;
    
            public void Add(T item)
            {
                var lvi = Items.Add("");
                lvi.Tag = item;
    
                RefreshContent();
            }
    
            public void AddRange(IList<T> items)
            {
                foreach (var item in items)
                {
                    Add(item);
                }
            }
    
            public void Remove(T item)
            {
                if (item == null) return;
    
                var listviewItem = Items
                            .Cast<ListViewItem>()
                            .Select(lvi => new
                            {
                                ListViewItem = lvi,
                                Obj = (T)lvi.Tag
                            })
                            .FirstOrDefault(lvi => item.Equals(lvi.Obj))
                            .ListViewItem;
    
                Items.Remove(listviewItem);
    
                RefreshContent();
            }
    
            public List<T> GetSelectedItems()
            {
                var result = SelectedItems
                                .OfType<ListViewItem>()
                                .Select(lvi => (T)lvi.Tag)
                                .ToList();
    
                return result;
            }
    
            public void RefreshContent()
            {
                var columnsChanged = new List<int>();
    
                Items
                    .Cast<ListViewItem>()
                    .Select(lvi => new
                    {
                        ListViewItem = lvi,
                        Obj = (T)lvi.Tag
                    })
                    .ToList()
                    .ForEach(lvi =>
                    {
                        //Update the contents of this ListViewItem
                        ColumnInfo
                            .Select((column, index) => new
                            {
                                Column = column,
                                Index = index
                            })
                            .ToList()
                            .ForEach(col =>
                            {
                                var newDisplayValue = col.Column.DisplayStringLookup(lvi.Obj);
                                if (lvi.ListViewItem.SubItems.Count <= col.Index)
                                {
                                    lvi.ListViewItem.SubItems.Add("");
                                }
    
                                var subitem = lvi.ListViewItem.SubItems[col.Index];
                                var oldDisplayValue = subitem.Text ?? "";
    
                                if (!oldDisplayValue.Equals(newDisplayValue))
                                {
                                    subitem.Text = newDisplayValue;
                                    columnsChanged.Add(col.Index);
                                }
                            });
                    });
    
                columnsChanged.ForEach(col => { Columns[col].Width = -2; });
    
                //AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
                //AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
            }
    
            private void ListViewEx_ColumnClick(object sender, ColumnClickEventArgs e)
            {
                if (e.Column == lvwColumnSorter.ColumnToSort)
                {
                    if (lvwColumnSorter.SortOrder == SortOrder.Ascending)
                    {
                        lvwColumnSorter.SortOrder = SortOrder.Descending;
                    }
                    else
                    {
                        lvwColumnSorter.SortOrder = SortOrder.Ascending;
                    }
                }
                else
                {
                    lvwColumnSorter.ColumnToSort = e.Column;
                    lvwColumnSorter.SortOrder = SortOrder.Ascending;
                }
    
                Sort();
            }
        }
    
        public class ListViewColumnSorter<T> : IComparer
        {
            public ListViewColumnSorter(IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> columnInfo)
            {
                ColumnInfo = columnInfo;
            }
    
            public int Compare(object x, object y)
            {
                if (x == null || y == null) return 0;
    
                int compareResult;
    
                var listviewX = (ListViewItem)x;
                var listviewY = (ListViewItem)y;
    
                var objX = (T)listviewX.Tag;
                var objY = (T)listviewY.Tag;
    
                if (objX == null || objY == null) return 0;
    
                var valueX = ColumnInfo[ColumnToSort].ValueLookup(objX);
                var valueY = ColumnInfo[ColumnToSort].ValueLookup(objY);
    
                compareResult = Comparer.Default.Compare(valueX, valueY);
    
                if (SortOrder == SortOrder.Ascending)
                {
                    return compareResult;
                }
                else if (SortOrder == SortOrder.Descending)
                {
                    return -compareResult;
                }
                else
                {
                    return 0;
                }
            }
    
            public int ColumnToSort { get; set; } = 0;
    
            public SortOrder SortOrder { get; set; } = SortOrder.Ascending;
    
            public IList<(string ColumnName, Func<T, object> ValueLookup, Func<T, string> DisplayStringLookup)> ColumnInfo { get; }
        }
    }
    

    【讨论】:

    • 非常有用!并且真的比使用 DataGridView 进行绑定要好得多!虽然初始化不是那么实用……
    • 对于适应常规 WinForms 有任何问题的任何人的提示:1) 在 Form 的 .cs 文件中执行:private List&lt;(string ColumnName, Func&lt;Person, object&gt; ValueLookup, Func&lt;Person, string&gt; DisplayStringLookup)&gt; _columnMappingForPerson;
    • 2) 在 InitializeComponent() 之前做所有的 columnMapping 初始化; 3) 在designer.cs private void InitializeComponent() 中做:this.listViewPerson = new ListViewEx(_columnMappingForPerson);
    • 3) 并且仍在设计器 .cs 上,您需要使用 private ListViewEx&lt;Person&gt; listViewForPerson; 而不是 private System.Windows.Forms.ListView listViewForPerson;
    • 但是,当然,它不是真正的数据源。对于小项目,没关系。对于较大的项目,我使用@Petr Havlicek indication
    【解决方案2】:

    或者,如果您想要数据绑定,您可以使用 DataGridView。当新项目添加到您的列表时,使用 BindingList 和 BindingSource 将更新您的 DataGrid。

    var barcodeContract = new BarcodeContract { Barcode = barcodeTxt.Text, Currency = currencyTxt.Text, Price = priceTxt.Text };
    
            list.Add(barcodeContract);
            var bindingList = new BindingList<BarcodeContract>(list);
            var source = new BindingSource(bindingList, null);
            dataGrid.DataSource = source;
    

    和数据模型类

        public class BarcodeContract
    {
        public string Barcode { get; set; }
        public string Price { get; set; }
        public string Currency { get; set; }
    }
    

    【讨论】:

    • 嗨@ozgur,关于您的代码的一个问题。您在数据网格中的哪里指定标题,我的意思是,您如何指示第 1 列是条形码,第 2 列是价格等等?
    【解决方案3】:

    【讨论】:

      【解决方案4】:

      ListView 类不支持设计时绑定。 this project 中提供了一种替代方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-11
        • 2010-10-24
        相关资源
        最近更新 更多