让您的列表项实现一个接口,该接口提供了显示所需的一切:
public interface IDisplayItem
{
event System.ComponentModel.ProgressChangedEventHandler ProgressChanged;
string Subject { get; }
string Description { get; }
}
传输对象不应该自己显示。您不应将领域逻辑(业务逻辑)和显示逻辑混合使用。
自定义 ListBox:
为了以自己的方式显示列表框项,您需要从System.Windows.Forms.ListBox
派生自己的列表框控件。在构造函数中将您的列表框的DrawMode
属性设置为DrawMode.OwnerDrawFixed
或DrawMode.OwnerDrawVariable
(如果项目大小不相同)。如果使用 OwnerDrawVariable
,则还需要覆盖OnMeasureItem
,以告诉列表框每个项目的大小。
public class TransmissionListBox : ListBox
{
public TransmissionListBox()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index >= 0 && e.Index < Items.Count) {
var displayItem = Items[e.Index] as IDisplayItem;
TextRenderer.DrawText(e.Graphics, displayItem.Subject, e.Font, ...);
e.Graphics.DrawIcon(...);
}
e.DrawFocusRectangle();
}
}
你可以让原始的传输类实现 IDisplayItem
接口,或者创建一个专门用于此目的的类。只要它们实现了接口,列表中也可以有不同类型的对象。关键是显示逻辑本身由控制类负责,传输类(或其他类)只提供所需信息。
例子:
由于与马克的持续讨论,我决定在这里包含一个完整的示例。让我们定义一个模型类:
public class Address : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value) {
_Name = value;
OnPropertyChanged("Name");
}
}
}
private string _City;
public string City
{
get { return _City; }
set
{
if (_City != value) {
_City = value;
OnPropertyChanged("City");
OnPropertyChanged("CityZip");
}
}
}
private int? _Zip;
public int? Zip
{
get { return _Zip; }
set
{
if (_Zip != value) {
_Zip = value;
OnPropertyChanged("Zip");
OnPropertyChanged("CityZip");
}
}
}
public string CityZip { get { return Zip.ToString() + " " + City; } }
public override string ToString()
{
return Name + "," + CityZip;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
这是一个自定义的 ListBox:
public class AddressListBox : ListBox
{
public AddressListBox()
{
DrawMode = DrawMode.OwnerDrawFixed;
ItemHeight = 18;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
const TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
if (e.Index >= 0) {
e.DrawBackground();
e.Graphics.DrawRectangle(Pens.Red, 2, e.Bounds.Y + 2, 14, 14);
var textRect = e.Bounds;
textRect.X += 20;
textRect.Width -= 20;
string itemText = DesignMode ? "AddressListBox" : Items[e.Index].ToString();
TextRenderer.DrawText(e.Graphics, itemText, e.Font, textRect, e.ForeColor, flags);
e.DrawFocusRectangle();
}
}
}
在一个表单中,我们放置了这个AddressListBox和一个按钮。在表单中,我们放置了一些初始化代码和一些按钮代码,用来改变我们的地址。我们这样做是为了看到我们的列表框是否会自动更新:
public partial class frmAddress : Form
{
BindingList<Address> _addressBindingList;
public frmAddress()
{
InitializeComponent();
_addressBindingList = new BindingList<Address>();
_addressBindingList.Add(new Address { Name = "Müller" });
_addressBindingList.Add(new Address { Name = "Aebi" });
lstAddress.DataSource = _addressBindingList;
}
private void btnChangeCity_Click(object sender, EventArgs e)
{
_addressBindingList[0].City = "Zürich";
_addressBindingList[1].City = "Burgdorf";
}
}
当按钮被点击时,AddressListBox中的项目会自动更新。请注意,仅定义了ListBox的DataSource。DataMember和ValueMember为空。