问题
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