ListView最终列自动调整大小创建滚动条

时间:2009-09-04 04:38:19

标签: c# winforms listview

我正在实现一个从ListView派生的自定义控件。

我想最后一列填充剩余的空间(相当常见的任务),我通过覆盖OnResize方法解决了这个问题:

     protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);

        if (Columns.Count == 0)
            return;
        Columns[Columns.Count - 1].Width = -2; // -2 = Fill remaining space
    }

或通过其他方法:

        protected override void OnResize(EventArgs e)
    {

        base.OnResize(e);

        if (!_autoFillLastColumn)
            return;

        if (Columns.Count == 0)
            return;

        int TotalWidth = 0;
        int i = 0;
        for (; i < Columns.Count - 1; i++)
        {
            TotalWidth += Columns[i].Width;
        }

        Columns[i].Width = this.DisplayRectangle.Width - TotalWidth;
    }

编辑:

这可以正常工作,直到我将ListView停靠到父容器并通过该控件调整大小。每隔一次控制尺寸缩小(IE,将边框拖动一个像素),底部就会出现一个根本无法移动的滚动条(甚至不是一个像素)。

结果是当我拖动我在ListView中使用闪烁的滚动条留下的父级的大小时,拖动停止时它将有50%的可能性。

8 个答案:

答案 0 :(得分:4)

ObjectListView(围绕.NET WinForms ListView的开源包装器)允许这种“扩展最后一列填充可用空间”。它实际上比第一次出现更难做 - 不要相信任何告诉你的人:)

如果您使用ObjectListView,您可以免费获得该问题的解决方案 - 以及其他几个问题。但如果你想自己完成所有工作,你需要:

  • 使用ClientSize而不是DisplayRectangle(正如@zxpro已经说过的那样)
  • 使用布局事件而非Resize事件。
  • 侦听ColumnResized事件并执行相同的工作
  • 在设计模式下不进行自动调整大小 - 它会变得非常混乱。

最棘手的问题仅在窗口缩小时出现 - 水平滚动条恼人地闪烁,有时在收缩完成后仍然存在。这似乎正是你案件中发生的事情。

解决方法是拦截WM_WINDOWPOSCHANGING消息并将ListView调整为新大小的大小。

protected override void WndProc(ref Message m) {
    switch (m.Msg) {
        case 0x46: // WM_WINDOWPOSCHANGING
            this.HandleWindowPosChanging(ref m);
            base.WndProc(ref m);
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}

protected virtual void HandleWindowPosChanging(ref Message m) {
    const int SWP_NOSIZE = 1;

    NativeMethods.WINDOWPOS pos = (NativeMethods.WINDOWPOS)m.GetLParam(typeof(NativeMethods.WINDOWPOS));
    if ((pos.flags & SWP_NOSIZE) == 0) {
        if (pos.cx < this.Bounds.Width) // only when shrinking
            // pos.cx is the window width, not the client area width, so we have to subtract the border widths
            this.ResizeFreeSpaceFillingColumns(pos.cx - (this.Bounds.Width - this.ClientSize.Width));
    }
}

答案 1 :(得分:1)

这可能已经很晚了,但我相信最简单的解决方案是添加一个函数来处理窗口调整大小事件。我将listview固定在右侧,所以当我调整窗口大小时,listview也会调整大小。在处理窗口调整大小的函数中,我设置myListView.Columns [lastColumnIndex] .Width = -2;

这显然重绘了listview,将最后一列扩展到控件的边缘。希望这可以帮助。

答案 2 :(得分:1)

这段代码非常适合我:


const int SWP_NOSIZE = 1;
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
    public IntPtr hwnd;
    public IntPtr hwndInsertAfter;
    public int x;
    public int y;
    public int cx;
    public int cy;
    public int flags;
}
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0x46: // WM_WINDOWPOSCHANGING
            this.HandleWindowPosChanging(ref m);
            base.WndProc(ref m);
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}
protected virtual void HandleWindowPosChanging(ref Message m)
{
    try
    {
        WINDOWPOS pos = new WINDOWPOS();
        pos = (WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, pos.GetType());
        if (m_EnableAutoColumnResize && this.Columns.Count > 0 && (pos.flags & SWP_NOSIZE) == 0)
        {
            this.ResizeFreeSpaceFillingColumns(pos.cx - (this.Bounds.Width - this.ClientSize.Width));
        }
    }
    catch (ArgumentException) { }
}

