C#自定义列表框GUI

Mar*_*ark 4 c# user-interface list winforms

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

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

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

在此输入图像描述

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

谢谢你的帮助.

Oli*_*bes 14

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

public interface IDisplayItem
{
    event System.ComponentModel.ProgressChangedEventHandler ProgressChanged;
    string Subject { get; }
    string Description { get; }
    // Provide everything you need for the display here
}
Run Code Online (Sandbox Code Playgroud)

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

自定义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();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以让原始传输类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
}
Run Code Online (Sandbox Code Playgroud)

这是一个自定义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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在表单上,​​我们放置此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";
    }
}
Run Code Online (Sandbox Code Playgroud)

单击该按钮时,AddressListBox中的项目将自动更新.请注意,仅定义了列表框的DataSource.DataMember和ValueMember保持为空.