C#自定义列表框GUI

我有一个类列表,但不同的子项具有需要显示的不同属性。

我想要实现的是在gui中有一个listbox类型的控件,它允许每个子节点以它想要的方式显示它的属性 – 所以不要为每个类使用相同的预定义列。

我设想类似传输接口(下面),每个类可以绘制它自己的条目,显示一些文本,相关的进度条等。

在此处输入图像描述

如何在C#中实现这一目标?

谢谢你的帮助。

让列表项实现一个界面,提供显示所需的一切:

public interface IDisplayItem { event System.ComponentModel.ProgressChangedEventHandler ProgressChanged; string Subject { get; } string Description { get; } // Provide everything you need for the display here } 

传输对象不应自行显示。 您不应该混合使用域逻辑(业务逻辑)和显示逻辑。

自定义ListBox :为了以您自己的方式显示列表框项,您必须从System.Windows.Forms.ListBox派生自己的列表框控件。 将列表框的DrawMode属性设置为构造函数中的DrawMode.OwnerDrawFixedDrawMode.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(...); // and so on } e.DrawFocusRectangle(); } } 

您可以让原始传输类实现IDisplayItem或为此创建一个特殊类。 您也可以在列表中使用不同类型的对象,只要它们实现接口即可。 关键是,显示逻辑本身在控件中,传输类(或任何类)仅提供所需的信息。

示例 :由于与Mark的持续讨论,我决定在此处包含一个完整的示例。 让我们定义一个模型类:

 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); // Simulate an icon. 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
_addressBindingList; public frmAddress() { InitializeComponent(); _addressBindingList = new BindingList
(); _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中的项目将自动更新。 请注意,仅定义了列表框的DataSource。 DataMember和ValueMember保持为空。

是的,如果你使用WPF,这很容易做到这一点。 您所要做的就是为不同类型制作不同的DataTemplate

MSDN用于数据模板
WPF博士的物品控制和数据模板