private void ResizeFreeSpaceFillingColumns(int listViewWitdh)
{
    int lastColumn = this.Columns.Count - 1;
    int sumWidth = 0;

    for (int i = 0; i < lastColumn; i++)
    {
        sumWidth += this.Columns[i].Width;
    }
    this.Columns[lastColumn].Width = listViewWitdh - sumWidth;
}

此致 拉里

答案 3 :(得分:1)

您需要覆盖受保护的方法SetBoundsCore()并执行之前的委派给base.SetBoundsCore()之后的列调整大小,具体取决于是否ListView宽度正在缩小或扩大。

以下示例是针对单个列ListView进行硬编码的:

protected override void SetBoundsCore( int x, int y, int width, int height, BoundsSpecified specified )
{
    ResizeColumns( width, true );
    base.SetBoundsCore( x, y, width, height, specified );
    ResizeColumns( width, false );
}

private void ResizeColumns( int controlWidth, bool ifShrinking )
{
    if( Columns.Count < 1 || Parent == null )
        return;

    int borderGap = Width - ClientSize.Width;
    int desiredWidth = controlWidth - borderGap;

    if( (desiredWidth < Columns[0].Width) == ifShrinking )
        Columns[0].Width = desiredWidth;
}

注意他的代码不处理specified参数,我会留给你的!

答案 4 :(得分:0)

在您进行更改后尝试拨打base.OnResize()

答案 5 :(得分:0)

尝试使用ClientSize属性。正如MSDN所说:

  

控件的客户区域是控件的边界,减去非客户端元素,如滚动条,边框,标题栏和菜单。

修改:我添加了一个代码示例,演示了如何实现不同的填充行为。

public class MyListView : ListView
{
    public MyListView()
    {
        View = View.Details;
        Columns.Add(Column1);
        Columns.Add(Column2);
        Columns.Add(Column3);
    }

    private readonly ColumnHeader Column1 = new ColumnHeader();
    private readonly ColumnHeader Column2 = new ColumnHeader();
    private readonly ColumnHeader Column3 = new ColumnHeader();

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);

        // Constant width
        Column1.Width = 200;
        // 50% of the ListView's width
        Column2.Width = (ClientSize.Width/2);
        // Fill the remaining width, but no less than 100 px
        Column3.Width = Math.Max(100, ClientSize.Width - (Column1.Width + Column2.Width));
    }
}

答案 6 :(得分:0)

当包含列表视图的表单从最大化状态恢复到FormWindowState.Normal时,即使已正确调整列宽,也会显示水平滚动条。当调整大小的列宽度缩小但不会增长时,会发生这种情况。这是一个黑客,可以纠正这个,而无需诉诸win32电话。 (幻数[-2]只是一个简单的快捷方式)

        // Member variable.
        private ListView myListView;

        // Method to resize last column.
        private void ResizeColumn(int width) {
              myListView.Columns[myListView.Columns.Count - 1].Width = width;
        }                  

        // ListView ClientSizeChanged event handler.
        private void Process_ListViewClientSizeChanged(object sender, EventArgs e)
        {
              // Get the width to resize the last column.
              int width = myListView.ClientSize.Width;
              for (int i = 0; i < myListView.Columns.Count - 1; i++)
                    width -= myListView.Columns[i].Width;

              // Last column width is growing. Use magic number to resize.
              if (width >= myListView.Columns[myListView.Columns.Count - 1].Width)
                    myListView.Columns[myListView.Columns.Count - 1].Width = -2;

              // Last column width is shrinking. 
              // Asynchronously invoke a method to resize the last column.
              else
                    myListView.BeginInvoke
                          ((MethodInvoker)delegate { ResizeColumn(width); });
        }

答案 7 :(得分:0)

我知道这是一个古老的问题,但在阅读完之后再找到我自己的答案时,我想我也会分享给其他读这篇文章的人。

我的修复(到目前为止在基本测试中保持不变)是将Docked设置为None,然后将我的ListView锚定到父级的左上角。然后,在更改了父级的OnClientSize中,我将ListView的大小设置为填充它,然后设置我的列宽。无论出于何种原因,可能是时间安排,这解决了原始问题中提到的滚动条问题。

    private void ContentSplit_Panel1_ClientSizeChanged(object sender, EventArgs e)
    {
        CaseFilter.Bounds = ContentSplit.Panel1.ClientRectangle;
        if(CaseFilter.Columns.Count == 1)
            CaseFilter.Columns[0].Width = CaseFilter.ClientRectangle.Width;
    }

CaseFilter是我的ListView,ContentSplit.Panel1是一个SplitContainer控件。当我将它设置为Dock = Fill时,我遇到了滚动条问题,上面的更改没有问题。