ListView Final Column Autosize creates scrollbar

前端 未结 8 883
闹比i
闹比i 2021-01-13 18:23

I am implementing a custom control which derives from ListView.

I would like for the final column to fill the remaining space (Quite a common task), I have gone abou

相关标签:
8条回答
  • 2021-01-13 18:52

    When a form containing a listview is restored to FormWindowState.Normal from a maximized state, horizontal scroll bar is displayed even though column widths have been correctly adjusted. This happens when the resized column width shrinks but not when it grows. Here's a hack that can correct this without resorting to win32 calls. (magic number [-2] is just a simple shortcut)

            // 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); });
            }
    
    0 讨论(0)
  • 2021-01-13 18:57

    Try calling base.OnResize() after you made your change.

    0 讨论(0)
  • 2021-01-13 18:58

    Try using the ClientSize property. As MSDN puts it:

    The client area of a control is the bounds of the control, minus the nonclient elements such as scroll bars, borders, title bars, and menus.

    Edit: I've added a code sample demonstrating how achieve different fill behaviors.

    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));
        }
    }
    
    0 讨论(0)
  • 2021-01-13 19:05

    This code works very well for me:

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

    Regards Lary

    0 讨论(0)
  • 2021-01-13 19:08

    You need to override the protected method SetBoundsCore() and perform the column resizing either before or after delegating to base.SetBoundsCore(), depending on whether the ListView width is narrowing or widening.

    The following example is hardcoded for a single column 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;
    }
    

    Note his code doesn't deal with the specified parameter, i'll leave that up to you!

    0 讨论(0)
  • 2021-01-13 19:09

    I know this is old question, but after reading this and then finding my own answer, I thought I would share for others reading this as well.

    The fix for me (so far holding up under basic testing) was to set Docked to None and just Anchor my ListView to the top-left of the parent. Then, in the OnClientSize changed of the parent I set the size of my ListView to fill it, and then set my column width. For whatever reason, probably timing, this fixed the scrollbar issue mentioned by the original question.

        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 is my ListView and ContentSplit.Panel1 is a SplitContainer control it is inside. When I had it set to Dock=Fill, I had the scrollbar issue, with the above changes, no problems.

    0 讨论(0)
提交回复
热议问题