所有者绘制的列表视图未正确自动调整列大小

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);