C#自定义列表框GUI

时间:2011-11-16 17:41:23

标签: c# winforms list user-interface

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

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

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

enter image description here

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

感谢您的帮助。

2 个答案:

答案 0 :(得分:13)

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

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

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

自定义列表框: 要以自己的方式显示列表框项目,您必须从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<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中的项目将自动更新。请注意,仅定义了列表框的DataSource。 DataMember和ValueMember保持为空。

答案 1 :(得分:1)

是的,如果你使用WPF,那么这很容易实现。您所要做的就是为不同的类型制作不同的DataTemplate

MSDN for data templates
Dr. WPF for Items Control & Data Templates