1

In the creation of some apps, a list-box with multiple lined content is preferred. Since listboxes have no such function, the creation of a custom control is needed. For this case, I'm working on a compiler app that the user can load an import and export C# prefab into the program to manipulate data. To see this compiler in action, you can check out my previous post here. For this instance, I want a debug log of any errors to be outputted into the listbox. Since some errors contain multiple lines, some of which are rather long, I read up and generated a Listbox of Textbox items.

The most current copy of this can be found on 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);
        }
    }
}

The difficulty I'm having, is that, even though I've set the textbox as it should be, the list view item still seems to be limiting content to TextWrap, and a maximum of 7.5 lines.

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

On line 32 foreach (string line in s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) I try to find the length of the longest line of text in the string to return in the OnMeasureItem override, but it refuses to go in excess of the assumed limit. Any help would be greatly appreciated.

Community
  • 1
  • 1
Komak57
  • 113
  • 1
  • 3
  • 12
  • [You're basically losing your time.](http://stackoverflow.com/q/15532639/643085) – Federico Berasategui Sep 08 '13 at 01:13
  • By the way, see [my example](http://stackoverflow.com/a/16745054/643085) of a [High-Performance](http://www.youtube.com/watch?v=D3Y6DnFpHCA), [highly scalable and customizable](http://wpftutorial.net/DataTemplates.html) Log Viewer with support for **anything** (rich text, images, even video or editable UI) – Federico Berasategui Sep 08 '13 at 01:15
  • To make it more efficient, you can replace "new string[] { Environment.NewLine }, StringSplitOptions.None" to "\n"; it does the same thing. – ismellike Sep 08 '13 at 01:32

2 Answers2

0

You should take a look at a control called GlacialList. you can do such thing easily. Latest versions are not free but i used to use version 1.3. If you get to it there is a property that allow you to put ANY control inside a list cell. just add multiline textbox and your good to go

Franck
  • 4,438
  • 1
  • 28
  • 55
  • I don't think that thing will work well for big collections. I don't see anything mentioning it supports [UI Virtualization](http://www.youtube.com/watch?v=D3Y6DnFpHCA) – Federico Berasategui Sep 08 '13 at 01:22
  • i've had issue when i started displaying about 30,000 records with about 20 columns each containing a control. So if you plan showing half a million control or more, yes you should forget about this option. here's the link BTW [Glacial List 1.3](http://www.codeproject.com/Articles/4012/C-List-View-v1-3) – Franck Sep 09 '13 at 12:35
  • Multiline Text box flashes when inserted into SubItem of GlacialList 1.3, Rich Text Box is inserted with a space above it and extends into next row. So not much use for multiline text in Listbox – SimonKravis Dec 24 '18 at 01:45
  • @SimonKravis Yes it sometime does that because all controls have a padding of typically 1 pixel around so if you leave the item height to auto it is always 1 pixel off the top and the bottom so you overlap 2 pixels and the more rows you have the worst it gets. Having no other controls that are as flexible as this control even 14 years later it's a manageable thing. The trick is to calculate the control height and adjust the item height accordingly. Note that i have never tried the `RichTextbox` with it but i had normal multiline textbox. Possible that the font variant of the RTB is the problem – Franck Dec 27 '18 at 17:33
0

It may not be an option for you but if you want more Advanced UI features why not just switch to WPF where you get this built-in?

Ronan Thibaudau
  • 3,413
  • 3
  • 29
  • 78