【发布时间】:2011-02-17 10:43:15
【问题描述】:
我想将 ListView 绑定到 List<string>。我正在使用此代码:
somelistview.DataBindings.Add ("Items", someclass, "SomeList");
我遇到了这个异常:无法绑定到属性“项目”,因为它是只读的。
如果 Items 属性是只读的,我不知道应该如何绑定?
【问题讨论】:
标签: c# winforms data-binding
我想将 ListView 绑定到 List<string>。我正在使用此代码:
somelistview.DataBindings.Add ("Items", someclass, "SomeList");
我遇到了这个异常:无法绑定到属性“项目”,因为它是只读的。
如果 Items 属性是只读的,我不知道应该如何绑定?
【问题讨论】:
标签: c# winforms data-binding
我使用以下技术将数据绑定到 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; }
}
}
【讨论】:
private List<(string ColumnName, Func<Person, object> ValueLookup, Func<Person, string> DisplayStringLookup)> _columnMappingForPerson;
private ListViewEx<Person> listViewForPerson; 而不是 private System.Windows.Forms.ListView listViewForPerson;
或者,如果您想要数据绑定,您可以使用 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; }
}
【讨论】:
ListView 的不错的绑定实现
http://www.interact-sw.co.uk/utilities/bindablelistview/source/
【讨论】:
ListView 类不支持设计时绑定。 this project 中提供了一种替代方法。
【讨论】: