【问题标题】:Creating a custom Multi-Line Listbox创建自定义多行列表框
【发布时间】:2013-09-07 23:08:22
【问题描述】:

在创建某些应用程序时,首选具有多行内容的列表框。由于列表框没有这样的功能,因此需要创建自定义控件。对于这种情况,我正在开发一个编译器应用程序,用户可以将导入和导出 C# 预制件加载到程序中以操作数据。要查看此编译器的实际运行情况,您可以查看我之前的帖子 here。对于这种情况,我希望将任何错误的调试日志输出到列表框中。由于一些错误包含多行,其中一些相当长,我阅读并生成了一个文本框项目的列表框。

可以在pastebin 上找到最新的副本。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace DataStripper
{
    public partial class MultiLineListView : System.Windows.Forms.ListBox
    {
        public MultiLineListView()
        {
            //InitializeComponent();
            this.DrawMode = DrawMode.OwnerDrawVariable;
            this.ScrollAlwaysVisible = true;
            tbox.Hide();
            tbox.mllb = this;
            Controls.Add(tbox);
        }

        protected override void OnMeasureItem(MeasureItemEventArgs e)
        {
            if (Site != null)
                return;
            if (e.Index > -1)
            {
                string s = Items[e.Index].ToString();
                float best = 0;
                foreach (string line in s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
                {
                    float chk = e.Graphics.MeasureString(line, Font, Width).Width;
                    if (chk > best)
                        best = chk;
                }
                SizeF sf = e.Graphics.MeasureString(s, Font, Width);
                int htex = 1;//(e.Index == 0) ? 15 : 10;
                e.ItemHeight = (int)(sf.Height*Items.Count) + htex;
                e.ItemWidth = (int)best;
                /*NTextBox i = (NTextBox)Items[e.Index];
                e.ItemHeight = i.Height;
                e.ItemWidth = i.Width;*/
            }
        }

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (Site != null)
                return;
            if (e.Index > -1)
            {
                string s = Items[e.Index].ToString();

                if ((e.State & DrawItemState.Focus) == 0)
                {
                    e.Graphics.FillRectangle(new SolidBrush(SystemColors.Window), e.Bounds);
                    e.Graphics.DrawString(s, Font, new SolidBrush(SystemColors.WindowText),
                        e.Bounds);
                    e.Graphics.DrawRectangle(new Pen(SystemColors.Highlight), e.Bounds);
                }
                else
                {
                    e.Graphics.FillRectangle(new SolidBrush(SystemColors.Highlight), e.Bounds);
                    e.Graphics.DrawString(s, Font, new SolidBrush(SystemColors.HighlightText),
                        e.Bounds);
                }
            }
        }

        protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
        {
            int index = IndexFromPoint(e.X, e.Y);

            if (index != ListBox.NoMatches &&
                index != 65535)
            {

                /*if (e.Button == MouseButtons.Right)
                {
                    SelectedIndex = index;
                    Focus();
                    //tbox.index = index;
                }*/
                /*if (e.Button == MouseButtons.Right)
                {

                    string s = Items[index].ToString();
                    Rectangle rect = GetItemRectangle(index);

                    tbox.Location = new Point(rect.X, rect.Y);
                    tbox.Size = new Size(rect.Width, rect.Height);
                    tbox.Text = s;
                    tbox.index = index;
                    tbox.SelectAll();
                    tbox.Show();
                    tbox.Focus();
                }*/
            }

            base.OnMouseUp(e);
        }

        NTextBox tbox = new NTextBox();

        class NTextBox : TextBox
        {
            public MultiLineListView mllb;
            public int index = -1;

            bool errshown = false;
            bool brementer = false;

            public NTextBox()
            {
                Multiline = true;
                MaxLength = 2147483647;
                MaximumSize = new System.Drawing.Size(0, 0);
                WordWrap = false;
                ScrollBars = ScrollBars.Both;
                AcceptsReturn = true;
                AcceptsTab = true;
            }

            protected override void OnKeyUp(KeyEventArgs e)
            {
                if (brementer)
                {
                    Text = "";
                    brementer = false;
                }
                base.OnKeyUp(e);
            }

            protected override void OnKeyPress(KeyPressEventArgs e)
            {
                base.OnKeyPress(e);
            }

            protected override void OnLostFocus(System.EventArgs e)
            {

                if (Text.Trim() == "")
                {
                    if (!errshown)
                    {
                        MessageBox.Show(
                            "Cannot enter NULL string as item!",
                            "Fatal error!", MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
                    }
                    errshown = false;
                }
                else
                {
                    errshown = false;
                    mllb.Items[index] = Text;
                    Hide();
                }
                base.OnLostFocus(e);
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyData == Keys.F2)
            {
                int index = SelectedIndex;
                if (index == ListBox.NoMatches ||
                    index == 65535)
                {
                    if (Items.Count > 0)
                        index = 0;
                }
                if (index != ListBox.NoMatches &&
                    index != 65535)
                {

                    string s = Items[index].ToString();
                    Rectangle rect = GetItemRectangle(index);

                    tbox.Location = new Point(rect.X, rect.Y);
                    tbox.Size = new Size(rect.Width, rect.Height);
                    tbox.Text = s;
                    tbox.index = index;
                    tbox.SelectAll();
                    tbox.Show();
                    tbox.Focus();
                }
            }
            base.OnKeyDown(e);
        }
    }
}

我遇到的困难是,即使我已将文本框设置为应有的状态,列表视图项似乎仍将内容限制为 TextWrap,最多为 7.5 行。

Image Reference http://imageshack.us/a/img819/9345/5nh4.png

在第 32 行 foreach (string line in s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) 我尝试在 OnMeasureItem 覆盖中找到要返回的字符串中最长文本行的长度,但它拒绝超出假定的限制。任何帮助将不胜感激。

【问题讨论】:

标签: c# listbox multiline


【解决方案1】:

您应该看一下名为 GlacialList 的控件。你可以很容易地做这样的事情。最新版本不是免费的,但我曾经使用 1.3 版。如果你得到它,有一个属性允许你将任何控件放在列表单元格中。只需添加多行文本框就可以了

【讨论】:

  • 我不认为这对大型收藏品有好处。我没有看到任何提及它支持UI Virtualization
  • 当我开始显示大约 30,000 条记录时遇到问题,其中大约 20 列每列都包含一个控件。因此,如果您计划显示 50 万或更多控制,是的,您应该忘记此选项。这是链接顺便说一句Glacial List 1.3
  • 多行文本框在插入到 GlacialList 1.3 的 SubItem 时会闪烁,富文本框插入时上方有一个空格并延伸到下一行。所以列表框中的多行文本用处不大
  • @SimonKravis 是的,它有时会这样做,因为所有控件通常都有 1 个像素的填充,所以如果您将项目高度设置为自动,它始终是顶部和底部 1 个像素,因此您重叠 2像素和行数越多,情况就越糟糕。即使在 14 年后,也没有其他控件像此控件一样灵活,这是一件易于管理的事情。诀窍是计算控件高度并相应地调整项目高度。请注意,我从未尝试过使用RichTextbox,但我有正常的多行文本框。可能是 RTB 的字体变体有问题
【解决方案2】:

这可能不是您的选择,但如果您想要更多高级 UI 功能,为什么不直接切换到 WPF 以获取此内置功能?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-05
    • 2014-12-10
    • 1970-01-01
    • 2016-04-30
    • 2018-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多