所有者绘制的列表视图未正确自动调整列大小
Owner drawn list view does not autosize columns correctly
我的列表视图 (WinForms) 派生 class 自己绘制单元格以获得 Excel 类似于二维字符串数组的外观。
Autosize 以某种方式起作用,但不完全正确。条目越大的列越宽,但省略号仍然出现在最大的字符串上。
如何让自动调整大小正常工作?
顺便说一句:由于性能糟糕,DataGridView 是不需要的。
TableViewTest.cs:
public class TableViewTest : Form
{
private LiftQD.TableView tableView1;
public TableViewTest()
{
this.tableView1 = new LiftQD.TableView();
this.SuspendLayout();
this.tableView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.tableView1.Location = new System.Drawing.Point(0, 0);
this.tableView1.Name = "tableView1";
this.tableView1.OwnerDraw = true;
this.tableView1.Scrollable = false;
this.tableView1.Size = new System.Drawing.Size(284, 261);
this.tableView1.TabIndex = 0;
this.tableView1.UseCompatibleStateImageBehavior = false;
this.tableView1.View = System.Windows.Forms.View.Details;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.tableView1);
this.Name = "TableViewTest";
this.Text = "TableViewTest";
this.ResumeLayout(false);
tableView1.Table = new Table(new String[,]{{"abcdefg","hijklmnop"},{"1234567","all good children go to heaven"}});
}
}
TableView.cs:
public class TableView: ListView
{
Table table;
bool isAdjustingLastColumn;
public TableView()
{
DoubleBuffered = true;
View = View.Details;
// we draw gridlines ourselves
GridLines = false;
OwnerDraw = true;
Scrollable = false;
HeaderStyle = ColumnHeaderStyle.Nonclickable;
}
protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
{
if (e.ColumnIndex == Columns.Count-1)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Left, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Bottom-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Right-1, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Top);
e.DrawText(TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
}
}
protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
{
if (e.ColumnIndex == Columns.Count-1)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
}
else if (e.ColumnIndex == 0)
{
e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Left, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Bottom-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Right-1, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Top);
e.DrawText(TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Left, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Bottom-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Right-1, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Top);
e.DrawText(TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.WordEllipsis);
}
}
public int TableWidth
{
get
{
if (Columns.Count < 2) return 0;
return Items[Items.Count-1].SubItems[Columns.Count-2].Bounds.Right;
}
}
public int TableHeight
{
get
{
if (Items.Count == 0) return 0;
return Items[Items.Count-1].Bounds.Bottom;
}
}
// sets the size of the last (empty) column so as to fill up the client area
void adjustLastColumn()
{
if (!isAdjustingLastColumn)
{
isAdjustingLastColumn = true;
if (Columns.Count > 0) Columns[Columns.Count-1].Width = Math.Max(ClientSize.Width-TableWidth,0);
isAdjustingLastColumn = false;
}
}
protected override void OnResize(EventArgs e)
{
adjustLastColumn();
base.OnResize(e);
}
protected override void OnColumnWidthChanged(ColumnWidthChangedEventArgs e)
{
adjustLastColumn();
base.OnColumnWidthChanged(e);
}
protected override void OnColumnWidthChanging(ColumnWidthChangingEventArgs e)
{
adjustLastColumn();
base.OnColumnWidthChanging(e);
}
string GetExcelColumnName(int columnNumber)
{
int dividend = columnNumber;
string columnName = String.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
public Table Table
{
get {return new Table(table);}
set
{
table = new Table(value);
if (table != null)
{
int columnCount = table.ColumnCount;
int rowCount = table.RowCount;
// one unnamed column for the row names (line numbers)
Columns.Add("");
for (int i=0; i<columnCount; i++)
{
Columns.Add(GetExcelColumnName(i+1));
}
// another empty column that will be filling the remaining client area
Columns.Add("");
for (int j=0; j<rowCount; j++)
{
ListViewItem lvi = new ListViewItem((j+1).ToString());
lvi.UseItemStyleForSubItems = false;
for (int i=0; i<columnCount; i++)
{
lvi.SubItems.Add(table[i,j]);
}
Items.Add(lvi);
}
AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
adjustLastColumn();
}
}
}
}
Table.cs:
public class Table
{
string[,] data;
string name = "";
string info = "";
public string Name
{
get {return name;}
set {if (value != null) name = value; else name = "";}
}
public string Info
{
get {return info;}
set {if (value != null) info = value; else info = "";}
}
public Table(string[,] originalData)
{
data = (string[,])(originalData.Clone());
}
public Table(Table originalData)
{
data = (string[,])(originalData.data.Clone());
string name = originalData.Name;
string info = originalData.Info;
}
public int RowCount {get{return data.GetLength(1);}}
public int ColumnCount {get{return data.GetLength(0);}}
public string this[int xIndex, int yIndex]
{
get
{
if (xIndex >= 0 && xIndex < data.GetLength(0) && yIndex >= 0 && yIndex < data.GetLength(1))
return data[xIndex, yIndex];
else
return "";
}
}
}
默认的 DrawText 实现似乎添加了不可移动的填充。使用 TextRenderer class 代替:
// e.DrawText(TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine |
// TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.WordEllipsis);
TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.Item.Font, e.Bounds,
SystemColors.WindowText, SystemColors.Window,
TextFormatFlags.VerticalCenter | TextFormatFlags.WordEllipsis);
我的列表视图 (WinForms) 派生 class 自己绘制单元格以获得 Excel 类似于二维字符串数组的外观。
Autosize 以某种方式起作用,但不完全正确。条目越大的列越宽,但省略号仍然出现在最大的字符串上。
如何让自动调整大小正常工作?
顺便说一句:由于性能糟糕,DataGridView 是不需要的。
TableViewTest.cs:
public class TableViewTest : Form
{
private LiftQD.TableView tableView1;
public TableViewTest()
{
this.tableView1 = new LiftQD.TableView();
this.SuspendLayout();
this.tableView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.tableView1.Location = new System.Drawing.Point(0, 0);
this.tableView1.Name = "tableView1";
this.tableView1.OwnerDraw = true;
this.tableView1.Scrollable = false;
this.tableView1.Size = new System.Drawing.Size(284, 261);
this.tableView1.TabIndex = 0;
this.tableView1.UseCompatibleStateImageBehavior = false;
this.tableView1.View = System.Windows.Forms.View.Details;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.tableView1);
this.Name = "TableViewTest";
this.Text = "TableViewTest";
this.ResumeLayout(false);
tableView1.Table = new Table(new String[,]{{"abcdefg","hijklmnop"},{"1234567","all good children go to heaven"}});
}
}
TableView.cs:
public class TableView: ListView
{
Table table;
bool isAdjustingLastColumn;
public TableView()
{
DoubleBuffered = true;
View = View.Details;
// we draw gridlines ourselves
GridLines = false;
OwnerDraw = true;
Scrollable = false;
HeaderStyle = ColumnHeaderStyle.Nonclickable;
}
protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
{
if (e.ColumnIndex == Columns.Count-1)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Left, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Bottom-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Right-1, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Top);
e.DrawText(TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
}
}
protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
{
if (e.ColumnIndex == Columns.Count-1)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
}
else if (e.ColumnIndex == 0)
{
e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Left, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Bottom-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Right-1, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Top);
e.DrawText(TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width-1, e.Bounds.Height-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Left, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Bottom-1);
e.Graphics.DrawLine(SystemPens.ActiveBorder, e.Bounds.Right-1, e.Bounds.Bottom-1, e.Bounds.Right-1, e.Bounds.Top);
e.DrawText(TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.WordEllipsis);
}
}
public int TableWidth
{
get
{
if (Columns.Count < 2) return 0;
return Items[Items.Count-1].SubItems[Columns.Count-2].Bounds.Right;
}
}
public int TableHeight
{
get
{
if (Items.Count == 0) return 0;
return Items[Items.Count-1].Bounds.Bottom;
}
}
// sets the size of the last (empty) column so as to fill up the client area
void adjustLastColumn()
{
if (!isAdjustingLastColumn)
{
isAdjustingLastColumn = true;
if (Columns.Count > 0) Columns[Columns.Count-1].Width = Math.Max(ClientSize.Width-TableWidth,0);
isAdjustingLastColumn = false;
}
}
protected override void OnResize(EventArgs e)
{
adjustLastColumn();
base.OnResize(e);
}
protected override void OnColumnWidthChanged(ColumnWidthChangedEventArgs e)
{
adjustLastColumn();
base.OnColumnWidthChanged(e);
}
protected override void OnColumnWidthChanging(ColumnWidthChangingEventArgs e)
{
adjustLastColumn();
base.OnColumnWidthChanging(e);
}
string GetExcelColumnName(int columnNumber)
{
int dividend = columnNumber;
string columnName = String.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
public Table Table
{
get {return new Table(table);}
set
{
table = new Table(value);
if (table != null)
{
int columnCount = table.ColumnCount;
int rowCount = table.RowCount;
// one unnamed column for the row names (line numbers)
Columns.Add("");
for (int i=0; i<columnCount; i++)
{
Columns.Add(GetExcelColumnName(i+1));
}
// another empty column that will be filling the remaining client area
Columns.Add("");
for (int j=0; j<rowCount; j++)
{
ListViewItem lvi = new ListViewItem((j+1).ToString());
lvi.UseItemStyleForSubItems = false;
for (int i=0; i<columnCount; i++)
{
lvi.SubItems.Add(table[i,j]);
}
Items.Add(lvi);
}
AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
adjustLastColumn();
}
}
}
}
Table.cs:
public class Table
{
string[,] data;
string name = "";
string info = "";
public string Name
{
get {return name;}
set {if (value != null) name = value; else name = "";}
}
public string Info
{
get {return info;}
set {if (value != null) info = value; else info = "";}
}
public Table(string[,] originalData)
{
data = (string[,])(originalData.Clone());
}
public Table(Table originalData)
{
data = (string[,])(originalData.data.Clone());
string name = originalData.Name;
string info = originalData.Info;
}
public int RowCount {get{return data.GetLength(1);}}
public int ColumnCount {get{return data.GetLength(0);}}
public string this[int xIndex, int yIndex]
{
get
{
if (xIndex >= 0 && xIndex < data.GetLength(0) && yIndex >= 0 && yIndex < data.GetLength(1))
return data[xIndex, yIndex];
else
return "";
}
}
}
默认的 DrawText 实现似乎添加了不可移动的填充。使用 TextRenderer class 代替:
// e.DrawText(TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine |
// TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.WordEllipsis);
TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.Item.Font, e.Bounds,
SystemColors.WindowText, SystemColors.Window,
TextFormatFlags.VerticalCenter | TextFormatFlags.WordEllipsis);