Can't make the items in a ListBox align correctly using TabStops

余生颓废 提交于 2021-01-29 05:11:21

问题


I have the following problem with ListbBox.

To make it easy to understand I have simplified it a bit.

public partial class Form1 : Form
{
    private void Form1_Load(object sender, EventArgs e)
    {
        listBox1.Items.Add("xxxxxx" +"\t\t\t"+ "yyyyyyy");
        listBox1.Items.Add("xxxxxxx" + "\t\t\t" + "yyyyyyy");
        listBox1.Items.Add("xxxxxxxx" + "\t\t\t" + "yyyyyyy");
        listBox1.Items.Add("xxxxxxxxx" + "\t\t\t" + "yyyyyyy");
        listBox1.Items.Add("xxxxxxxxxx" + "\t\t\t" + "yyyyyyy");
    }
}

From row 1 to 4 it prints out perfectly in straight rows downwards. But the 5th row is completly off when I run the program, although there is plenty of space. Can any body help me to get all items to be in straight rows downwards?


回答1:


You need to set the property CustomTabOffsets and the property UseCustomTabOffsets and then you can reduce the number of tabs in your strings to only one.

For example

ListBox lb = new ListBox();
lb.Size = new Size(500, 200);
lb.CustomTabOffsets.Add(100);
lb.UseCustomTabOffsets = true;

lb.Items.Add("xxxxxx" + "\t" + "yyyyyyy");
lb.Items.Add("xxxxxxx" + "\t" + "yyyyyyy");
lb.Items.Add("xxxxxxxx" + "\t" + "yyyyyyy");
lb.Items.Add("xxxxxxxxx" + "\t" + "yyyyyyy");
lb.Items.Add("xxxxxxxxxx" + "\t" + "yyyyyyy");

Form f = new Form();
f.Controls.Add(lb);
f.Show();

Of course you should change that 100 to something more consistent with the actual maximum length of the first part of your strings and the width of your ListBox




回答2:


The standard ListBox supports Tab Stops (it's created using the LBS_USETABSTOPS style), but custom Tab Stop must be enabled setting UseCustomTabOffsets= true, then adding one or more values, representing the Tab positions, to the CustomTabOffsets IntegerCollection.

There's a catch in calculating these positions; as described in Docs about the LB_SETTABSTOPS message:

(...) the integers represent the number of quarters of the average character width for the font that is selected into the list box. For example, a tab stop of 4 is placed at 1.0 character units, and a tab stop of 6 is placed at 1.5 average character units. However, if the list box is part of a dialog box, the integers are in dialog template units. The tab stops must be sorted in ascending order (...)

Since Font.ToLogFont() doesn't return the average size of a character in the lfWidth member of the LOGFONT structure, we can calculate it (without PInvoking) from pixels measures as:

([CurrentTextWidth] / [AverageCharWidth]) * [TabSize]

Where [CurrentTextWidth] is the width in pixels of a string, calculated using TextRenderer.MeasureText,
the [AverageCharWidth] can be calculated measuring the difference between M and i,
and [TabSize] represent the quarters of the average char width (as described in the Docs, a TabStop of 4 is equal to the width of the average char, in relation to the Font currently selected).

Sample test, to create 3 columns from text parts separated by '\t' in a ListBox:

listBox1.Items.AddRange(new[] {
    "x\tyyyyyyy\teeeeee",
    "xxxx\tyyyyyyy\tmmmmmmm",
    "xxxxxx\tyyyyyyy\tlllllll",
    "AbcdEfgHilm\tyyyyyyy\tgggggggg",
    "xxxxxx\tyyyyyyy\tzzzzzzz",
    "XXaaMMiixxx\tyyyyyyy\tiiiiiiiiiiiiiiii"
});
SetListBoxTabs(listBox1);

Here, I'm setting float tabSize = 4.2f instead of 4.0, because there must be some space between the text parts separated by a Tab, so I'm adding a fraction of the base value to create some space between Columns.
This value can then be used to proportionally adjust the space between columns.

public void SetListBoxTabs(ListBox listBox)
{
    float tabSize = 4.2f;
    float currTabStop = 0;

    int tabs = listBox.GetItemText(listBox.Items[0]).Split('\t').Length - 1;
    if (tabs == 0) return;
    var tabStops = new List<int>(tabs);
    tabStops.AddRange(Enumerable.Repeat(0, tabs).ToArray());

    using (var g = Graphics.FromHwnd(listBox.Handle))
    {
        float average = GetFontAverageCharSize(g, listBox.Font);

        foreach (var item in listBox.Items)
        {
            string text = listBox.GetItemText(item);
            string[] parts = text.Split('\t');  // Use Substring(IndexOf()) here
            for (int i = 0; i < parts.Length - 1; i++)
            {
                float width = TextRenderer.MeasureText(g, parts[i], listBox.Font,
                    Size.Empty, TextFormatFlags.LeftAndRightPadding).Width;
                float tabWidth = (width / average) * tabSize;
                currTabStop += tabWidth;
                tabStops[i] = (int)Math.Max(currTabStop, tabStops[i]);
            }
            currTabStop = 0;
        }
    }

    listBox.UseTabStops = true;  // Just in case 1 ...
    listBox.UseCustomTabOffsets = true;
    var offsets = listBox.CustomTabOffsets;
    offsets.Clear(); // Just in case 2 ...
    foreach (int tab in tabStops) { offsets.Add(tab); }
}

public float GetFontAverageCharSize(Graphics g, Font font)
{
    string textMax = new string('M', 100);
    string textMin = new string('i', 100);
    float maxWidth = TextRenderer.MeasureText(g, textMax, listBox1.Font).Width;
    float minWidth = TextRenderer.MeasureText(g, textMin, listBox1.Font).Width;
    return (maxWidth + minWidth) / (2.0f * textMax.Length);
}


来源:https://stackoverflow.com/questions/56826174/cant-make-the-items-in-a-listbox-align-correctly-using-tabstops

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!